aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/utils')
-rw-r--r--src/libs/utils/CMakeLists.txt3
-rw-r--r--src/libs/utils/algorithm.h8
-rw-r--r--src/libs/utils/aspects.cpp404
-rw-r--r--src/libs/utils/aspects.h77
-rw-r--r--src/libs/utils/async.h5
-rw-r--r--src/libs/utils/changeset.cpp35
-rw-r--r--src/libs/utils/changeset.h6
-rw-r--r--src/libs/utils/checkablemessagebox.cpp5
-rw-r--r--src/libs/utils/clangutils.cpp2
-rw-r--r--src/libs/utils/codegeneration.cpp17
-rw-r--r--src/libs/utils/codegeneration.h3
-rw-r--r--src/libs/utils/commandline.cpp13
-rw-r--r--src/libs/utils/commandline.h16
-rw-r--r--src/libs/utils/crumblepath.cpp4
-rw-r--r--src/libs/utils/detailsbutton.cpp4
-rw-r--r--src/libs/utils/detailswidget.cpp2
-rw-r--r--src/libs/utils/devicefileaccess.cpp8
-rw-r--r--src/libs/utils/dropsupport.cpp4
-rw-r--r--src/libs/utils/environment.cpp92
-rw-r--r--src/libs/utils/environmentmodel.cpp3
-rw-r--r--src/libs/utils/execmenu.cpp3
-rw-r--r--src/libs/utils/expected.h9
-rw-r--r--src/libs/utils/externalterminalprocessimpl.cpp4
-rw-r--r--src/libs/utils/fancylineedit.cpp28
-rw-r--r--src/libs/utils/filepath.cpp76
-rw-r--r--src/libs/utils/filepath.h4
-rw-r--r--src/libs/utils/fileutils.cpp18
-rw-r--r--src/libs/utils/fileutils.h4
-rw-r--r--src/libs/utils/fsengine/fsengine_impl.h3
-rw-r--r--src/libs/utils/fsengine/fsenginehandler.cpp31
-rw-r--r--src/libs/utils/fsengine/rootinjectfsengine.h38
-rw-r--r--src/libs/utils/futuresynchronizer.cpp17
-rw-r--r--src/libs/utils/futuresynchronizer.h2
-rw-r--r--src/libs/utils/guiutils.cpp12
-rw-r--r--src/libs/utils/guiutils.h5
-rw-r--r--src/libs/utils/highlightingitemdelegate.cpp2
-rw-r--r--src/libs/utils/historycompleter.cpp6
-rw-r--r--src/libs/utils/icon.cpp10
-rw-r--r--src/libs/utils/iconbutton.cpp2
-rw-r--r--src/libs/utils/infobar.cpp4
-rw-r--r--src/libs/utils/infolabel.cpp5
-rw-r--r--src/libs/utils/layoutbuilder.cpp1070
-rw-r--r--src/libs/utils/layoutbuilder.h570
-rw-r--r--src/libs/utils/lua.cpp30
-rw-r--r--src/libs/utils/lua.h34
-rw-r--r--src/libs/utils/macroexpander.cpp2
-rw-r--r--src/libs/utils/macroexpander.h1
-rw-r--r--src/libs/utils/namevalueitem.cpp11
-rw-r--r--src/libs/utils/namevalueitem.h2
-rw-r--r--src/libs/utils/namevaluesdialog.cpp11
-rw-r--r--src/libs/utils/outputformatter.cpp79
-rw-r--r--src/libs/utils/outputformatter.h9
-rw-r--r--src/libs/utils/overlaywidget.cpp27
-rw-r--r--src/libs/utils/overlaywidget.h10
-rw-r--r--src/libs/utils/passworddialog.cpp8
-rw-r--r--src/libs/utils/persistentsettings.cpp30
-rw-r--r--src/libs/utils/projectintropage.cpp33
-rw-r--r--src/libs/utils/projectintropage.h1
-rw-r--r--src/libs/utils/qtcolorbutton.cpp4
-rw-r--r--src/libs/utils/qtcprocess.cpp7
-rw-r--r--src/libs/utils/reloadpromptutils.cpp6
-rw-r--r--src/libs/utils/smallstring.h113
-rw-r--r--src/libs/utils/store.cpp6
-rw-r--r--src/libs/utils/stringutils.cpp8
-rw-r--r--src/libs/utils/stringutils.h2
-rw-r--r--src/libs/utils/stylehelper.cpp139
-rw-r--r--src/libs/utils/stylehelper.h4
-rw-r--r--src/libs/utils/terminalinterface.cpp18
-rw-r--r--src/libs/utils/theme/theme.cpp44
-rw-r--r--src/libs/utils/theme/theme.h7
-rw-r--r--src/libs/utils/threadutils.cpp5
-rw-r--r--src/libs/utils/tooltip/tooltip.cpp8
-rw-r--r--src/libs/utils/treemodel.cpp6
-rw-r--r--src/libs/utils/unarchiver.cpp44
-rw-r--r--src/libs/utils/unarchiver.h6
-rw-r--r--src/libs/utils/utility.h14
-rw-r--r--src/libs/utils/utils.qbs5
-rw-r--r--src/libs/utils/wizard.cpp35
-rw-r--r--src/libs/utils/wizard.h4
-rw-r--r--src/libs/utils/wizardpage.h4
80 files changed, 2193 insertions, 1218 deletions
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt
index 3dff6c4f6f..ca5e88b66c 100644
--- a/src/libs/utils/CMakeLists.txt
+++ b/src/libs/utils/CMakeLists.txt
@@ -94,6 +94,7 @@ add_qtc_library(Utils
link.cpp link.h
listmodel.h
listutils.h
+ lua.cpp lua.h
macroexpander.cpp macroexpander.h
mathutils.cpp mathutils.h
mimeconstants.h
@@ -198,6 +199,7 @@ add_qtc_library(Utils
utilstr.h
utilsicons.cpp utilsicons.h
utiltypes.h
+ utility.h
variablechooser.cpp variablechooser.h
winutils.cpp winutils.h
wizard.cpp wizard.h
@@ -270,7 +272,6 @@ extend_qtc_library(Utils
fsengine/fsengine_impl.h
fsengine/diriterator.h
fsengine/fileiteratordevicesappender.h
- fsengine/rootinjectfsengine.h
fsengine/fixedlistfsengine.h
fsengine/fsenginehandler.cpp
fsengine/fsenginehandler.h
diff --git a/src/libs/utils/algorithm.h b/src/libs/utils/algorithm.h
index d1c3f37bff..849316c9a9 100644
--- a/src/libs/utils/algorithm.h
+++ b/src/libs/utils/algorithm.h
@@ -987,6 +987,14 @@ C filtered(const C &container, R (S::*predicate)() const)
return out;
}
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT C filtered(const C &container, R S::*predicate)
+{
+ C out;
+ std::copy_if(std::begin(container), std::end(container), inserter(out), std::mem_fn(predicate));
+ return out;
+}
+
//////////////////
// filteredCast
/////////////////
diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp
index 6ef84c119e..e6b8267c4e 100644
--- a/src/libs/utils/aspects.cpp
+++ b/src/libs/utils/aspects.cpp
@@ -10,6 +10,7 @@
#include "guard.h"
#include "iconbutton.h"
#include "layoutbuilder.h"
+#include "macroexpander.h"
#include "passworddialog.h"
#include "pathchooser.h"
#include "pathlisteditor.h"
@@ -27,6 +28,7 @@
#include <QDebug>
#include <QGroupBox>
#include <QLabel>
+#include <QLayout>
#include <QLineEdit>
#include <QListWidget>
#include <QPaintEvent>
@@ -38,6 +40,7 @@
#include <QSpinBox>
#include <QStandardItemModel>
#include <QTextEdit>
+#include <QTreeWidget>
#include <QUndoStack>
using namespace Layouting;
@@ -93,6 +96,7 @@ public:
BaseAspect::DataCloner m_dataCloner;
QList<BaseAspect::DataExtractor> m_dataExtractors;
+ MacroExpander *m_expander = globalMacroExpander();
QUndoStack *m_undoStack = nullptr;
};
@@ -103,16 +107,32 @@ public:
\brief The \c BaseAspect class provides a common base for classes implementing
aspects.
- An aspect is a hunk of data like a property or collection of related
+ An \e aspect is a hunk of data like a property or collection of related
properties of some object, together with a description of its behavior
for common operations like visualizing or persisting.
- Simple aspects are for example a boolean property represented by a QCheckBox
+ Simple aspects are, for example, a boolean property represented by a QCheckBox
in the user interface, or a string property represented by a PathChooser,
- selecting directories in the filesystem.
+ for selecting directories in the filesystem.
- While aspects implementations usually have the ability to visualize and to persist
+ While aspects implementations usually can visualize and persist
their data, or use an ID, neither of these is mandatory.
+
+ The derived classes can implement addToLayout() to create a UI.
+
+ Implement \c guiToBuffer() and \c bufferToGui() to synchronize the UI with
+ the internal data.
+*/
+
+/*!
+ \enum Utils::BaseAspect::Announcement
+
+ Whether to emit a signal when a value changes.
+
+ \value DoEmit
+ Emit a signal.
+ \value BeQuiet
+ Don't emit a signal.
*/
/*!
@@ -159,7 +179,9 @@ QVariant BaseAspect::variantValue() const
/*!
Sets \a value.
- Prefer the typed setValue() of derived classes.
+ If \a howToAnnounce is set to \c DoEmit, emits the \c valueChanged signal.
+
+ Prefer the typed \c setValue() of the derived classes.
*/
void BaseAspect::setVariantValue(const QVariant &value, Announcement howToAnnounce)
{
@@ -185,7 +207,20 @@ QVariant BaseAspect::defaultVariantValue() const
}
/*!
- \fn TypedAspect::setDefaultValue(const ValueType &value)
+ \class Utils::TypedAspect
+ \inheaderfile utils/aspects.h
+ \inmodule QtCreator
+
+ \brief The \c TypedAspect class is a helper class for implementing a simple
+ aspect.
+
+ A typed aspect contains a single piece of data that is of the type
+ \c ValueType.
+*/
+
+
+/*!
+ \fn template <typename ValueType> void Utils::TypedAspect<ValueType>::setDefaultValue(const ValueType &value)
Sets a default \a value and the current value for this aspect.
@@ -248,14 +283,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);
}
}
@@ -501,21 +536,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<BaseAspect &>(aspect).addToLayout(*item);
+ aspect.addToLayout(*iface);
}
-void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect)
+void addToLayout(Layouting::Layout *item, BaseAspect *aspect)
{
- const_cast<BaseAspect *>(aspect)->addToLayout(*item);
+ aspect->addToLayout(*item);
}
-
/*!
Updates this aspect's value from user-initiated changes in the widget.
@@ -692,6 +726,27 @@ QVariant BaseAspect::fromSettingsValue(const QVariant &val) const
return d->m_fromSettings ? d->m_fromSettings(val) : val;
}
+void BaseAspect::setMacroExpander(MacroExpander *expander)
+{
+ d->m_expander = expander;
+}
+
+MacroExpander *BaseAspect::macroExpander() const
+{
+ return d->m_expander;
+}
+
+void BaseAspect::addMacroExpansion(QWidget *w)
+{
+ if (!d->m_expander)
+ return;
+ const auto chooser = new VariableChooser(w);
+ chooser->addSupportedWidget(w);
+ chooser->addMacroExpanderProvider([this] { return d->m_expander; });
+ if (auto pathChooser = qobject_cast<PathChooser *>(w))
+ pathChooser->setMacroExpander(d->m_expander);
+}
+
namespace Internal {
class BoolAspectPrivate
@@ -827,15 +882,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);
@@ -855,7 +910,6 @@ public:
Qt::TextElideMode m_elideMode = Qt::ElideNone;
QString m_placeHolderText;
Key m_historyCompleterKey;
- MacroExpanderProvider m_expanderProvider;
StringAspect::ValueAcceptor m_valueAcceptor;
std::optional<FancyLineEdit::ValidationFunction> m_validator;
@@ -900,6 +954,10 @@ public:
class StringListAspectPrivate
{
public:
+ UndoableValue<QStringList> undoable;
+ bool allowAdding{true};
+ bool allowRemoving{true};
+ bool allowEditing{true};
};
class FilePathListAspectPrivate
@@ -939,9 +997,6 @@ public:
Based on QTextEdit, used for user-editable strings that often
do not fit on a line.
- \value PathChooserDisplay
- Based on Utils::PathChooser.
-
\value PasswordLineEditDisplay
Based on QLineEdit, used for password strings
@@ -1086,16 +1141,6 @@ void StringAspect::setAcceptRichText(bool acceptRichText)
emit acceptRichTextChanged(acceptRichText);
}
-void StringAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider)
-{
- d->m_expanderProvider = expanderProvider;
-}
-
-void StringAspect::setUseGlobalMacroExpander()
-{
- d->m_expanderProvider = &globalMacroExpander;
-}
-
void StringAspect::setUseResetButton()
{
d->m_useResetButton = true;
@@ -1112,18 +1157,10 @@ void StringAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished)
d->m_autoApplyOnEditingFinished = applyOnEditingFinished;
}
-void StringAspect::addToLayout(LayoutItem &parent)
+void StringAspect::addToLayout(Layout &parent)
{
d->m_checkerImpl.addToLayoutFirst(parent);
- const auto useMacroExpander = [this](QWidget *w) {
- if (!d->m_expanderProvider)
- return;
- const auto chooser = new VariableChooser(w);
- chooser->addSupportedWidget(w);
- chooser->addMacroExpanderProvider(d->m_expanderProvider);
- };
-
const QString displayedString = d->m_displayFilter ? d->m_displayFilter(volatileValue())
: volatileValue();
@@ -1131,6 +1168,7 @@ void StringAspect::addToLayout(LayoutItem &parent)
case PasswordLineEditDisplay:
case LineEditDisplay: {
auto lineEditDisplay = createSubWidget<FancyLineEdit>();
+ addMacroExpansion(lineEditDisplay);
lineEditDisplay->setPlaceholderText(d->m_placeHolderText);
if (!d->m_historyCompleterKey.isEmpty())
lineEditDisplay->setHistoryCompleter(d->m_historyCompleterKey);
@@ -1164,7 +1202,6 @@ void StringAspect::addToLayout(LayoutItem &parent)
}
addLabeledItem(parent, lineEditDisplay);
- useMacroExpander(lineEditDisplay);
if (d->m_useResetButton) {
auto resetButton = createSubWidget<QPushButton>(Tr::tr("Reset"));
resetButton->setEnabled(lineEditDisplay->text() != defaultValue());
@@ -1222,6 +1259,7 @@ void StringAspect::addToLayout(LayoutItem &parent)
}
case TextEditDisplay: {
auto textEditDisplay = createSubWidget<QTextEdit>();
+ addMacroExpansion(textEditDisplay);
textEditDisplay->setPlaceholderText(d->m_placeHolderText);
textEditDisplay->setUndoRedoEnabled(false);
textEditDisplay->setAcceptRichText(d->m_acceptRichText);
@@ -1240,7 +1278,6 @@ void StringAspect::addToLayout(LayoutItem &parent)
}
addLabeledItem(parent, textEditDisplay);
- useMacroExpander(textEditDisplay);
bufferToGui();
connect(this,
&StringAspect::acceptRichTextChanged,
@@ -1290,8 +1327,10 @@ void StringAspect::addToLayout(LayoutItem &parent)
QString StringAspect::expandedValue() const
{
- if (!m_internal.isEmpty() && d->m_expanderProvider)
- return d->m_expanderProvider()->expand(m_internal);
+ if (!m_internal.isEmpty()) {
+ if (auto expander = macroExpander())
+ return expander->expand(m_internal);
+ }
return m_internal;
}
@@ -1373,10 +1412,10 @@ public:
PathChooser::Kind m_expectedKind = PathChooser::File;
Environment m_environment;
QPointer<PathChooser> m_pathChooserDisplay;
- MacroExpanderProvider m_expanderProvider;
FilePath m_baseFileName;
StringAspect::ValueAcceptor m_valueAcceptor;
std::optional<FancyLineEdit::ValidationFunction> m_validator;
+ std::optional<FilePath> m_effectiveBinary;
std::function<void()> m_openTerminal;
CheckableAspectImplementation m_checkerImpl;
@@ -1397,6 +1436,8 @@ FilePathAspect::FilePathAspect(AspectContainer *container)
addDataExtractor(this, &FilePathAspect::value, &Data::value);
addDataExtractor(this, &FilePathAspect::operator(), &Data::filePath);
+
+ connect(this, &BaseAspect::changed, this, [this] { d->m_effectiveBinary.reset(); });
}
FilePathAspect::~FilePathAspect() = default;
@@ -1416,7 +1457,35 @@ FilePath FilePathAspect::operator()() const
FilePath FilePathAspect::expandedValue() const
{
- return FilePath::fromUserInput(TypedAspect::value());
+ const auto value = TypedAspect::value();
+ if (!value.isEmpty()) {
+ if (auto expander = macroExpander())
+ return FilePath::fromUserInput(expander->expand(value));
+ }
+ return FilePath::fromUserInput(value);
+}
+
+/*!
+ Returns the full path of the set command. Only makes a difference if
+ expected kind is \c Command or \c ExistingCommand and the current
+ file path is an executable provided without its path.
+ Performs a lookup in PATH if necessary.
+ */
+FilePath FilePathAspect::effectiveBinary() const
+{
+ if (d->m_effectiveBinary)
+ return *d->m_effectiveBinary;
+
+ const FilePath current = expandedValue();
+ const PathChooser::Kind kind = d->m_expectedKind;
+ if (kind != PathChooser::ExistingCommand && kind != PathChooser::Command)
+ return current;
+
+ if (current.needsDevice())
+ return current;
+
+ d->m_effectiveBinary.emplace(current.searchInPath());
+ return *d->m_effectiveBinary;
}
QString FilePathAspect::value() const
@@ -1425,7 +1494,9 @@ QString FilePathAspect::value() const
}
/*!
- Sets the value of this file path aspect to \a value.
+ Sets the value of this file path aspect to \a filePath.
+
+ If \a howToAnnounce is set to \c DoEmit, emits the \c valueChanged signal.
\note This does not use any check that the value is actually
a file path.
@@ -1518,21 +1589,14 @@ 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);
- const auto useMacroExpander = [this](QWidget *w) {
- if (!d->m_expanderProvider)
- return;
- const auto chooser = new VariableChooser(w);
- chooser->addSupportedWidget(w);
- chooser->addMacroExpanderProvider(d->m_expanderProvider);
- };
-
const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value();
d->m_pathChooserDisplay = createSubWidget<PathChooser>();
+ addMacroExpansion(d->m_pathChooserDisplay->lineEdit());
d->m_pathChooserDisplay->setExpectedKind(d->m_expectedKind);
if (!d->m_historyCompleterKey.isEmpty())
d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey);
@@ -1557,7 +1621,6 @@ void FilePathAspect::addToLayout(Layouting::LayoutItem &parent)
d->m_pathChooserDisplay->lineEdit()->setPlaceholderText(d->m_placeHolderText);
d->m_checkerImpl.updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data());
addLabeledItem(parent, d->m_pathChooserDisplay);
- useMacroExpander(d->m_pathChooserDisplay->lineEdit());
connect(d->m_pathChooserDisplay, &PathChooser::validChanged, this, &FilePathAspect::validChanged);
bufferToGui();
if (isAutoApply() && d->m_autoApplyOnEditingFinished) {
@@ -1655,9 +1718,12 @@ void FilePathAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished)
*/
void FilePathAspect::setExpectedKind(const PathChooser::Kind expectedKind)
{
- d->m_expectedKind = expectedKind;
- if (d->m_pathChooserDisplay)
- d->m_pathChooserDisplay->setExpectedKind(expectedKind);
+ if (d->m_expectedKind != expectedKind) {
+ d->m_expectedKind = expectedKind;
+ d->m_effectiveBinary.reset();
+ if (d->m_pathChooserDisplay)
+ d->m_pathChooserDisplay->setExpectedKind(expectedKind);
+ }
}
void FilePathAspect::setEnvironment(const Environment &env)
@@ -1698,11 +1764,6 @@ void FilePathAspect::setHistoryCompleter(const Key &historyCompleterKey)
d->m_pathChooserDisplay->setHistoryCompleter(historyCompleterKey);
}
-void FilePathAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider)
-{
- d->m_expanderProvider = expanderProvider;
-}
-
void FilePathAspect::validateInput()
{
if (d->m_pathChooserDisplay)
@@ -1736,7 +1797,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<QtColorButton>();
@@ -1913,7 +1974,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:
@@ -1922,7 +1983,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:
@@ -1940,20 +2001,18 @@ void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButto
});
}
-LayoutItem BoolAspect::adoptButton(QAbstractButton *button)
+std::function<void(Layouting::Layout *)> 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<QCheckBox>();
addToLayoutHelper(parent, checkBox);
@@ -2059,7 +2118,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);
@@ -2233,7 +2292,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())
@@ -2342,7 +2401,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<QSpinBox>();
@@ -2444,7 +2503,7 @@ DoubleAspect::~DoubleAspect() = default;
/*!
\reimp
*/
-void DoubleAspect::addToLayout(LayoutItem &builder)
+void DoubleAspect::addToLayout(Layout &builder)
{
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QDoubleSpinBox>();
@@ -2598,13 +2657,119 @@ StringListAspect::StringListAspect(AspectContainer *container)
*/
StringListAspect::~StringListAspect() = default;
-/*!
- \reimp
-*/
-void StringListAspect::addToLayout(LayoutItem &parent)
+bool StringListAspect::guiToBuffer()
{
- Q_UNUSED(parent)
- // TODO - when needed.
+ const QStringList newValue = d->undoable.get();
+ if (newValue != m_buffer) {
+ m_buffer = newValue;
+ return true;
+ }
+ return false;
+}
+
+void StringListAspect::bufferToGui()
+{
+ d->undoable.setWithoutUndo(m_buffer);
+}
+
+void StringListAspect::addToLayout(Layout &parent)
+{
+ d->undoable.setSilently(value());
+
+ auto editor = new QTreeWidget();
+ editor->setHeaderHidden(true);
+ editor->setRootIsDecorated(false);
+ editor->setEditTriggers(
+ d->allowEditing ? QAbstractItemView::AllEditTriggers : QAbstractItemView::NoEditTriggers);
+
+ QPushButton *add = d->allowAdding ? new QPushButton(Tr::tr("Add")) : nullptr;
+ QPushButton *remove = d->allowRemoving ? new QPushButton(Tr::tr("Remove")) : nullptr;
+
+ auto itemsToStringList = [editor] {
+ QStringList items;
+ const QTreeWidgetItem *rootItem = editor->invisibleRootItem();
+ for (int i = 0, count = rootItem->childCount(); i < count; ++i) {
+ auto expr = rootItem->child(i)->data(0, Qt::DisplayRole).toString();
+ items.append(expr);
+ }
+ return items;
+ };
+
+ auto populate = [editor, this] {
+ editor->clear();
+ for (const QString &entry : d->undoable.get()) {
+ auto item = new QTreeWidgetItem(editor, {entry});
+ item->setData(0, Qt::ToolTipRole, entry);
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
+ }
+ };
+
+ if (add) {
+ connect(add, &QPushButton::clicked, this, [this, populate, editor] {
+ d->undoable.setSilently(d->undoable.get() << "");
+ populate();
+ const QTreeWidgetItem *root = editor->invisibleRootItem();
+ QTreeWidgetItem *lastChild = root->child(root->childCount() - 1);
+ const QModelIndex index = editor->indexFromItem(lastChild, 0);
+ editor->edit(index);
+ });
+ }
+
+ if (remove) {
+ connect(remove, &QPushButton::clicked, this, [this, editor, itemsToStringList] {
+ const QList<QTreeWidgetItem *> selected = editor->selectedItems();
+ QTC_ASSERT(selected.size() == 1, return);
+ editor->invisibleRootItem()->removeChild(selected.first());
+ delete selected.first();
+ d->undoable.set(undoStack(), itemsToStringList());
+ });
+ }
+
+ connect(
+ &d->undoable.m_signal, &UndoSignaller::changed, editor, [this, populate, itemsToStringList] {
+ if (itemsToStringList() != d->undoable.get())
+ populate();
+
+ handleGuiChanged();
+ });
+
+ connect(
+ editor->model(),
+ &QAbstractItemModel::dataChanged,
+ this,
+ [this,
+ itemsToStringList](const QModelIndex &tl, const QModelIndex &br, const QList<int> &roles) {
+ if (!roles.contains(Qt::DisplayRole))
+ return;
+ if (tl != br)
+ return;
+ d->undoable.set(undoStack(), itemsToStringList());
+ });
+
+ populate();
+
+ parent.addItem(
+ // clang-format off
+ Column {
+ createLabel(),
+ Row {
+ editor,
+ If { d->allowAdding || d->allowRemoving, {
+ Column {
+ If { d->allowAdding, {add}, {}},
+ If { d->allowRemoving, {remove}, {}},
+ st,
+ }
+ }, {}},
+ }
+ } // clang-format on
+ );
+
+ registerSubWidget(editor);
+ if (d->allowAdding)
+ registerSubWidget(add);
+ if (d->allowRemoving)
+ registerSubWidget(remove);
}
void StringListAspect::appendValue(const QString &s, bool allowDuplicates)
@@ -2640,6 +2805,32 @@ void StringListAspect::removeValues(const QStringList &values)
setValue(val);
}
+void StringListAspect::setUiAllowAdding(bool allowAdding)
+{
+ d->allowAdding = allowAdding;
+}
+void StringListAspect::setUiAllowRemoving(bool allowRemoving)
+{
+ d->allowRemoving = allowRemoving;
+}
+void StringListAspect::setUiAllowEditing(bool allowEditing)
+{
+ d->allowEditing = allowEditing;
+}
+
+bool StringListAspect::uiAllowAdding() const
+{
+ return d->allowAdding;
+}
+bool StringListAspect::uiAllowRemoving() const
+{
+ return d->allowRemoving;
+}
+bool StringListAspect::uiAllowEditing() const
+{
+ return d->allowEditing;
+}
+
/*!
\class Utils::FilePathListAspect
\inmodule QtCreator
@@ -2677,7 +2868,7 @@ void FilePathListAspect::bufferToGui()
d->undoable.setWithoutUndo(m_buffer);
}
-void FilePathListAspect::addToLayout(LayoutItem &parent)
+void FilePathListAspect::addToLayout(Layout &parent)
{
d->undoable.setSilently(value());
@@ -2771,7 +2962,7 @@ IntegersAspect::~IntegersAspect() = default;
/*!
\reimp
*/
-void IntegersAspect::addToLayout(Layouting::LayoutItem &parent)
+void IntegersAspect::addToLayout(Layouting::Layout &parent)
{
Q_UNUSED(parent)
// TODO - when needed.
@@ -2790,8 +2981,8 @@ void IntegersAspect::addToLayout(Layouting::LayoutItem &parent)
*/
/*!
- Constructs a text display showing the \a message with an icon representing
- type \a type.
+ Constructs a text display with the parent \a container. The display shows
+ \a message and an icon representing the type \a type.
*/
TextDisplay::TextDisplay(AspectContainer *container, const QString &message, InfoLabel::InfoType type)
: BaseAspect(container), d(new Internal::TextDisplayPrivate)
@@ -2808,7 +2999,7 @@ TextDisplay::~TextDisplay() = default;
/*!
\reimp
*/
-void TextDisplay::addToLayout(LayoutItem &parent)
+void TextDisplay::addToLayout(Layout &parent)
{
if (!d->m_label) {
d->m_label = createSubWidget<InfoLabel>(d->m_message, d->m_type);
@@ -2860,7 +3051,7 @@ public:
QList<BaseAspect *> m_items; // Both owned and non-owned.
QList<BaseAspect *> m_ownedItems; // Owned only.
QStringList m_settingsGroup;
- std::function<Layouting::LayoutItem ()> m_layouter;
+ std::function<Layouting::Layout()> m_layouter;
};
AspectContainer::AspectContainer()
@@ -2875,6 +3066,11 @@ AspectContainer::~AspectContainer()
qDeleteAll(d->m_ownedItems);
}
+void AspectContainer::addToLayout(Layouting::Layout &parent)
+{
+ parent.addItem(layouter()());
+}
+
/*!
\internal
*/
@@ -2884,6 +3080,8 @@ void AspectContainer::registerAspect(BaseAspect *aspect, bool takeOwnership)
d->m_items.append(aspect);
if (takeOwnership)
d->m_ownedItems.append(aspect);
+
+ connect(aspect, &BaseAspect::changed, this, [this]() { emit changed(); });
}
void AspectContainer::registerAspects(const AspectContainer &aspects)
@@ -2912,12 +3110,12 @@ AspectContainer::const_iterator AspectContainer::end() const
return d->m_items.cend();
}
-void AspectContainer::setLayouter(const std::function<Layouting::LayoutItem ()> &layouter)
+void AspectContainer::setLayouter(const std::function<Layouting::Layout ()> &layouter)
{
d->m_layouter = layouter;
}
-std::function<LayoutItem ()> AspectContainer::layouter() const
+std::function<Layout ()> AspectContainer::layouter() const
{
return d->m_layouter;
}
@@ -3033,6 +3231,14 @@ void AspectContainer::setUndoStack(QUndoStack *undoStack)
aspect->setUndoStack(undoStack);
}
+void AspectContainer::setMacroExpander(MacroExpander *expander)
+{
+ BaseAspect::setMacroExpander(expander);
+
+ for (BaseAspect *aspect : std::as_const(d->m_items))
+ aspect->setMacroExpander(expander);
+}
+
bool AspectContainer::equals(const AspectContainer &other) const
{
// FIXME: Expensive, but should not really be needed in a fully aspectified world.
@@ -3414,7 +3620,7 @@ private:
int m_index;
};
-void AspectList::addToLayout(Layouting::LayoutItem &parent)
+void AspectList::addToLayout(Layouting::Layout &parent)
{
using namespace Layouting;
@@ -3433,7 +3639,7 @@ void AspectList::addToLayout(Layouting::LayoutItem &parent)
addItem(d->createItem());
});
- Column column{noMargin()};
+ Column column{noMargin};
forEachItem<BaseAspect>([&column, this](const std::shared_ptr<BaseAspect> &item, int idx) {
auto removeBtn = new IconButton;
@@ -3524,7 +3730,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);
@@ -3597,4 +3803,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 a5c22bb858..60a198c1f7 100644
--- a/src/libs/utils/aspects.h
+++ b/src/libs/utils/aspects.h
@@ -7,7 +7,6 @@
#include "guiutils.h"
#include "id.h"
#include "infolabel.h"
-#include "macroexpander.h"
#include "pathchooser.h"
#include "qtcsettings.h"
#include "store.h"
@@ -29,15 +28,14 @@ class QStandardItemModel;
class QItemSelectionModel;
QT_END_NAMESPACE
-namespace Layouting {
-class LayoutItem;
-}
+namespace Layouting { class Layout; }
namespace Utils {
class AspectContainer;
class BoolAspect;
class CheckableDecider;
+class MacroExpander;
namespace Internal {
class AspectContainerPrivate;
@@ -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;
@@ -205,6 +202,9 @@ public:
// This is expensive. Do not use without good reason
void writeToSettingsImmediatly() const;
+ void setMacroExpander(MacroExpander *expander);
+ MacroExpander *macroExpander() const;
+
signals:
void changed();
void volatileValueChanged();
@@ -222,8 +222,10 @@ protected:
virtual void handleGuiChanged();
+ void addMacroExpansion(QWidget *w);
+
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 +278,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<typename ValueType>
class
@@ -439,7 +441,7 @@ public:
BoolAspect(AspectContainer *container = nullptr);
~BoolAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
std::function<void(QObject *)> groupChecker();
Utils::CheckableDecider askAgainCheckableDecider();
@@ -452,10 +454,10 @@ public:
LabelPlacement labelPlacement = LabelPlacement::InExtraLabel);
void setLabelPlacement(LabelPlacement labelPlacement);
- Layouting::LayoutItem adoptButton(QAbstractButton *button);
+ std::function<void(Layouting::Layout *)> 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 +506,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 +523,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 +571,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 +598,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;
@@ -610,8 +612,6 @@ public:
void setPlaceHolderText(const QString &placeHolderText);
void setHistoryCompleter(const Key &historyCompleterKey);
void setAcceptRichText(bool acceptRichText);
- void setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider);
- void setUseGlobalMacroExpander();
void setUseResetButton();
void setValidationFunction(const FancyLineEdit::ValidationFunction &validator);
void setAutoApplyOnEditingFinished(bool applyOnEditingFinished);
@@ -666,6 +666,7 @@ public:
};
FilePath operator()() const;
+ FilePath effectiveBinary() const;
FilePath expandedValue() const;
QString value() const;
void setValue(const FilePath &filePath, Announcement howToAnnounce = DoEmit);
@@ -687,7 +688,6 @@ public:
void setValidationFunction(const FancyLineEdit::ValidationFunction &validator);
void setDisplayFilter(const std::function<QString (const QString &)> &displayFilter);
void setHistoryCompleter(const Key &historyCompleterKey);
- void setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider);
void setShowToolTipOnLabel(bool show);
void setAutoApplyOnEditingFinished(bool applyOnEditingFinished);
@@ -703,7 +703,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 +730,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 +759,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,13 +831,24 @@ public:
StringListAspect(AspectContainer *container = nullptr);
~StringListAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ bool guiToBuffer() override;
+ void bufferToGui() override;
+
+ void addToLayout(Layouting::Layout &parent) override;
void appendValue(const QString &value, bool allowDuplicates = true);
void removeValue(const QString &value);
void appendValues(const QStringList &values, bool allowDuplicates = true);
void removeValues(const QStringList &values);
+ void setUiAllowAdding(bool allowAdding);
+ void setUiAllowRemoving(bool allowRemoving);
+ void setUiAllowEditing(bool allowEditing);
+
+ bool uiAllowAdding() const;
+ bool uiAllowRemoving() const;
+ bool uiAllowEditing() const;
+
private:
std::unique_ptr<Internal::StringListAspectPrivate> d;
};
@@ -855,7 +866,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 +886,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 +899,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 +950,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);
@@ -964,6 +977,8 @@ public:
bool isDirty() override;
void setUndoStack(QUndoStack *undoStack) override;
+ void setMacroExpander(MacroExpander *expander);
+
template <typename T> T *aspect() const
{
for (BaseAspect *aspect : aspects())
@@ -989,8 +1004,8 @@ public:
const_iterator begin() const;
const_iterator end() const;
- void setLayouter(const std::function<Layouting::LayoutItem()> &layouter);
- std::function<Layouting::LayoutItem()> layouter() const;
+ void setLayouter(const std::function<Layouting::Layout()> &layouter);
+ std::function<Layouting::Layout()> layouter() const;
signals:
void applied();
@@ -1131,7 +1146,7 @@ public:
QVariant volatileVariantValue() const override { return {}; }
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
private:
std::unique_ptr<Internal::AspectListPrivate> d;
@@ -1143,7 +1158,7 @@ class QTCREATOR_UTILS_EXPORT StringSelectionAspect : public Utils::TypedAspect<Q
public:
StringSelectionAspect(Utils::AspectContainer *container = nullptr);
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
using ResultCallback = std::function<void(QList<QStandardItem *> items)>;
using FillCallback = std::function<void(ResultCallback)>;
diff --git a/src/libs/utils/async.h b/src/libs/utils/async.h
index af6fb3fd85..8c7155bc13 100644
--- a/src/libs/utils/async.h
+++ b/src/libs/utils/async.h
@@ -7,6 +7,7 @@
#include "futuresynchronizer.h"
#include "qtcassert.h"
+#include "threadutils.h"
#include <solutions/tasking/tasktree.h>
@@ -130,7 +131,9 @@ template <typename ResultType>
class Async : public AsyncBase
{
public:
- Async() {
+ Async()
+ : m_synchronizer(isMainThread() ? futureSynchronizer() : nullptr)
+ {
connect(&m_watcher, &QFutureWatcherBase::finished, this, &AsyncBase::done);
connect(&m_watcher, &QFutureWatcherBase::resultReadyAt, this, &AsyncBase::resultReadyAt);
}
diff --git a/src/libs/utils/changeset.cpp b/src/libs/utils/changeset.cpp
index c0c54b1e48..881097c6b7 100644
--- a/src/libs/utils/changeset.cpp
+++ b/src/libs/utils/changeset.cpp
@@ -100,6 +100,41 @@ void ChangeSet::clear()
m_error = false;
}
+ChangeSet ChangeSet::makeReplace(const Range &range, const QString &replacement)
+{
+ ChangeSet c;
+ c.replace(range, replacement);
+ return c;
+}
+
+ChangeSet ChangeSet::makeReplace(int start, int end, const QString &replacement)
+{
+ ChangeSet c;
+ c.replace(start, end, replacement);
+ return c;
+}
+
+ChangeSet ChangeSet::makeRemove(const Range &range)
+{
+ ChangeSet c;
+ c.remove(range);
+ return c;
+}
+
+ChangeSet ChangeSet::makeFlip(int start1, int end1, int start2, int end2)
+{
+ ChangeSet c;
+ c.flip(start1, end1, start2, end2);
+ return c;
+}
+
+ChangeSet ChangeSet::makeInsert(int pos, const QString &text)
+{
+ ChangeSet c;
+ c.insert(pos, text);
+ return c;
+}
+
bool ChangeSet::replace_helper(int pos, int length, const QString &replacement)
{
if (hasOverlap(pos, length))
diff --git a/src/libs/utils/changeset.h b/src/libs/utils/changeset.h
index 6370b20d3c..aee9c99dab 100644
--- a/src/libs/utils/changeset.h
+++ b/src/libs/utils/changeset.h
@@ -76,6 +76,12 @@ public:
void clear();
+ static ChangeSet makeReplace(const Range &range, const QString &replacement);
+ static ChangeSet makeReplace(int start, int end, const QString &replacement);
+ static ChangeSet makeRemove(const Range &range);
+ static ChangeSet makeFlip(int start1, int end1, int start2, int end2);
+ static ChangeSet makeInsert(int pos, const QString &text);
+
bool replace(const Range &range, const QString &replacement);
bool remove(const Range &range);
bool move(const Range &range, int to);
diff --git a/src/libs/utils/checkablemessagebox.cpp b/src/libs/utils/checkablemessagebox.cpp
index 19d3669ada..1d721e04c8 100644
--- a/src/libs/utils/checkablemessagebox.cpp
+++ b/src/libs/utils/checkablemessagebox.cpp
@@ -3,6 +3,7 @@
#include "checkablemessagebox.h"
+#include "guiutils.h"
#include "hostosinfo.h"
#include "qtcassert.h"
#include "qtcsettings.h"
@@ -95,7 +96,7 @@ static QMessageBox::StandardButton exec(
return acceptButton;
}
- QMessageBox msgBox(parent);
+ QMessageBox msgBox(dialogParent(parent));
prepare(icon, title, text, decider, buttons, defaultButton, buttonTextOverrides, msg, msgBox);
msgBox.exec();
@@ -131,7 +132,7 @@ static void show(QWidget *parent,
return;
}
- QMessageBox *msgBox = new QMessageBox(parent);
+ QMessageBox *msgBox = new QMessageBox(dialogParent(parent));
prepare(icon, title, text, decider, buttons, defaultButton, buttonTextOverrides, msg, *msgBox);
std::optional<QPointer<QObject>> guardPtr;
diff --git a/src/libs/utils/clangutils.cpp b/src/libs/utils/clangutils.cpp
index 4c8c7d801d..86e8e6204e 100644
--- a/src/libs/utils/clangutils.cpp
+++ b/src/libs/utils/clangutils.cpp
@@ -65,7 +65,7 @@ bool checkClangdVersion(const FilePath &clangd, QString *error)
QVersionNumber minimumClangdVersion()
{
- return QVersionNumber(14);
+ return QVersionNumber(17);
}
} // namespace Utils
diff --git a/src/libs/utils/codegeneration.cpp b/src/libs/utils/codegeneration.cpp
index 8842023a23..ad3b8bad18 100644
--- a/src/libs/utils/codegeneration.cpp
+++ b/src/libs/utils/codegeneration.cpp
@@ -29,23 +29,6 @@ QTCREATOR_UTILS_EXPORT QString fileNameToCppIdentifier(const QString &s)
return rc;
}
-QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file)
-{
- return headerGuard(file, QStringList());
-}
-
-QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file, const QStringList &namespaceList)
-{
- const QChar underscore = QLatin1Char('_');
- QString rc;
- for (int i = 0; i < namespaceList.count(); i++)
- rc += namespaceList.at(i).toUpper() + underscore;
-
- const QFileInfo fi(file);
- rc += fileNameToCppIdentifier(fi.fileName()).toUpper();
- return rc;
-}
-
QTCREATOR_UTILS_EXPORT
void writeIncludeFileDirective(const QString &file, bool globalInclude,
QTextStream &str)
diff --git a/src/libs/utils/codegeneration.h b/src/libs/utils/codegeneration.h
index 9ea0bb28c7..e1c297eb92 100644
--- a/src/libs/utils/codegeneration.h
+++ b/src/libs/utils/codegeneration.h
@@ -17,9 +17,6 @@ namespace Utils {
// or replacing them by an underscore).
QTCREATOR_UTILS_EXPORT QString fileNameToCppIdentifier(const QString &s);
-QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file);
-QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file, const QStringList &namespaceList);
-
QTCREATOR_UTILS_EXPORT
void writeIncludeFileDirective(const QString &file,
bool globalInclude,
diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp
index 8e6bc60277..b4fa60481e 100644
--- a/src/libs/utils/commandline.cpp
+++ b/src/libs/utils/commandline.cpp
@@ -1425,6 +1425,19 @@ CommandLine::CommandLine(const FilePath &exe, const QStringList &args)
addArgs(args);
}
+CommandLine::CommandLine(const FilePath &exe, std::initializer_list<ArgRef> args)
+ : m_executable(exe)
+{
+ for (const ArgRef &arg : args) {
+ if (const auto ptr = std::get_if<const char *>(&arg.m_arg))
+ addArg(QString::fromUtf8(*ptr));
+ else if (const auto ptr = std::get_if<std::reference_wrapper<const QString>>(&arg.m_arg))
+ addArg(*ptr);
+ else if (const auto ptr = std::get_if<std::reference_wrapper<const QStringList>>(&arg.m_arg))
+ addArgs(*ptr);
+ }
+}
+
CommandLine::CommandLine(const FilePath &exe, const QStringList &args, OsType osType)
: m_executable(exe)
{
diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h
index 52ff8c5496..3718f80bca 100644
--- a/src/libs/utils/commandline.h
+++ b/src/libs/utils/commandline.h
@@ -11,6 +11,8 @@
#include <QPair>
#include <QStringList>
+#include <variant>
+
namespace Utils {
class AbstractMacroExpander;
@@ -120,8 +122,22 @@ public:
CommandLine();
~CommandLine();
+ struct ArgRef
+ {
+ ArgRef(const char *arg) : m_arg(arg) {}
+ ArgRef(const QString &arg) : m_arg(arg) {}
+ ArgRef(const QStringList &args) : m_arg(args) {}
+
+ private:
+ friend class CommandLine;
+ const std::variant<const char *,
+ std::reference_wrapper<const QString>,
+ std::reference_wrapper<const QStringList>> m_arg;
+ };
+
explicit CommandLine(const FilePath &executable);
CommandLine(const FilePath &exe, const QStringList &args);
+ CommandLine(const FilePath &exe, std::initializer_list<ArgRef> args);
CommandLine(const FilePath &exe, const QStringList &args, OsType osType);
CommandLine(const FilePath &exe, const QString &unparsedArgs, RawType);
diff --git a/src/libs/utils/crumblepath.cpp b/src/libs/utils/crumblepath.cpp
index 779b1c9e55..d434350460 100644
--- a/src/libs/utils/crumblepath.cpp
+++ b/src/libs/utils/crumblepath.cpp
@@ -111,9 +111,9 @@ void CrumblePathButton::paintEvent(QPaintEvent*)
p.drawPixmap(width() - overlapSize, segmentRect.top(), middleSegmentPixmap);
if (option.state & QStyle::State_Enabled)
- option.palette.setColor(QPalette::ButtonText, creatorTheme()->color(Theme::PanelTextColorLight));
+ option.palette.setColor(QPalette::ButtonText, creatorColor(Theme::PanelTextColorLight));
else
- option.palette.setColor(QPalette::Disabled, QPalette::ButtonText, creatorTheme()->color(Theme::IconsDisabledColor));
+ option.palette.setColor(QPalette::Disabled, QPalette::ButtonText, creatorColor(Theme::IconsDisabledColor));
QStylePainter sp(this);
if (option.state & QStyle::State_Sunken)
diff --git a/src/libs/utils/detailsbutton.cpp b/src/libs/utils/detailsbutton.cpp
index 88c0ca273e..c6e1b6da3f 100644
--- a/src/libs/utils/detailsbutton.cpp
+++ b/src/libs/utils/detailsbutton.cpp
@@ -87,8 +87,8 @@ QColor DetailsButton::outlineColor()
{
return HostOsInfo::isMacHost()
? QGuiApplication::palette().color(QPalette::Mid)
- : StyleHelper::mergedColors(creatorTheme()->color(Theme::TextColorNormal),
- creatorTheme()->color(Theme::BackgroundColorNormal), 15);
+ : StyleHelper::mergedColors(creatorColor(Theme::TextColorNormal),
+ creatorColor(Theme::BackgroundColorNormal), 15);
}
void DetailsButton::paintEvent(QPaintEvent *e)
diff --git a/src/libs/utils/detailswidget.cpp b/src/libs/utils/detailswidget.cpp
index ec7a3d22eb..d278a609ff 100644
--- a/src/libs/utils/detailswidget.cpp
+++ b/src/libs/utils/detailswidget.cpp
@@ -224,7 +224,7 @@ void DetailsWidget::paintEvent(QPaintEvent *paintEvent)
QPainter p(this);
if (creatorTheme()->flag(Theme::FlatProjectsMode) || HostOsInfo::isMacHost()) {
const QColor bgColor = creatorTheme()->flag(Theme::FlatProjectsMode) ?
- creatorTheme()->color(Theme::DetailsWidgetBackgroundColor)
+ creatorColor(Theme::DetailsWidgetBackgroundColor)
: palette().color(QPalette::Window);
p.fillRect(rect(), bgColor);
}
diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp
index 50d1f276ef..303ffa6bf5 100644
--- a/src/libs/utils/devicefileaccess.cpp
+++ b/src/libs/utils/devicefileaccess.cpp
@@ -1059,6 +1059,10 @@ expected_str<QByteArray> UnixDeviceFileAccess::fileContents(const FilePath &file
if (disconnected())
return make_unexpected_disconnected();
+ expected_str<FilePath> localSource = filePath.localSource();
+ if (localSource && *localSource != filePath)
+ return localSource->fileContents(limit, offset);
+
QStringList args = {"if=" + filePath.path()};
if (limit > 0 || offset > 0) {
const qint64 gcd = std::gcd(limit, offset);
@@ -1088,6 +1092,10 @@ expected_str<qint64> UnixDeviceFileAccess::writeFileContents(const FilePath &fil
if (disconnected())
return make_unexpected_disconnected();
+ expected_str<FilePath> localSource = filePath.localSource();
+ if (localSource && *localSource != filePath)
+ return localSource->writeFileContents(data);
+
QStringList args = {"of=" + filePath.path()};
RunResult result = runInShell({"dd", args, OsType::OsTypeLinux}, data);
diff --git a/src/libs/utils/dropsupport.cpp b/src/libs/utils/dropsupport.cpp
index 7416edf9c3..4ed1245643 100644
--- a/src/libs/utils/dropsupport.cpp
+++ b/src/libs/utils/dropsupport.cpp
@@ -108,7 +108,7 @@ bool DropSupport::eventFilter(QObject *obj, QEvent *event)
de->acceptProposedAction();
bool needToScheduleEmit = m_files.isEmpty();
m_files.append(tempFiles);
- m_dropPos = de->pos();
+ m_dropPos = de->position().toPoint();
if (needToScheduleEmit) { // otherwise we already have a timer pending
// Delay the actual drop, to avoid conflict between
// actions that happen when opening files, and actions that the item views do
@@ -122,7 +122,7 @@ bool DropSupport::eventFilter(QObject *obj, QEvent *event)
accepted = true;
bool needToScheduleEmit = m_values.isEmpty();
m_values.append(fileDropMimeData->values());
- m_dropPos = de->pos();
+ m_dropPos = de->position().toPoint();
if (needToScheduleEmit)
QTimer::singleShot(100, this, &DropSupport::emitValuesDropped);
}
diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp
index be43f2a206..190cde2d2d 100644
--- a/src/libs/utils/environment.cpp
+++ b/src/libs/utils/environment.cpp
@@ -433,60 +433,82 @@ const NameValueDictionary &Environment::resolved() const
m_dict = Environment::systemEnvironment().toDictionary();
m_fullDict = true;
break;
- case SetFixedDictionary:
- m_dict = std::get<SetFixedDictionary>(item);
- m_fullDict = true;
+ case SetFixedDictionary: {
+ const auto dict = std::get_if<SetFixedDictionary>(&item);
+ if (QTC_GUARD(dict)) {
+ m_dict = *dict;
+ m_fullDict = true;
+ }
break;
+ }
case SetValue: {
- auto [key, value, enabled] = std::get<SetValue>(item);
- m_dict.set(key, value, enabled);
+ const auto setvalue = std::get_if<SetValue>(&item);
+ if (QTC_GUARD(setvalue)) {
+ auto [key, value, enabled] = *setvalue;
+ m_dict.set(key, value, enabled);
+ }
break;
}
case SetFallbackValue: {
- auto [key, value] = std::get<SetFallbackValue>(item);
- if (m_fullDict) {
- if (m_dict.value(key).isEmpty())
+ const auto fallbackvalue = std::get_if<SetFallbackValue>(&item);
+ if (QTC_GUARD(fallbackvalue)) {
+ auto [key, value] = *fallbackvalue;
+ if (m_fullDict) {
+ if (m_dict.value(key).isEmpty())
+ m_dict.set(key, value, true);
+ } else {
+ QTC_ASSERT(false, qDebug() << "operating on partial dictionary");
m_dict.set(key, value, true);
- } else {
- QTC_ASSERT(false, qDebug() << "operating on partial dictionary");
- m_dict.set(key, value, true);
+ }
}
break;
}
- case UnsetValue:
- m_dict.unset(std::get<UnsetValue>(item));
+ case UnsetValue: {
+ const auto unsetvalue = std::get_if<UnsetValue>(&item);
+ if (QTC_GUARD(unsetvalue))
+ m_dict.unset(*unsetvalue);
break;
+ }
case PrependOrSet: {
- auto [key, value, sep] = std::get<PrependOrSet>(item);
- QTC_ASSERT(!key.contains('='), return m_dict);
- const auto it = m_dict.findKey(key);
- if (it == m_dict.m_values.end()) {
- m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
- } else {
- // Prepend unless it is already there
- const QString toPrepend = value + pathListSeparator(sep);
- if (!it.value().first.startsWith(toPrepend))
- it.value().first.prepend(toPrepend);
+ const auto prependorset = std::get_if<PrependOrSet>(&item);
+ if (QTC_GUARD(prependorset)) {
+ auto [key, value, sep] = *prependorset;
+ QTC_ASSERT(!key.contains('='), return m_dict);
+ const auto it = m_dict.findKey(key);
+ if (it == m_dict.m_values.end()) {
+ m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
+ } else {
+ // Prepend unless it is already there
+ const QString toPrepend = value + pathListSeparator(sep);
+ if (!it.value().first.startsWith(toPrepend))
+ it.value().first.prepend(toPrepend);
+ }
}
break;
}
case AppendOrSet: {
- auto [key, value, sep] = std::get<AppendOrSet>(item);
- QTC_ASSERT(!key.contains('='), return m_dict);
- const auto it = m_dict.findKey(key);
- if (it == m_dict.m_values.end()) {
- m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
- } else {
- // Prepend unless it is already there
- const QString toAppend = pathListSeparator(sep) + value;
- if (!it.value().first.endsWith(toAppend))
- it.value().first.append(toAppend);
+ const auto appendorset = std::get_if<AppendOrSet>(&item);
+ if (QTC_GUARD(appendorset)) {
+ auto [key, value, sep] = *appendorset;
+ QTC_ASSERT(!key.contains('='), return m_dict);
+ const auto it = m_dict.findKey(key);
+ if (it == m_dict.m_values.end()) {
+ m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
+ } else {
+ // Prepend unless it is already there
+ const QString toAppend = pathListSeparator(sep) + value;
+ if (!it.value().first.endsWith(toAppend))
+ it.value().first.append(toAppend);
+ }
}
break;
}
case Modify: {
- EnvironmentItems items = std::get<Modify>(item);
- m_dict.modify(items);
+ const auto modify = std::get_if<Modify>(&item);
+ if (QTC_GUARD(modify)) {
+ EnvironmentItems items = *modify;
+ m_dict.modify(items);
+ }
break;
}
case SetupEnglishOutput:
diff --git a/src/libs/utils/environmentmodel.cpp b/src/libs/utils/environmentmodel.cpp
index 6ac9b35640..e81ee520d8 100644
--- a/src/libs/utils/environmentmodel.cpp
+++ b/src/libs/utils/environmentmodel.cpp
@@ -397,7 +397,8 @@ EnvironmentItems EnvironmentModel::userChanges() const
void EnvironmentModel::setUserChanges(const EnvironmentItems &items)
{
EnvironmentItems filtered = Utils::filtered(items, [](const EnvironmentItem &i) {
- return i.name != "export " && !i.name.contains('=');
+ return i.operation == EnvironmentItem::Comment
+ || (i.name != "export " && !i.name.contains('='));
});
// We assume nobody is reordering the items here.
if (filtered == d->m_items)
diff --git a/src/libs/utils/execmenu.cpp b/src/libs/utils/execmenu.cpp
index 13d7915295..4e463bdd6f 100644
--- a/src/libs/utils/execmenu.cpp
+++ b/src/libs/utils/execmenu.cpp
@@ -44,7 +44,8 @@ QAction *execMenuAtWidget(QMenu *menu, QWidget *widget)
}
/*!
- Adds tool tips to the menu that show the actions tool tip when hovering over an entry.
+ Adds tool tips to the \a menu that show the action's tool tip when hovering
+ over an entry.
*/
void addToolTipsToMenu(QMenu *menu)
{
diff --git a/src/libs/utils/expected.h b/src/libs/utils/expected.h
index 33231c1246..0e018eb8ab 100644
--- a/src/libs/utils/expected.h
+++ b/src/libs/utils/expected.h
@@ -30,3 +30,12 @@ using expected_str = tl::expected<T, QString>;
} \
do { \
} while (0)
+
+#define QTC_CHECK_EXPECTED(expected) \
+ if (Q_LIKELY(expected)) { \
+ } else { \
+ ::Utils::writeAssertLocation( \
+ QString("%1:%2: %3").arg(__FILE__).arg(__LINE__).arg(expected.error()).toUtf8().data()); \
+ } \
+ do { \
+ } while (0)
diff --git a/src/libs/utils/externalterminalprocessimpl.cpp b/src/libs/utils/externalterminalprocessimpl.cpp
index 41b7259b8f..693905bddc 100644
--- a/src/libs/utils/externalterminalprocessimpl.cpp
+++ b/src/libs/utils/externalterminalprocessimpl.cpp
@@ -181,7 +181,7 @@ expected_str<qint64> ProcessStubCreator::startStubProcess(const ProcessSetupData
process->setProcessMode(ProcessMode::Writer);
} else {
QString extraArgsFromOptions = terminal.executeArgs;
- CommandLine cmdLine = {terminal.command, {}};
+ CommandLine cmdLine{terminal.command};
if (!extraArgsFromOptions.isEmpty())
cmdLine.addArgs(extraArgsFromOptions, CommandLine::Raw);
cmdLine.addCommandLineAsArgs(setupData.m_commandLine, CommandLine::Raw);
@@ -190,8 +190,6 @@ expected_str<qint64> ProcessStubCreator::startStubProcess(const ProcessSetupData
process->setEnvironment(
setupData.m_environment.appliedToEnvironment(Environment::systemEnvironment()));
- process->setEnvironment(setupData.m_environment);
-
process->start();
process->waitForStarted();
if (process->error() != QProcess::UnknownError) {
diff --git a/src/libs/utils/fancylineedit.cpp b/src/libs/utils/fancylineedit.cpp
index 44a9423608..69d7209c3a 100644
--- a/src/libs/utils/fancylineedit.cpp
+++ b/src/libs/utils/fancylineedit.cpp
@@ -5,6 +5,7 @@
#include "camelcasecursor.h"
#include "execmenu.h"
+#include "futuresynchronizer.h"
#include "historycompleter.h"
#include "hostosinfo.h"
#include "icon.h"
@@ -98,6 +99,7 @@ class FancyLineEditPrivate : public QObject
{
public:
explicit FancyLineEditPrivate(FancyLineEdit *parent);
+ ~FancyLineEditPrivate();
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -134,8 +136,8 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent)
: QObject(parent)
, m_lineEdit(parent)
, m_completionShortcut(completionShortcut()->key(), parent)
- , m_okTextColor(creatorTheme()->color(Theme::TextColorNormal))
- , m_errorTextColor(creatorTheme()->color(Theme::TextColorError))
+ , m_okTextColor(creatorColor(Theme::TextColorNormal))
+ , m_errorTextColor(creatorColor(Theme::TextColorError))
, m_placeholderTextColor(QApplication::palette().color(QPalette::PlaceholderText))
, m_spinner(new SpinnerSolution::Spinner(SpinnerSolution::SpinnerSize::Small, m_lineEdit))
{
@@ -163,6 +165,12 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent)
}
}
+FancyLineEditPrivate::~FancyLineEditPrivate()
+{
+ if (m_validatorWatcher)
+ m_validatorWatcher->cancel();
+}
+
bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
{
int buttonIndex = -1;
@@ -446,12 +454,27 @@ void FancyLineEdit::setFiltering(bool on)
}
}
+/*!
+ Set a synchronous or asynchronous validation function \a fn.
+ Asynchronous validation functions can continue to run after destruction of the
+ FancyLineEdit instance. During shutdown asynchronous validation functions can continue
+ to run until before the plugin instances are deleted (at that point the plugin manager
+ waits for them to finish before continuing).
+
+ \sa defaultValidationFunction()
+ */
void FancyLineEdit::setValidationFunction(const FancyLineEdit::ValidationFunction &fn)
{
d->m_validationFunction = fn;
validate();
}
+/*!
+ Returns the default validation function, which synchonously executes the line edit's
+ validator.
+
+ \sa setValidationFunction()
+*/
FancyLineEdit::ValidationFunction FancyLineEdit::defaultValidationFunction()
{
return &FancyLineEdit::validateWithValidator;
@@ -581,6 +604,7 @@ void FancyLineEdit::validate()
AsyncValidationFuture future = validationFunction(text());
d->m_validatorWatcher->setFuture(future);
+ Utils::futureSynchronizer()->addFuture(future);
return;
}
diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp
index 1b9b6e943c..c4d77a29ef 100644
--- a/src/libs/utils/filepath.cpp
+++ b/src/libs/utils/filepath.cpp
@@ -19,9 +19,11 @@
#include <QFileInfo>
#include <QRegularExpression>
#include <QStringView>
+#include <QTemporaryFile>
#include <QUrl>
+
+#include <QtConcurrent>
#include <QtGlobal>
-#include <QTemporaryFile>
#ifdef Q_OS_WIN
#ifdef QTCREATOR_PCH_H
@@ -291,6 +293,26 @@ QString FilePath::toString() const
return scheme() + "://" + encodedHost() + pathView();
}
+bool FilePath::equals(const FilePath &first, const FilePath &second, Qt::CaseSensitivity cs)
+{
+ if (first.m_hash != 0 && second.m_hash != 0 && first.m_hash != second.m_hash)
+ return false;
+
+ return first.pathView().compare(second.pathView(), cs) == 0
+ && first.host() == second.host()
+ && first.scheme() == second.scheme();
+}
+
+/*!
+ * Returns true if the two file paths compare equal case-sensitively.
+ * This is relevant on semi-case sensitive systems like Windows with NTFS.
+ * @see QTCREATORBUG-30846
+ */
+bool FilePath::equalsCaseSensitive(const FilePath &other) const
+{
+ return equals(*this, other, Qt::CaseSensitive);
+}
+
/*!
Returns a QString for passing on to QString based APIs.
@@ -1228,6 +1250,9 @@ FilePathInfo FilePath::filePathInfo() const
*/
bool FilePath::exists() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1240,6 +1265,9 @@ bool FilePath::exists() const
*/
bool FilePath::isExecutableFile() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1252,6 +1280,9 @@ bool FilePath::isExecutableFile() const
*/
bool FilePath::isWritableDir() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1264,6 +1295,9 @@ bool FilePath::isWritableDir() const
*/
bool FilePath::isWritableFile() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1271,9 +1305,11 @@ bool FilePath::isWritableFile() const
return (*access)->isWritableFile(*this);
}
-
bool FilePath::isReadableFile() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1283,6 +1319,9 @@ bool FilePath::isReadableFile() const
bool FilePath::isReadableDir() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1292,6 +1331,9 @@ bool FilePath::isReadableDir() const
bool FilePath::isFile() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1301,6 +1343,9 @@ bool FilePath::isFile() const
bool FilePath::isDir() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1310,6 +1355,9 @@ bool FilePath::isDir() const
bool FilePath::isSymLink() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1365,7 +1413,7 @@ FilePath FilePath::fromUtf8(const char *filename, int filenameSize)
FilePath FilePath::fromSettings(const QVariant &variant)
{
- if (variant.type() == QVariant::Url) {
+ if (variant.typeId() == QMetaType::QUrl) {
const QUrl url = variant.toUrl();
return FilePath::fromParts(url.scheme(), url.host(), url.path());
}
@@ -1497,21 +1545,28 @@ FilePath FilePath::relativePathFrom(const FilePath &anchor) const
FilePath absPath;
QString filename;
- if (isFile()) {
+
+ const QList<FilePathInfo> infos
+ = QtConcurrent::blockingMapped(QList<FilePath>{*this, anchor}, [](const FilePath &path) {
+ return path.filePathInfo();
+ });
+
+ if (infos.first().fileFlags.testFlag(FilePathInfo::FileFlag::FileType)) {
absPath = absolutePath();
filename = fileName();
- } else if (isDir()) {
+ } else if (infos.first().fileFlags.testFlag(FilePathInfo::FileFlag::DirectoryType)) {
absPath = absoluteFilePath();
} else {
return {};
}
FilePath absoluteAnchorPath;
- if (anchor.isFile())
+ if (infos.last().fileFlags.testFlag(FilePathInfo::FileFlag::FileType))
absoluteAnchorPath = anchor.absolutePath();
- else if (anchor.isDir())
+ else if (infos.last().fileFlags.testFlag(FilePathInfo::FileFlag::DirectoryType))
absoluteAnchorPath = anchor.absoluteFilePath();
else
return {};
+
QString relativeFilePath = calcRelativePath(absPath.pathView(), absoluteAnchorPath.pathView());
if (!filename.isEmpty()) {
if (relativeFilePath == ".")
@@ -2319,12 +2374,7 @@ DeviceFileHooks &DeviceFileHooks::instance()
QTCREATOR_UTILS_EXPORT bool operator==(const FilePath &first, const FilePath &second)
{
- if (first.m_hash != 0 && second.m_hash != 0 && first.m_hash != second.m_hash)
- return false;
-
- return first.pathView().compare(second.pathView(), first.caseSensitivity()) == 0
- && first.host() == second.host()
- && first.scheme() == second.scheme();
+ return FilePath::equals(first, second, first.caseSensitivity());
}
QTCREATOR_UTILS_EXPORT bool operator!=(const FilePath &first, const FilePath &second)
diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h
index cb975248ec..e742bf1a0d 100644
--- a/src/libs/utils/filepath.h
+++ b/src/libs/utils/filepath.h
@@ -268,6 +268,8 @@ public:
// FIXME: Avoid. See toSettings, toVariant, toUserOutput, toFSPathString, path, nativePath.
QString toString() const;
+ bool equalsCaseSensitive(const FilePath &other) const;
+
private:
// These are needed.
QTCREATOR_UTILS_EXPORT friend bool operator==(const FilePath &first, const FilePath &second);
@@ -282,6 +284,8 @@ private:
QTCREATOR_UTILS_EXPORT friend QDebug operator<<(QDebug dbg, const FilePath &c);
+ static bool equals(const FilePath &first, const FilePath &second, Qt::CaseSensitivity cs);
+
// Implementation details. May change.
friend class ::tst_fileutils;
void setPath(QStringView path);
diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp
index a2998e2f07..d82353e246 100644
--- a/src/libs/utils/fileutils.cpp
+++ b/src/libs/utils/fileutils.cpp
@@ -5,7 +5,6 @@
#include "savefile.h"
#include "algorithm.h"
-#include "devicefileaccess.h"
#include "environment.h"
#include "qtcassert.h"
#include "utilstr.h"
@@ -24,6 +23,8 @@
#include <qplatformdefs.h>
#ifdef QT_GUI_LIB
+#include "guiutils.h"
+
#include <QMessageBox>
#include <QGuiApplication>
#endif
@@ -197,7 +198,8 @@ FileSaver::FileSaver(const FilePath &filePath, QIODevice::OpenMode mode)
m_file.reset(tf);
} else {
const bool readOnlyOrAppend = mode & (QIODevice::ReadOnly | QIODevice::Append);
- m_isSafe = !readOnlyOrAppend && !filePath.hasHardLinks();
+ m_isSafe = !readOnlyOrAppend && !filePath.hasHardLinks()
+ && !qtcEnvironmentVariableIsSet("QTC_DISABLE_ATOMICSAVE");
if (m_isSafe)
m_file.reset(new SaveFile(filePath));
else
@@ -413,18 +415,6 @@ void withNtfsPermissions(const std::function<void()> &task)
#ifdef QT_WIDGETS_LIB
-static std::function<QWidget *()> s_dialogParentGetter;
-
-void FileUtils::setDialogParentGetter(const std::function<QWidget *()> &getter)
-{
- s_dialogParentGetter = getter;
-}
-
-static QWidget *dialogParent(QWidget *parent)
-{
- return parent ? parent : s_dialogParentGetter ? s_dialogParentGetter() : nullptr;
-}
-
static QUrl filePathToQUrl(const FilePath &filePath)
{
return QUrl::fromLocalFile(filePath.toFSPathString());
diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h
index b20f7568db..bdf7a7e3ba 100644
--- a/src/libs/utils/fileutils.h
+++ b/src/libs/utils/fileutils.h
@@ -89,8 +89,6 @@ public:
static FilePaths usefulExtraSearchPaths();
#ifdef QT_WIDGETS_LIB
- static void setDialogParentGetter(const std::function<QWidget *()> &getter);
-
static bool hasNativeFileDialog();
static FilePath getOpenFilePath(QWidget *parent,
@@ -236,7 +234,5 @@ private:
QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FilePath &fn);
-bool isRelativePathHelper(const QString &path, OsType osType);
-
} // namespace Utils
diff --git a/src/libs/utils/fsengine/fsengine_impl.h b/src/libs/utils/fsengine/fsengine_impl.h
index 9d11b917ac..1a49f77cd2 100644
--- a/src/libs/utils/fsengine/fsengine_impl.h
+++ b/src/libs/utils/fsengine/fsengine_impl.h
@@ -48,7 +48,8 @@ public:
uint ownerId(FileOwner) const override;
QString owner(FileOwner) const override;
-#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
+ // The FileTime change in QAbstractFileEngine, in qtbase, is in since Qt 6.7.1
+#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 1)
using FileTime = QFile::FileTime;
#endif
bool setFileTime(const QDateTime &newDate, FileTime time) override;
diff --git a/src/libs/utils/fsengine/fsenginehandler.cpp b/src/libs/utils/fsengine/fsenginehandler.cpp
index 29fe759a86..d6751d8b6a 100644
--- a/src/libs/utils/fsengine/fsenginehandler.cpp
+++ b/src/libs/utils/fsengine/fsenginehandler.cpp
@@ -3,17 +3,42 @@
#include "fsenginehandler.h"
+#include "fileiteratordevicesappender.h"
#include "fixedlistfsengine.h"
-#include "fsengine_impl.h"
-#include "rootinjectfsengine.h"
-
#include "fsengine.h"
+#include "fsengine_impl.h"
#include "../algorithm.h"
#include "../hostosinfo.h"
+#include <QtCore/private/qfsfileengine_p.h>
+
namespace Utils::Internal {
+class RootInjectFSEngine final : public QFSFileEngine
+{
+public:
+ using QFSFileEngine::QFSFileEngine;
+
+public:
+#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
+ IteratorUniquePtr beginEntryList(const QString &path,
+ QDir::Filters filters,
+ const QStringList &filterNames) override
+ {
+ return std::make_unique<FileIteratorWrapper>(
+ QFSFileEngine::beginEntryList(path, filters, filterNames));
+ }
+#else
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
+ {
+ std::unique_ptr<QAbstractFileEngineIterator> baseIterator(
+ QFSFileEngine::beginEntryList(filters, filterNames));
+ return new FileIteratorWrapper(std::move(baseIterator));
+ }
+#endif
+};
+
static FilePath removeDoubleSlash(const QString &fileName)
{
// Reduce every two or more slashes to a single slash.
diff --git a/src/libs/utils/fsengine/rootinjectfsengine.h b/src/libs/utils/fsengine/rootinjectfsengine.h
deleted file mode 100644
index 9eb6a8a832..0000000000
--- a/src/libs/utils/fsengine/rootinjectfsengine.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "fileiteratordevicesappender.h"
-
-#include <QtCore/private/qfsfileengine_p.h>
-
-namespace Utils {
-namespace Internal {
-
-class RootInjectFSEngine : public QFSFileEngine
-{
-public:
- using QFSFileEngine::QFSFileEngine;
-
-public:
-#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
- IteratorUniquePtr beginEntryList(const QString &path,
- QDir::Filters filters,
- const QStringList &filterNames) override
- {
- return std::make_unique<FileIteratorWrapper>(
- QFSFileEngine::beginEntryList(path, filters, filterNames));
- }
-#else
- Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
- {
- std::unique_ptr<QAbstractFileEngineIterator> baseIterator(
- QFSFileEngine::beginEntryList(filters, filterNames));
- return new FileIteratorWrapper(std::move(baseIterator));
- }
-#endif
-};
-
-} // namespace Internal
-} // namespace Utils
diff --git a/src/libs/utils/futuresynchronizer.cpp b/src/libs/utils/futuresynchronizer.cpp
index da3347f35e..912de677b9 100644
--- a/src/libs/utils/futuresynchronizer.cpp
+++ b/src/libs/utils/futuresynchronizer.cpp
@@ -3,6 +3,9 @@
#include "futuresynchronizer.h"
+#include "qtcassert.h"
+#include "threadutils.h"
+
/*!
\class Utils::FutureSynchronizer
\inmodule QtCreator
@@ -62,4 +65,18 @@ void FutureSynchronizer::flushFinishedFutures()
m_futures = newFutures;
}
+Q_GLOBAL_STATIC(FutureSynchronizer, s_futureSynchronizer);
+
+/*!
+ Returns a global FutureSynchronizer.
+ The application should cancel and wait for the tasks in this synchronizer before actually
+ unloading any libraries. This is for example done by the plugin manager in Qt Creator.
+ May only be accessed by the main thread.
+*/
+FutureSynchronizer *futureSynchronizer()
+{
+ QTC_ASSERT(isMainThread(), return nullptr);
+ return s_futureSynchronizer;
+}
+
} // namespace Utils
diff --git a/src/libs/utils/futuresynchronizer.h b/src/libs/utils/futuresynchronizer.h
index 29b1f5e456..83391dbc72 100644
--- a/src/libs/utils/futuresynchronizer.h
+++ b/src/libs/utils/futuresynchronizer.h
@@ -42,4 +42,6 @@ private:
bool m_cancelOnWait = true;
};
+QTCREATOR_UTILS_EXPORT FutureSynchronizer *futureSynchronizer();
+
} // namespace Utils
diff --git a/src/libs/utils/guiutils.cpp b/src/libs/utils/guiutils.cpp
index 17cc905300..d7d9cb5f84 100644
--- a/src/libs/utils/guiutils.cpp
+++ b/src/libs/utils/guiutils.cpp
@@ -46,4 +46,16 @@ void QTCREATOR_UTILS_EXPORT setWheelScrollingWithoutFocusBlocked(QWidget *widget
widget->setFocusPolicy(Qt::StrongFocus);
}
+static QWidget *(*s_dialogParentGetter)() = nullptr;
+
+void setDialogParentGetter(QWidget *(*getter)())
+{
+ s_dialogParentGetter = getter;
+}
+
+QWidget *dialogParent(QWidget *parent)
+{
+ return parent ? parent : s_dialogParentGetter ? s_dialogParentGetter() : nullptr;
+}
+
} // namespace Utils
diff --git a/src/libs/utils/guiutils.h b/src/libs/utils/guiutils.h
index 8316377718..fea37f98f3 100644
--- a/src/libs/utils/guiutils.h
+++ b/src/libs/utils/guiutils.h
@@ -9,6 +9,9 @@ class QWidget;
namespace Utils {
-void QTCREATOR_UTILS_EXPORT setWheelScrollingWithoutFocusBlocked(QWidget *widget);
+QTCREATOR_UTILS_EXPORT void setWheelScrollingWithoutFocusBlocked(QWidget *widget);
+
+QTCREATOR_UTILS_EXPORT QWidget *dialogParent(QWidget *parent);
+QTCREATOR_UTILS_EXPORT void setDialogParentGetter(QWidget *(*getter)());
} // namespace Utils
diff --git a/src/libs/utils/highlightingitemdelegate.cpp b/src/libs/utils/highlightingitemdelegate.cpp
index 7fe0507311..9ad7abb01a 100644
--- a/src/libs/utils/highlightingitemdelegate.cpp
+++ b/src/libs/utils/highlightingitemdelegate.cpp
@@ -91,7 +91,7 @@ int HighlightingItemDelegate::drawLineNumber(QPainter *painter, const QStyleOpti
return 0;
const bool isSelected = option.state & QStyle::State_Selected;
const QString lineText = QString::number(lineNumber);
- const int minimumLineNumberDigits = qMax(kMinimumLineNumberDigits, lineText.count());
+ const int minimumLineNumberDigits = qMax(kMinimumLineNumberDigits, lineText.size());
const int fontWidth =
painter->fontMetrics().horizontalAdvance(QString(minimumLineNumberDigits, '0'));
const int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth
diff --git a/src/libs/utils/historycompleter.cpp b/src/libs/utils/historycompleter.cpp
index 8eff3fdad8..8dc9ec3887 100644
--- a/src/libs/utils/historycompleter.cpp
+++ b/src/libs/utils/historycompleter.cpp
@@ -54,11 +54,11 @@ public:
optCopy.state |= QStyle::State_HasFocus;
QItemDelegate::paint(painter,option,index);
// add remove button
- QWindow *window = view->window()->windowHandle();
- const QPixmap iconPixmap = icon.pixmap(window, option.rect.size());
+ const qreal devicePixelRatio = painter->device()->devicePixelRatio();
+ const QPixmap iconPixmap = icon.pixmap(option.rect.size(), devicePixelRatio);
QRect pixmapRect = QStyle::alignedRect(option.direction,
Qt::AlignRight | Qt::AlignVCenter,
- iconPixmap.size() / window->devicePixelRatio(),
+ iconPixmap.size() / devicePixelRatio,
option.rect);
if (!clearIconSize.isValid())
clearIconSize = pixmapRect.size();
diff --git a/src/libs/utils/icon.cpp b/src/libs/utils/icon.cpp
index 5bd862c8f1..df880ac67e 100644
--- a/src/libs/utils/icon.cpp
+++ b/src/libs/utils/icon.cpp
@@ -42,7 +42,7 @@ static MasksAndColors masksAndColors(const QList<IconMaskAndColor> &icon, int dp
MasksAndColors result;
for (const IconMaskAndColor &i: icon) {
const QString &fileName = i.first.toString();
- const QColor color = creatorTheme()->color(i.second);
+ const QColor color = creatorColor(i.second);
const QString dprFileName = StyleHelper::availableImageResolutions(i.first.toString())
.contains(dpr)
? StyleHelper::imageFileWithResolution(fileName, dpr)
@@ -165,7 +165,7 @@ QIcon Icon::icon() const
const QPixmap combinedMask = Utils::combinedMask(masks, m_style);
m_lastIcon.addPixmap(masksToIcon(masks, combinedMask, m_style));
- const QColor disabledColor = creatorTheme()->color(Theme::IconsDisabledColor);
+ const QColor disabledColor = creatorColor(Theme::IconsDisabledColor);
m_lastIcon.addPixmap(maskToColorAndAlpha(combinedMask, disabledColor), QIcon::Disabled);
}
return m_lastIcon;
@@ -182,7 +182,7 @@ QPixmap Icon::pixmap(QIcon::Mode iconMode) const
masksAndColors(m_iconSourceList, qRound(qApp->devicePixelRatio()));
const QPixmap combinedMask = Utils::combinedMask(masks, m_style);
return iconMode == QIcon::Disabled
- ? maskToColorAndAlpha(combinedMask, creatorTheme()->color(Theme::IconsDisabledColor))
+ ? maskToColorAndAlpha(combinedMask, creatorColor(Theme::IconsDisabledColor))
: masksToIcon(masks, combinedMask, m_style);
}
}
@@ -221,11 +221,11 @@ QIcon Icon::modeIcon(const Icon &classic, const Icon &flat, const Icon &flatActi
QIcon Icon::combinedIcon(const QList<QIcon> &icons)
{
QIcon result;
- QWindow *window = QApplication::allWidgets().constFirst()->windowHandle();
+ const qreal devicePixelRatio = QApplication::allWidgets().constFirst()->devicePixelRatio();
for (const QIcon &icon: icons)
for (const QIcon::Mode mode: {QIcon::Disabled, QIcon::Normal})
for (const QSize &size: icon.availableSizes(mode))
- result.addPixmap(icon.pixmap(window, size, mode), mode);
+ result.addPixmap(icon.pixmap(size, devicePixelRatio, mode), mode);
return result;
}
diff --git a/src/libs/utils/iconbutton.cpp b/src/libs/utils/iconbutton.cpp
index 746cf0ecb1..3be368c43b 100644
--- a/src/libs/utils/iconbutton.cpp
+++ b/src/libs/utils/iconbutton.cpp
@@ -26,7 +26,7 @@ void IconButton::paintEvent(QPaintEvent *e)
QRect r(QPoint(), size());
if (m_containsMouse && isEnabled()) {
- QColor c = creatorTheme()->color(Theme::TextColorDisabled);
+ QColor c = creatorColor(Theme::TextColorDisabled);
c.setAlphaF(c.alphaF() * .5);
StyleHelper::drawPanelBgRect(&p, r, c);
}
diff --git a/src/libs/utils/infobar.cpp b/src/libs/utils/infobar.cpp
index bd6429a762..dc5fb4878d 100644
--- a/src/libs/utils/infobar.cpp
+++ b/src/libs/utils/infobar.cpp
@@ -47,10 +47,10 @@ void InfoBarWidget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
QPainter p(this);
- p.fillRect(rect(), creatorTheme()->color(Theme::InfoBarBackground));
+ p.fillRect(rect(), creatorColor(Theme::InfoBarBackground));
const QRectF adjustedRect = QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5);
const bool topEdge = m_edge == Qt::TopEdge;
- p.setPen(creatorTheme()->color(Theme::FancyToolBarSeparatorColor));
+ p.setPen(creatorColor(Theme::FancyToolBarSeparatorColor));
p.drawLine(QLineF(topEdge ? adjustedRect.bottomLeft() : adjustedRect.topLeft(),
topEdge ? adjustedRect.bottomRight() : adjustedRect.topRight()));
}
diff --git a/src/libs/utils/infolabel.cpp b/src/libs/utils/infolabel.cpp
index dbc762d9fe..e05c093ca5 100644
--- a/src/libs/utils/infolabel.cpp
+++ b/src/libs/utils/infolabel.cpp
@@ -114,14 +114,13 @@ void InfoLabel::paintEvent(QPaintEvent *event)
if (m_filled && isEnabled()) {
p.save();
p.setOpacity(0.175);
- p.fillRect(rect(), creatorTheme()->color(fillColorForType(m_type)));
+ p.fillRect(rect(), creatorColor(fillColorForType(m_type)));
p.restore();
}
const QIcon &icon = iconForType(m_type);
- QWindow *window = this->window()->windowHandle();
const QIcon::Mode mode = !this->isEnabled() ? QIcon::Disabled : QIcon::Normal;
const QPixmap iconPx =
- icon.pixmap(window, QSize(iconSize, iconSize) * devicePixelRatio(), mode);
+ icon.pixmap(QSize(iconSize, iconSize) * devicePixelRatio(), devicePixelRatio(), mode);
p.drawPixmap(iconRect, iconPx);
ElidingLabel::paintEvent(event);
}
diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp
index 31c67e4d0f..6f33e4f5ca 100644
--- a/src/libs/utils/layoutbuilder.cpp
+++ b/src/libs/utils/layoutbuilder.cpp
@@ -3,7 +3,6 @@
#include "layoutbuilder.h"
-#include <QApplication>
#include <QDebug>
#include <QFormLayout>
#include <QGridLayout>
@@ -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>
+typename X::Implementation *access(const X *x)
{
- Q_OBJECT
+ return static_cast<typename X::Implementation *>(x->ptr);
+}
+
+template <typename X>
+void apply(X *x, std::initializer_list<typename X::I> 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,56 @@ 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::LayoutItem
+ \class Layouting::Layout
\inmodule QtCreator
- \brief The LayoutItem class represents widgets, layouts, and aggregate
- items for use in conjunction with layout builders.
+ The Layout class is a base class for more specific builder
+ classes to create QLayout derived objects.
+ */
- 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.
+/*!
+ \class Layouting::Widget
+ \inmodule QtCreator
+
+ The Widget class is a base class for more specific builder
+ classes to create QWidget derived objects.
*/
/*!
- Constructs a layout item instance representing an empty cell.
- */
+ \class Layouting::LayoutItem
+ \inmodule QtCreator
+
+ The LayoutItem class is used for intermediate results
+ while creating layouts with a concept of rows and spans, such
+ as Form and Grid.
+*/
+
LayoutItem::LayoutItem() = default;
LayoutItem::~LayoutItem() = default;
+LayoutItem::LayoutItem(QLayout *l)
+ : layout(l), empty(!l)
+{}
+
+LayoutItem::LayoutItem(QWidget *w)
+ : widget(w), empty(!w)
+{}
+
+LayoutItem::LayoutItem(const QString &t)
+ : text(t), empty(t.isEmpty())
+{}
/*!
\fn template <class T> LayoutItem(const T &t)
@@ -217,44 +256,15 @@ LayoutItem::~LayoutItem() = default;
\li \c {QWidget *}
\li \c {QLayout *}
\endlist
- */
-
-struct ResultItem
-{
- 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;
-};
+// Object
-struct Slice
+Object::Object(std::initializer_list<I> ps)
{
- 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<ResultItem> pendingItems;
-};
+ ptr = new Implementation;
+ apply(this, ps);
+}
static QWidget *widgetForItem(QLayoutItem *item)
{
@@ -262,12 +272,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 +288,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 +296,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 +305,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 +313,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,759 +322,682 @@ static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item)
}
}
-void Slice::flush()
-{
- if (pendingItems.empty())
- return;
-
- 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));
- }
-
- 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<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 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;
+/*!
+ \class Layouting::Space
+ \inmodule QtCreator
- } else if (auto boxLayout = qobject_cast<QBoxLayout *>(layout)) {
+ \brief The Space class represents some empty space in a layout.
+ */
- for (const ResultItem &item : std::as_const(pendingItems))
- addItemToBoxLayout(boxLayout, item);
+/*!
+ \class Layouting::Stretch
+ \inmodule QtCreator
- } else if (auto flowLayout = qobject_cast<FlowLayout *>(layout)) {
+ \brief The Stretch class represents some stretch in a layout.
+ */
- for (const ResultItem &item : std::as_const(pendingItems))
- addItemToFlowLayout(flowLayout, item);
- } else {
- QTC_CHECK(false);
- }
+// Layout
- pendingItems.clear();
+void Layout::span(int cols, int rows)
+{
+ QTC_ASSERT(!pendingItems.empty(), return);
+ pendingItems.back().spanCols = cols;
+ pendingItems.back().spanRows = rows;
}
-// LayoutBuilder
-
-class LayoutBuilder
+void Layout::setNoMargins()
{
- Q_DISABLE_COPY_MOVE(LayoutBuilder)
-
-public:
- LayoutBuilder();
- ~LayoutBuilder();
-
- void addItem(const LayoutItem &item);
- void addItems(const LayoutItems &items);
-
- QList<Slice> stack;
-};
+ setContentsMargins(0, 0, 0, 0);
+}
-static void addItemHelper(LayoutBuilder &builder, const LayoutItem &item)
+void Layout::setNormalMargins()
{
- 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);
+ setContentsMargins(9, 9, 9, 9);
}
-void doAddText(LayoutBuilder &builder, const QString &text)
+void Layout::setContentsMargins(int left, int top, int right, int bottom)
{
- ResultItem fi;
- fi.text = text;
- builder.stack.last().pendingItems.append(fi);
+ access(this)->setContentsMargins(left, top, right, bottom);
}
-void doAddSpace(LayoutBuilder &builder, const Space &space)
+/*!
+ 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)
{
- ResultItem fi;
- fi.space = space.space;
- builder.stack.last().pendingItems.append(fi);
+ flush();
+ widget->setLayout(access(this));
}
-void doAddStretch(LayoutBuilder &builder, const Stretch &stretch)
+/*!
+ Adds the layout item \a item as sub items.
+ */
+void Layout::addItem(I item)
{
- ResultItem fi;
- fi.stretch = stretch.stretch;
- builder.stack.last().pendingItems.append(fi);
+ item.apply(this);
}
-void doAddLayout(LayoutBuilder &builder, QLayout *layout)
+void Layout::addLayoutItem(const LayoutItem &item)
{
- builder.stack.last().pendingItems.append(ResultItem(layout));
+ if (QBoxLayout *lt = asBox())
+ addItemToBoxLayout(lt, item);
+ else if (FlowLayout *lt = asFlow())
+ addItemToFlowLayout(lt, item);
+ else
+ pendingItems.push_back(item);
}
-void doAddWidget(LayoutBuilder &builder, QWidget *widget)
+/*!
+ Adds the layout items \a items as sub items.
+ */
+void Layout::addItems(std::initializer_list<I> items)
{
- builder.stack.last().pendingItems.append(ResultItem(widget));
+ for (const I &item : items)
+ item.apply(this);
}
-
/*!
- \class Layouting::Space
- \inmodule QtCreator
+ Starts a new row containing \a items. The row can be further extended by
+ other items using \c addItem() or \c addItems().
- \brief The Space class represents some empty space in a layout.
+ \sa addItem(), addItems()
*/
-/*!
- \class Layouting::Stretch
- \inmodule QtCreator
+void Layout::addRow(std::initializer_list<I> items)
+{
+ for (const I &item : items)
+ item.apply(this);
+ flush();
+}
- \brief The Stretch class represents some stretch in a layout.
- */
+void Layout::setSpacing(int spacing)
+{
+ access(this)->setSpacing(spacing);
+}
-/*!
- \class Layouting::LayoutBuilder
- \internal
- \inmodule QtCreator
+void Layout::setColumnStretch(int column, int stretch)
+{
+ if (auto grid = qobject_cast<QGridLayout *>(access(this))) {
+ grid->setColumnStretch(column, stretch);
+ } else {
+ QTC_CHECK(false);
+ }
+}
- \brief The LayoutBuilder class provides a convenient way to fill \c QFormLayout
- and \c QGridLayouts with contents.
+void addToWidget(Widget *widget, const Layout &layout)
+{
+ layout.flush_();
+ access(widget)->setLayout(access(&layout));
+}
- Filling a layout with items happens item-by-item, row-by-row.
+void addToLayout(Layout *layout, const Widget &inner)
+{
+ layout->addLayoutItem(access(&inner));
+}
- A LayoutBuilder instance is typically used locally within a function and never stored.
+void addToLayout(Layout *layout, QWidget *inner)
+{
+ layout->addLayoutItem(inner);
+}
- \sa addItem(), addItems()
-*/
+void addToLayout(Layout *layout, QLayout *inner)
+{
+ layout->addLayoutItem(inner);
+}
+void addToLayout(Layout *layout, const Layout &inner)
+{
+ inner.flush_();
+ layout->addLayoutItem(access(&inner));
+}
-LayoutBuilder::LayoutBuilder() = default;
+void addToLayout(Layout *layout, const LayoutModifier &inner)
+{
+ inner(layout);
+}
-/*!
- \internal
- Destructs a layout builder.
- */
-LayoutBuilder::~LayoutBuilder() = default;
+void addToLayout(Layout *layout, const QString &inner)
+{
+ layout->addLayoutItem(inner);
+}
-void LayoutBuilder::addItem(const LayoutItem &item)
+void empty(Layout *layout)
{
- addItemHelper(*this, item);
+ LayoutItem item;
+ item.empty = true;
+ layout->addLayoutItem(item);
}
-void LayoutBuilder::addItems(const LayoutItems &items)
+void hr(Layout *layout)
{
- for (const LayoutItem &item : items)
- addItemHelper(*this, item);
+ layout->addLayoutItem(createHr());
}
-/*!
- Starts a new row containing \a items. The row can be further extended by
- other items using \c addItem() or \c addItems().
+void br(Layout *layout)
+{
+ layout->flush();
+}
- \sa addItem(), addItems()
- */
-void LayoutItem::addRow(const LayoutItems &items)
+void st(Layout *layout)
{
- addItem(br);
- addItems(items);
+ LayoutItem item;
+ item.stretch = 1;
+ layout->addLayoutItem(item);
}
-/*!
- Adds the layout item \a item as sub items.
- */
-void LayoutItem::addItem(const LayoutItem &item)
+void noMargin(Layout *layout)
{
- subItems.append(item);
+ layout->setNoMargins();
}
-/*!
- Adds the layout items \a items as sub items.
- */
-void LayoutItem::addItems(const LayoutItems &items)
+void normalMargin(Layout *layout)
{
- subItems.append(items);
+ layout->setNormalMargins();
}
-/*!
- Attaches the constructed layout to the provided QWidget \a w.
+QFormLayout *Layout::asForm()
+{
+ return qobject_cast<QFormLayout *>(access(this));
+}
- This operation can only be performed once per LayoutBuilder instance.
- */
+QGridLayout *Layout::asGrid()
+{
+ return qobject_cast<QGridLayout *>(access(this));
+}
-void LayoutItem::attachTo(QWidget *w) const
+QBoxLayout *Layout::asBox()
{
- LayoutBuilder builder;
+ return qobject_cast<QBoxLayout *>(access(this));
+}
- builder.stack.append(w);
- addItemHelper(builder, *this);
+FlowLayout *Layout::asFlow()
+{
+ return dynamic_cast<FlowLayout *>(access(this));
}
-QWidget *LayoutItem::emerge()
+void Layout::flush()
{
- LayoutBuilder builder;
+ if (pendingItems.empty())
+ return;
- builder.stack.append(Slice());
- addItemHelper(builder, *this);
+ 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;
+ }
- if (builder.stack.empty())
- return nullptr;
+ 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);
+ }
- QTC_ASSERT(builder.stack.last().pendingItems.size() == 1, return nullptr);
- ResultItem ri = builder.stack.last().pendingItems.takeFirst();
+ 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);
- QTC_ASSERT(ri.layout || ri.widget, return nullptr);
+ // 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);
+ }
- if (ri.layout) {
- auto w = new QWidget;
- w->setLayout(ri.layout);
- return w;
+ // 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<QLabel *>(l->widget())) {
+ if (QWidget *widget = widgetForItem(f))
+ label->setBuddy(widget);
+ }
+ }
+
+ pendingItems.clear();
+ return;
}
- return ri.widget;
+ QTC_CHECK(false); // The other layouts shouldn't use flush()
}
-static void layoutExit(LayoutBuilder &builder)
+void Layout::flush_() const
{
- 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));
+ const_cast<Layout *>(this)->flush();
}
-template<class T>
-static void layoutingWidgetExit(LayoutBuilder &builder)
+void withFormAlignment(Layout *layout)
{
- const Slice slice = builder.stack.last();
- T *w = qobject_cast<T *>(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);
- }
- }
- builder.stack.pop_back();
- builder.stack.last().pendingItems.append(ResultItem(w));
+ layout->useFormAlignment = true;
}
-static void widgetExit(LayoutBuilder &builder)
+// Flow
+
+Flow::Flow(std::initializer_list<I> ps)
{
- QWidget *widget = builder.stack.last().widget;
- builder.stack.pop_back();
- builder.stack.last().pendingItems.append(ResultItem(widget));
+ ptr = new FlowLayout;
+ apply(this, ps);
+ flush();
}
-Column::Column(std::initializer_list<LayoutItem> items)
+// Row & Column
+
+Row::Row(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QVBoxLayout); };
- onExit = layoutExit;
+ ptr = new QHBoxLayout;
+ apply(this, ps);
+ flush();
}
-Row::Row(std::initializer_list<LayoutItem> items)
+Column::Column(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QHBoxLayout); };
- onExit = layoutExit;
+ ptr = new QVBoxLayout;
+ apply(this, ps);
+ flush();
}
-Flow::Flow(std::initializer_list<LayoutItem> items)
+// Grid
+
+Grid::Grid()
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new FlowLayout); };
- onExit = layoutExit;
+ ptr = new QGridLayout;
}
-Grid::Grid(std::initializer_list<LayoutItem> items)
+Grid::Grid(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QGridLayout); };
- onExit = layoutExit;
+ ptr = new QGridLayout;
+ apply(this, ps);
+ flush();
}
-static QFormLayout *newFormLayout()
+// Form
+
+Form::Form()
{
- auto formLayout = new QFormLayout;
- formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
- return formLayout;
+ ptr = new QFormLayout;
}
-Form::Form(std::initializer_list<LayoutItem> items)
+Form::Form(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(newFormLayout()); };
- onExit = layoutExit;
+ auto lt = new QFormLayout;
+ ptr = lt;
+ lt->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
+ apply(this, ps);
+ flush();
}
-LayoutItem br()
+void Layout::setFieldGrowthPolicy(int policy)
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- builder.stack.last().flush();
- };
- return item;
+ if (auto lt = asForm())
+ lt->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy(policy));
}
-LayoutItem empty()
+QWidget *Layout::emerge() const
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- ResultItem ri;
- ri.empty = true;
- builder.stack.last().pendingItems.append(ri);
- };
- return item;
+ const_cast<Layout *>(this)->flush();
+ QWidget *widget = new QWidget;
+ widget->setLayout(access(this));
+ return widget;
}
-LayoutItem hr()
+void Layout::show() const
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) { doAddWidget(builder, createHr()); };
- return item;
+ return emerge()->show();
}
-LayoutItem st()
+// "Widgets"
+
+Widget::Widget(std::initializer_list<I> ps)
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) { doAddStretch(builder, Stretch(1)); };
- return item;
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem noMargin()
+void Widget::setSize(int w, int h)
{
- return customMargin({});
+ access(this)->resize(w, h);
}
-LayoutItem normalMargin()
+void Widget::setLayout(const Layout &layout)
{
- return customMargin({9, 9, 9, 9});
+ access(this)->setLayout(access(&layout));
}
-LayoutItem customMargin(const QMargins &margin)
+void Widget::setWindowTitle(const QString &title)
{
- 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;
+ access(this)->setWindowTitle(title);
}
-LayoutItem withFormAlignment()
+void Widget::setToolTip(const QString &title)
{
- 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;
- }
- }
- };
- return item;
+ access(this)->setToolTip(title);
}
-// "Widgets"
+void Widget::show()
+{
+ access(this)->show();
+}
-template <class T>
-void setupWidget(LayoutItem *item)
+void Widget::setNoMargins(int)
{
- item->onAdd = [](LayoutBuilder &builder) { builder.stack.append(new T); };
- item->onExit = widgetExit;
-};
+ setContentsMargins(0, 0, 0, 0);
+}
-Widget::Widget(std::initializer_list<LayoutItem> items)
+void Widget::setNormalMargins(int)
{
- this->subItems = items;
- setupWidget<QWidget>(this);
+ setContentsMargins(9, 9, 9, 9);
}
-Group::Group(std::initializer_list<LayoutItem> items)
+void Widget::setContentsMargins(int left, int top, int right, int bottom)
{
- this->subItems = items;
- setupWidget<QGroupBox>(this);
+ access(this)->setContentsMargins(left, top, right, bottom);
}
-Stack::Stack(std::initializer_list<LayoutItem> items)
+QWidget *Widget::emerge() const
{
- // 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<QStackedWidget>;
+ return access(this);
}
-PushButton::PushButton(std::initializer_list<LayoutItem> items)
+// Label
+
+Label::Label(std::initializer_list<I> ps)
{
- this->subItems = items;
- setupWidget<QPushButton>(this);
+ ptr = new Implementation;
+ apply(this, ps);
}
-SpinBox::SpinBox(std::initializer_list<LayoutItem> items)
+Label::Label(const QString &text)
{
- this->subItems = items;
- setupWidget<QSpinBox>(this);
+ ptr = new Implementation;
+ setText(text);
}
-TextEdit::TextEdit(std::initializer_list<LayoutItem> items)
+void Label::setText(const QString &text)
{
- this->subItems = items;
- setupWidget<QTextEdit>(this);
+ access(this)->setText(text);
}
-Splitter::Splitter(std::initializer_list<LayoutItem> items)
+void Label::setTextFormat(Qt::TextFormat format)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- auto splitter = new QSplitter;
- splitter->setOrientation(Qt::Vertical);
- builder.stack.append(Slice(splitter, true));
- };
- onExit = layoutingWidgetExit<QSplitter>;
+ access(this)->setTextFormat(format);
}
-ToolBar::ToolBar(std::initializer_list<LayoutItem> items)
+void Label::setWordWrap(bool on)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- auto toolbar = new QToolBar;
- toolbar->setOrientation(Qt::Horizontal);
- builder.stack.append(Slice(toolbar, true));
- };
- onExit = layoutingWidgetExit<QToolBar>;
+ access(this)->setWordWrap(on);
}
-TabWidget::TabWidget(std::initializer_list<LayoutItem> items)
+void Label::setTextInteractionFlags(Qt::TextInteractionFlags flags)
{
- this->subItems = items;
- setupWidget<QTabWidget>(this);
+ access(this)->setTextInteractionFlags(flags);
}
-// Special Tab
+void Label::setOpenExternalLinks(bool on)
+{
+ access(this)->setOpenExternalLinks(on);
+}
-Tab::Tab(const QString &tabName, const LayoutItem &item)
+void Label::onLinkHovered(const std::function<void (const QString &)> &func, QObject *guard)
{
- 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);
- };
+ QObject::connect(access(this), &QLabel::linkHovered, guard, func);
}
-// Special If
+// Group
-If::If(bool condition, const LayoutItems &items, const LayoutItems &other)
+Group::Group(std::initializer_list<I> ps)
{
- subItems.append(condition ? items : other);
+ ptr = new Implementation;
+ apply(this, ps);
}
-// Special Application
-
-Application::Application(std::initializer_list<LayoutItem> items)
+void Group::setTitle(const QString &title)
{
- subItems = items;
- setupWidget<QWidget>(this);
- onExit = {}; // Hack: Don't dropp the last slice, we need the resulting widget.
+ access(this)->setTitle(title);
+ access(this)->setObjectName(title);
}
-int Application::exec(int &argc, char *argv[])
+void Group::setGroupChecker(const std::function<void (QObject *)> &checker)
{
- QApplication app(argc, argv);
- LayoutBuilder builder;
- addItemHelper(builder, *this);
- if (QWidget *widget = builder.stack.last().widget)
- widget->show();
- return app.exec();
+ checker(access(this));
}
-// "Properties"
+// SpinBox
-LayoutItem title(const QString &title)
+SpinBox::SpinBox(std::initializer_list<I> ps)
{
- return [title](QObject *target) {
- if (auto groupBox = qobject_cast<QGroupBox *>(target)) {
- groupBox->setTitle(title);
- groupBox->setObjectName(title);
- } else if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setWindowTitle(title);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem windowTitle(const QString &windowTitle)
+void SpinBox::setValue(int val)
{
- return [windowTitle](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setWindowTitle(windowTitle);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(this)->setValue(val);
}
-LayoutItem text(const QString &text)
+void SpinBox::onTextChanged(const std::function<void (QString)> &func)
{
- return [text](QObject *target) {
- if (auto button = qobject_cast<QAbstractButton *>(target)) {
- button->setText(text);
- } else if (auto textEdit = qobject_cast<QTextEdit *>(target)) {
- textEdit->setText(text);
- } else {
- QTC_CHECK(false);
- }
- };
+ QObject::connect(access(this), &QSpinBox::textChanged, func);
}
-LayoutItem tooltip(const QString &toolTip)
+// TextEdit
+
+TextEdit::TextEdit(std::initializer_list<I> ps)
{
- return [toolTip](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setToolTip(toolTip);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem spacing(int spacing)
+void TextEdit::setText(const QString &text)
{
- return [spacing](QObject *target) {
- if (auto layout = qobject_cast<QLayout *>(target)) {
- layout->setSpacing(spacing);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(this)->setText(text);
}
-LayoutItem resize(int w, int h)
+// PushButton
+
+PushButton::PushButton(std::initializer_list<I> ps)
{
- return [w, h](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->resize(w, h);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem columnStretch(int column, int stretch)
+void PushButton::setText(const QString &text)
{
- return [column, stretch](QObject *target) {
- if (auto grid = qobject_cast<QGridLayout *>(target)) {
- grid->setColumnStretch(column, stretch);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(this)->setText(text);
}
-LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy)
+void PushButton::onClicked(const std::function<void ()> &func, QObject *guard)
{
- return [policy](QObject *target) {
- if (auto form = qobject_cast<QFormLayout *>(target)) {
- form->setFieldGrowthPolicy(policy);
- } else {
- QTC_CHECK(false);
- }
- };
+ QObject::connect(access(this), &QAbstractButton::clicked, guard, func);
}
+// Stack
-// Id based setters
-
-LayoutItem id(ID &out)
+// 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<I> ps)
{
- return [&out](QObject *target) { out.ob = target; };
+ ptr = new Implementation;
+ apply(this, ps);
}
-void setText(ID id, const QString &text)
+void addToStack(Stack *stack, const Widget &inner)
{
- if (auto textEdit = qobject_cast<QTextEdit *>(id.ob))
- textEdit->setText(text);
+ access(stack)->addWidget(inner.emerge());
}
-// Signals
+void addToStack(Stack *stack, const Layout &inner)
+{
+ inner.flush_();
+ access(stack)->addWidget(inner.emerge());
+}
-LayoutItem onClicked(const std::function<void ()> &func, QObject *guard)
+void addToStack(Stack *stack, QWidget *inner)
{
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QAbstractButton *>(target)) {
- QObject::connect(button, &QAbstractButton::clicked, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(stack)->addWidget(inner);
}
-LayoutItem onTextChanged(const std::function<void (const QString &)> &func, QObject *guard)
+// Splitter
+
+Splitter::Splitter(std::initializer_list<I> ps)
{
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QSpinBox *>(target)) {
- QObject::connect(button, &QSpinBox::textChanged, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ access(this)->setOrientation(Qt::Vertical);
+ apply(this, ps);
}
-LayoutItem onValueChanged(const std::function<void (int)> &func, QObject *guard)
+void addToSplitter(Splitter *splitter, QWidget *inner)
{
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QSpinBox *>(target)) {
- QObject::connect(button, &QSpinBox::valueChanged, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(splitter)->addWidget(inner);
}
-// Convenience
+void addToSplitter(Splitter *splitter, const Widget &inner)
+{
+ access(splitter)->addWidget(inner.emerge());
+}
-QWidget *createHr(QWidget *parent)
+void addToSplitter(Splitter *splitter, const Layout &inner)
{
- auto frame = new QFrame(parent);
- frame->setFrameShape(QFrame::HLine);
- frame->setFrameShadow(QFrame::Sunken);
- return frame;
+ inner.flush_();
+ access(splitter)->addWidget(inner.emerge());
}
-// Singletons.
+// ToolBar
-LayoutItem::LayoutItem(const LayoutItem &t)
+ToolBar::ToolBar(std::initializer_list<I> ps)
{
- operator=(t);
+ ptr = new Implementation;
+ apply(this, ps);
+ access(this)->setOrientation(Qt::Horizontal);
}
-void createItem(LayoutItem *item, LayoutItem(*t)())
+// TabWidget
+
+TabWidget::TabWidget(std::initializer_list<I> ps)
{
- *item = t();
+ ptr = new Implementation;
+ apply(this, ps);
}
-void createItem(LayoutItem *item, const std::function<void(QObject *target)> &t)
+Tab::Tab(const QString &tabName, const Layout &inner)
+ : tabName(tabName), inner(inner)
+{}
+
+void addToTabWidget(TabWidget *tabWidget, const Tab &tab)
{
- item->setter = t;
+ access(tabWidget)->addTab(tab.inner.emerge(), tab.tabName);
}
-void createItem(LayoutItem *item, QWidget *t)
-{
- if (auto l = qobject_cast<QLabel *>(t))
- l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse);
+// Special If
+
+If::If(bool condition,
+ const std::initializer_list<Layout::I> ifcase,
+ const std::initializer_list<Layout::I> thencase)
+ : used(condition ? ifcase : thencase)
+{}
- item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
+void addToLayout(Layout *layout, const If &inner)
+{
+ for (const Layout::I &item : inner.used)
+ item.apply(layout);
}
-void createItem(LayoutItem *item, QLayout *t)
+// Specials
+
+QWidget *createHr(QWidget *parent)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddLayout(builder, t); };
+ auto frame = new QFrame(parent);
+ frame->setFrameShape(QFrame::HLine);
+ frame->setFrameShadow(QFrame::Sunken);
+ return frame;
}
-void createItem(LayoutItem *item, const QString &t)
+Span::Span(int cols, const Layout::I &item)
+ : item(item), spanCols(cols)
+{}
+
+Span::Span(int cols, int rows, const Layout::I &item)
+ : item(item), spanCols(cols), spanRows(rows)
+{}
+
+void addToLayout(Layout *layout, const Span &inner)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddText(builder, 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, const Space &t)
+LayoutModifier spacing(int space)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddSpace(builder, t); };
+ return [space](Layout *layout) { layout->setSpacing(space); };
}
-void createItem(LayoutItem *item, const Stretch &t)
+void addToLayout(Layout *layout, const Space &inner)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddStretch(builder, t); };
+ if (auto lt = layout->asBox())
+ lt->addSpacing(inner.space);
}
-void createItem(LayoutItem *item, const Span &t)
+void addToLayout(Layout *layout, const Stretch &inner)
{
- item->onAdd = [t](LayoutBuilder &builder) {
- addItemHelper(builder, t.item);
- builder.stack.last().pendingItems.last().span = t.span;
- };
+ if (auto lt = layout->asBox())
+ lt->addStretch(inner.stretch);
}
-} // Layouting
+// void createItem(LayoutItem *item, QWidget *t)
+// {
+// if (auto l = qobject_cast<QLabel *>(t))
+// l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse);
+
+// item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
+// }
-#include "layoutbuilder.moc"
+
+} // Layouting
diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h
index c9fac7d838..a411ea4763 100644
--- a/src/libs/utils/layoutbuilder.h
+++ b/src/libs/utils/layoutbuilder.h
@@ -3,12 +3,11 @@
#pragma once
-#include <QFormLayout>
-#include <QList>
#include <QString>
-#include <QtGlobal>
-#include <optional>
+#include <functional>
+#include <initializer_list>
+#include <vector>
#if defined(UTILS_LIBRARY)
# define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT
@@ -19,251 +18,570 @@
#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 <class T> T qobject_cast(QObject *object);
QT_END_NAMESPACE
namespace Layouting {
-// LayoutItem
+class NestId {};
-class LayoutBuilder;
-class LayoutItem;
-using LayoutItems = QList<LayoutItem>;
-
-class QTCREATOR_UTILS_EXPORT LayoutItem
+template <typename Id, typename Arg>
+class IdAndArg
{
public:
- using Setter = std::function<void(QObject *target)>;
+ IdAndArg(Id, const Arg &arg) : arg(arg) {}
+ const Arg arg; // FIXME: Could be const &, but this would currently break bindTo().
+};
- LayoutItem();
- ~LayoutItem();
+template<typename T1, typename T2>
+struct Arg2
+{
+ Arg2(const T1 &a1, const T2 &a2)
+ : p1(a1)
+ , p2(a2)
+ {}
+ const T1 p1;
+ const T2 p2;
+};
+
+template<typename T1, typename T2, typename T3>
+struct Arg3
+{
+ Arg3(const T1 &a1, const T2 &a2, const T3 &a3)
+ : p1(a1)
+ , p2(a2)
+ , p3(a3)
+ {}
+ const T1 p1;
+ const T2 p2;
+ const T3 p3;
+};
+
+template<typename T1, typename T2, typename T3, typename T4>
+struct Arg4
+{
+ Arg4(const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4)
+ : p1(a1)
+ , p2(a2)
+ , p3(a3)
+ , p4(a4)
+ {}
+ const T1 p1;
+ const T2 p2;
+ const T3 p3;
+ const T4 p4;
+};
+
+// The main dispatcher
- LayoutItem(const LayoutItem &t);
- LayoutItem &operator=(const LayoutItem &t) = default;
+void doit(auto x, auto id, auto p);
- template <class T> LayoutItem(const T &t)
+template <typename X> class BuilderItem
+{
+public:
+ // Nested child object
+ template <typename Inner>
+ BuilderItem(Inner && p)
{
- if constexpr (std::is_base_of_v<LayoutItem, T>)
- LayoutItem::operator=(t);
- else
- createItem(this, t);
+ apply = [&p](X *x) { doit(x, NestId{}, std::forward<Inner>(p)); };
}
- void attachTo(QWidget *w) const;
- QWidget *emerge();
-
- void addItem(const LayoutItem &item);
- void addItems(const LayoutItems &items);
- void addRow(const LayoutItems &items);
+ // Property setter
+ template <typename Id, typename Arg>
+ BuilderItem(IdAndArg<Id, Arg> && idarg)
+ {
+ apply = [&idarg](X *x) { doit(x, Id{}, idarg.arg); };
+ }
- std::function<void(LayoutBuilder &)> onAdd;
- std::function<void(LayoutBuilder &)> onExit;
- std::function<void(QObject *target)> setter;
- LayoutItems subItems;
+ std::function<void(X *)> apply;
};
-// Special items
-class QTCREATOR_UTILS_EXPORT Space
+//////////////////////////////////////////////
+
+//
+// Basic
+//
+
+class QTCREATOR_UTILS_EXPORT Thing
{
public:
- explicit Space(int space) : space(space) {}
- const int space;
+ void *ptr; // The product.
};
-class QTCREATOR_UTILS_EXPORT Stretch
+class QTCREATOR_UTILS_EXPORT Object : public Thing
{
public:
- explicit Stretch(int stretch = 1) : stretch(stretch) {}
- const int stretch;
+ using Implementation = QObject;
+ using I = BuilderItem<Object>;
+
+ Object() = default;
+ Object(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Span
+//
+// Layouts
+//
+
+class FlowLayout;
+class Layout;
+using LayoutModifier = std::function<void(Layout *)>;
+
+class QTCREATOR_UTILS_EXPORT LayoutItem
{
public:
- Span(int span, const LayoutItem &item) : span(span), item(item) {}
- const int span;
- LayoutItem item;
+ ~LayoutItem();
+ LayoutItem();
+ LayoutItem(QLayout *l);
+ LayoutItem(QWidget *w);
+ LayoutItem(const QString &t);
+
+ QString text;
+ QLayout *layout = nullptr;
+ QWidget *widget = nullptr;
+ int stretch = -1;
+ int spanCols = 1;
+ int spanRows = 1;
+ bool empty = false;
};
-class QTCREATOR_UTILS_EXPORT Column : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Layout : public Object
{
public:
- Column(std::initializer_list<LayoutItem> items);
+ using Implementation = QLayout;
+ using I = BuilderItem<Layout>;
+
+ Layout() = default;
+ Layout(Implementation *w) { ptr = w; }
+
+ void span(int cols, int rows);
+
+ void setNoMargins();
+ void setNormalMargins();
+ void setContentsMargins(int left, int top, int right, int bottom);
+ void setColumnStretch(int cols, int rows);
+ void setSpacing(int space);
+ void setFieldGrowthPolicy(int policy);
+
+ void attachTo(QWidget *);
+
+ void addItem(I item);
+ void addItems(std::initializer_list<I> items);
+ void addRow(std::initializer_list<I> items);
+ void addLayoutItem(const LayoutItem &item);
+
+ void flush();
+ void flush_() const;
+
+ QWidget *emerge() const;
+ void show() 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<LayoutItem> pendingItems;
};
-class QTCREATOR_UTILS_EXPORT Row : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Column : public Layout
{
public:
- Row(std::initializer_list<LayoutItem> items);
+ using Implementation = QVBoxLayout;
+ using I = BuilderItem<Column>;
+
+ Column(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Flow : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Row : public Layout
{
public:
- Flow(std::initializer_list<LayoutItem> items);
+ using Implementation = QHBoxLayout;
+ using I = BuilderItem<Row>;
+
+ Row(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Grid : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Form : public Layout
{
public:
- Grid() : Grid({}) {}
- Grid(std::initializer_list<LayoutItem> items);
+ using Implementation = QFormLayout;
+ using I = BuilderItem<Form>;
+
+ Form();
+ Form(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Form : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Grid : public Layout
{
public:
- Form() : Form({}) {}
- Form(std::initializer_list<LayoutItem> items);
+ using Implementation = QGridLayout;
+ using I = BuilderItem<Grid>;
+
+ Grid();
+ Grid(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Widget : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Flow : public Layout
{
public:
- Widget(std::initializer_list<LayoutItem> items);
+ Flow(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Stack : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Stretch
{
public:
- Stack() : Stack({}) {}
- Stack(std::initializer_list<LayoutItem> items);
+ explicit Stretch(int stretch) : stretch(stretch) {}
+
+ int stretch;
};
-class QTCREATOR_UTILS_EXPORT Tab : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Space
{
public:
- Tab(const QString &tabName, const LayoutItem &item);
+ explicit Space(int space) : space(space) {}
+
+ int space;
};
-class QTCREATOR_UTILS_EXPORT If : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Span
{
public:
- If(bool condition, const LayoutItems &item, const LayoutItems &other = {});
+ 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 Group : public LayoutItem
+//
+// Widgets
+//
+
+class QTCREATOR_UTILS_EXPORT Widget : public Object
{
public:
- Group(std::initializer_list<LayoutItem> items);
+ using Implementation = QWidget;
+ using I = BuilderItem<Widget>;
+
+ Widget() = default;
+ Widget(std::initializer_list<I> ps);
+ Widget(Implementation *w) { ptr = w; }
+
+ QWidget *emerge() const;
+ void show();
+
+ void setLayout(const Layout &layout);
+ void setSize(int, int);
+ void setWindowTitle(const QString &);
+ void setToolTip(const QString &);
+ void setNoMargins(int = 0);
+ void setNormalMargins(int = 0);
+ void setContentsMargins(int left, int top, int right, int bottom);
};
-class QTCREATOR_UTILS_EXPORT TextEdit : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Label : public Widget
{
public:
- TextEdit(std::initializer_list<LayoutItem> items);
+ using Implementation = QLabel;
+ using I = BuilderItem<Label>;
+
+ Label(std::initializer_list<I> ps);
+ Label(const QString &text);
+
+ void setText(const QString &);
+ void setTextFormat(Qt::TextFormat);
+ void setWordWrap(bool);
+ void setTextInteractionFlags(Qt::TextInteractionFlags);
+ void setOpenExternalLinks(bool);
+ void onLinkHovered(const std::function<void(const QString &)> &, QObject *guard);
};
-class QTCREATOR_UTILS_EXPORT PushButton : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Group : public Widget
{
public:
- PushButton(std::initializer_list<LayoutItem> items);
+ using Implementation = QGroupBox;
+ using I = BuilderItem<Group>;
+
+ Group(std::initializer_list<I> ps);
+
+ void setTitle(const QString &);
+ void setGroupChecker(const std::function<void(QObject *)> &);
};
-class QTCREATOR_UTILS_EXPORT SpinBox : public LayoutItem
+class QTCREATOR_UTILS_EXPORT SpinBox : public Widget
{
public:
- SpinBox(std::initializer_list<LayoutItem> items);
+ using Implementation = QSpinBox;
+ using I = BuilderItem<SpinBox>;
+
+ SpinBox(std::initializer_list<I> ps);
+
+ void setValue(int);
+ void onTextChanged(const std::function<void(QString)> &);
};
-class QTCREATOR_UTILS_EXPORT Splitter : public LayoutItem
+class QTCREATOR_UTILS_EXPORT PushButton : public Widget
{
public:
- Splitter(std::initializer_list<LayoutItem> items);
+ using Implementation = QPushButton;
+ using I = BuilderItem<PushButton>;
+
+ PushButton(std::initializer_list<I> ps);
+
+ void setText(const QString &);
+ void onClicked(const std::function<void()> &, QObject *guard);
};
-class QTCREATOR_UTILS_EXPORT ToolBar : public LayoutItem
+class QTCREATOR_UTILS_EXPORT TextEdit : public Widget
{
public:
- ToolBar(std::initializer_list<LayoutItem> items);
+ using Implementation = QTextEdit;
+ using I = BuilderItem<TextEdit>;
+ using Id = Implementation *;
+
+ TextEdit(std::initializer_list<I> ps);
+
+ void setText(const QString &);
};
-class QTCREATOR_UTILS_EXPORT TabWidget : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Splitter : public Widget
{
public:
- TabWidget(std::initializer_list<LayoutItem> items);
+ using Implementation = QSplitter;
+ using I = BuilderItem<Splitter>;
+
+ Splitter(std::initializer_list<I> items);
};
-class QTCREATOR_UTILS_EXPORT Application : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Stack : public Widget
{
public:
- Application(std::initializer_list<LayoutItem> items);
+ using Implementation = QStackedWidget;
+ using I = BuilderItem<Stack>;
- int exec(int &argc, char *argv[]);
+ Stack() : Stack({}) {}
+ Stack(std::initializer_list<I> items);
};
+class QTCREATOR_UTILS_EXPORT Tab : public Widget
+{
+public:
+ using Implementation = QWidget;
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const std::function<void(QObject *target)> &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QWidget *t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QLayout *t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, LayoutItem(*t)());
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const QString &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Span &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Space &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Stretch &t);
-
+ Tab(const QString &tabName, const Layout &inner);
-// "Singletons"
+ const QString tabName;
+ const Layout inner;
+};
-QTCREATOR_UTILS_EXPORT LayoutItem br();
-QTCREATOR_UTILS_EXPORT LayoutItem st();
-QTCREATOR_UTILS_EXPORT LayoutItem empty();
-QTCREATOR_UTILS_EXPORT LayoutItem hr();
-QTCREATOR_UTILS_EXPORT LayoutItem noMargin();
-QTCREATOR_UTILS_EXPORT LayoutItem normalMargin();
-QTCREATOR_UTILS_EXPORT LayoutItem customMargin(const QMargins &margin);
-QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment();
+class QTCREATOR_UTILS_EXPORT TabWidget : public Widget
+{
+public:
+ using Implementation = QTabWidget;
+ using I = BuilderItem<TabWidget>;
-// "Setters"
+ TabWidget(std::initializer_list<I> items);
+};
-QTCREATOR_UTILS_EXPORT LayoutItem title(const QString &title);
-QTCREATOR_UTILS_EXPORT LayoutItem text(const QString &text);
-QTCREATOR_UTILS_EXPORT LayoutItem tooltip(const QString &toolTip);
-QTCREATOR_UTILS_EXPORT LayoutItem resize(int, int);
-QTCREATOR_UTILS_EXPORT LayoutItem columnStretch(int column, int stretch);
-QTCREATOR_UTILS_EXPORT LayoutItem spacing(int);
-QTCREATOR_UTILS_EXPORT LayoutItem windowTitle(const QString &windowTitle);
-QTCREATOR_UTILS_EXPORT LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy);
+class QTCREATOR_UTILS_EXPORT ToolBar : public Widget
+{
+public:
+ using Implementation = QToolBar;
+ using I = Layouting::BuilderItem<ToolBar>;
+ ToolBar(std::initializer_list<I> items);
+};
-// "Getters"
+// Special
-class ID
+class QTCREATOR_UTILS_EXPORT If
{
public:
- QObject *ob = nullptr;
+ If(bool condition,
+ const std::initializer_list<Layout::I> ifcase,
+ const std::initializer_list<Layout::I> thencase = {});
+
+ const std::initializer_list<Layout::I> used;
};
-QTCREATOR_UTILS_EXPORT LayoutItem id(ID &out);
+//
+// Dispatchers
+//
-QTCREATOR_UTILS_EXPORT void setText(ID id, const QString &text);
+// We need one 'Id' (and a corresponding function wrapping arguments into a
+// tuple marked by this id) per 'name' of "backend" setter member function,
+// i.e. one 'text' is sufficient for QLabel::setText, QLineEdit::setText.
+// The name of the Id does not have to match the backend names as it
+// is mapped per-backend-type in the respective setter implementation
+// but we assume that it generally makes sense to stay close to the
+// wrapped API name-wise.
+// These are free functions overloaded on the type of builder object
+// and setter id. The function implementations are independent, but
+// the base expectation is that they will forwards to the backend
+// type's setter.
-// "Signals"
+// Special dispatchers
-QTCREATOR_UTILS_EXPORT LayoutItem onClicked(const std::function<void()> &,
- QObject *guard = nullptr);
-QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(const std::function<void(const QString &)> &,
- QObject *guard = nullptr);
-QTCREATOR_UTILS_EXPORT LayoutItem onValueChanged(const std::function<void(int)> &,
- QObject *guard = nullptr);
-QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(ID &id, QVariant(*sig)(QObject *));
+class BindToId {};
-// Convenience
+template <typename T>
+auto bindTo(T **p)
+{
+ return IdAndArg{BindToId{}, p};
+}
-QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
+template <typename Interface>
+void doit(Interface *x, BindToId, auto p)
+{
+ *p = static_cast<typename Interface::Implementation *>(x->ptr);
+}
+
+class IdId {};
+auto id(auto p) { return IdAndArg{IdId{}, p}; }
+
+template <typename Interface>
+void doit(Interface *x, IdId, auto p)
+{
+ **p = static_cast<typename Interface::Implementation *>(x->ptr);
+}
+
+// Setter dispatchers
+
+#define QTCREATOR_SETTER(name, setter) \
+ class name##_TAG {}; \
+ inline auto name(auto p) { return IdAndArg{name##_TAG{}, p}; } \
+ inline void doit(auto x, name##_TAG, auto p) { x->setter(p); }
+
+#define QTCREATOR_SETTER2(name, setter) \
+ class name##_TAG {}; \
+ inline auto name(auto p1, auto p2) { return IdAndArg{name##_TAG{}, Arg2{p1, p2}}; } \
+ inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2); }
+
+#define QTCREATOR_SETTER3(name, setter) \
+ class name##_TAG {}; \
+ inline auto name(auto p1, auto p2, auto p3) { return IdAndArg{name##_TAG{}, Arg3{p1, p2, p3}}; } \
+ inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2, p.p3); }
+
+#define QTCREATOR_SETTER4(name, setter) \
+ class name##_TAG {}; \
+ inline auto name(auto p1, auto p2, auto p3, auto p4) { return IdAndArg{name##_TAG{}, Arg4{p1, p2, p3, p4}}; } \
+ inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2, p.p3, p.p4); }
+
+QTCREATOR_SETTER(fieldGrowthPolicy, setFieldGrowthPolicy);
+QTCREATOR_SETTER(groupChecker, setGroupChecker);
+QTCREATOR_SETTER(openExternalLinks, setOpenExternalLinks);
+QTCREATOR_SETTER2(size, setSize)
+QTCREATOR_SETTER(text, setText)
+QTCREATOR_SETTER(textFormat, setTextFormat);
+QTCREATOR_SETTER(textInteractionFlags, setTextInteractionFlags);
+QTCREATOR_SETTER(title, setTitle)
+QTCREATOR_SETTER(toolTip, setToolTip);
+QTCREATOR_SETTER(windowTitle, setWindowTitle);
+QTCREATOR_SETTER(wordWrap, setWordWrap);
+QTCREATOR_SETTER2(columnStretch, setColumnStretch);
+QTCREATOR_SETTER2(onClicked, onClicked);
+QTCREATOR_SETTER2(onLinkHovered, onLinkHovered);
+QTCREATOR_SETTER2(onTextChanged, onTextChanged);
+QTCREATOR_SETTER4(customMargins, setContentsMargins);
+
+// Nesting dispatchers
+
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Layout &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QLayout *inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const LayoutModifier &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const QString &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Space &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Stretch &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const If &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Span &inner);
+// ... can be added to anywhere later to support "user types"
+
+QTCREATOR_UTILS_EXPORT void addToWidget(Widget *widget, const Layout &layout);
+
+QTCREATOR_UTILS_EXPORT void addToTabWidget(TabWidget *tabWidget, const Tab &inner);
+
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Layout &inner);
+
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Layout &inner);
+
+template <class Inner>
+void doit_nested(Layout *outer, Inner && inner)
+{
+ addToLayout(outer, std::forward<Inner>(inner));
+}
+
+void doit_nested(Widget *outer, auto inner)
+{
+ addToWidget(outer, inner);
+}
+
+void doit_nested(TabWidget *outer, auto inner)
+{
+ addToTabWidget(outer, inner);
+}
+
+void doit_nested(Stack *outer, auto inner)
+{
+ addToStack(outer, inner);
+}
+
+void doit_nested(Splitter *outer, auto inner)
+{
+ addToSplitter(outer, inner);
+}
-template <class T>
-LayoutItem bindTo(T **out)
+template <class Inner>
+void doit(auto outer, NestId, Inner && inner)
{
- return [out](QObject *target) { *out = qobject_cast<T *>(target); };
+ doit_nested(outer, std::forward<Inner>(inner));
}
+// Special layout items
+
+QTCREATOR_UTILS_EXPORT void empty(Layout *);
+QTCREATOR_UTILS_EXPORT void br(Layout *);
+QTCREATOR_UTILS_EXPORT void st(Layout *);
+QTCREATOR_UTILS_EXPORT void noMargin(Layout *);
+QTCREATOR_UTILS_EXPORT void normalMargin(Layout *);
+QTCREATOR_UTILS_EXPORT void withFormAlignment(Layout *);
+QTCREATOR_UTILS_EXPORT void hr(Layout *);
+
+QTCREATOR_UTILS_EXPORT LayoutModifier spacing(int space);
+
+// Convenience
+
+QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
} // Layouting
diff --git a/src/libs/utils/lua.cpp b/src/libs/utils/lua.cpp
new file mode 100644
index 0000000000..4f12c626f1
--- /dev/null
+++ b/src/libs/utils/lua.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "lua.h"
+
+#include "utilstr.h"
+
+namespace Utils {
+
+static LuaInterface *s_luaInterface = nullptr;
+
+void setLuaInterface(LuaInterface *luaInterface)
+{
+ s_luaInterface = luaInterface;
+}
+
+LuaInterface *luaInterface()
+{
+ return s_luaInterface;
+}
+
+expected_str<std::unique_ptr<LuaState>> runScript(const QString &script, const QString &name)
+{
+ if (!s_luaInterface)
+ return make_unexpected(Tr::tr("No Lua interface set"));
+
+ return s_luaInterface->runScript(script, name);
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/lua.h b/src/libs/utils/lua.h
new file mode 100644
index 0000000000..7a3b8f7833
--- /dev/null
+++ b/src/libs/utils/lua.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QObject>
+
+#include "expected.h"
+#include "utils_global.h"
+
+namespace Utils {
+
+class LuaState
+{
+public:
+ virtual ~LuaState() = default;
+};
+
+class QTCREATOR_UTILS_EXPORT LuaInterface
+{
+public:
+ virtual ~LuaInterface() = default;
+ virtual expected_str<std::unique_ptr<LuaState>> runScript(
+ const QString &script, const QString &name)
+ = 0;
+};
+
+QTCREATOR_UTILS_EXPORT void setLuaInterface(LuaInterface *luaInterface);
+QTCREATOR_UTILS_EXPORT LuaInterface *luaInterface();
+
+QTCREATOR_UTILS_EXPORT expected_str<std::unique_ptr<LuaState>> runScript(
+ const QString &script, const QString &name);
+
+} // namespace Utils
diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp
index dd57cd6b5a..fc13897474 100644
--- a/src/libs/utils/macroexpander.cpp
+++ b/src/libs/utils/macroexpander.cpp
@@ -284,7 +284,7 @@ QByteArray MacroExpander::expand(const QByteArray &stringWithVariables) const
QVariant MacroExpander::expandVariant(const QVariant &v) const
{
- const auto type = QMetaType::Type(v.type());
+ const int type = v.typeId();
if (type == QMetaType::QString) {
return expand(v.toString());
} else if (type == QMetaType::QStringList) {
diff --git a/src/libs/utils/macroexpander.h b/src/libs/utils/macroexpander.h
index adde4db1d3..fe98d46872 100644
--- a/src/libs/utils/macroexpander.h
+++ b/src/libs/utils/macroexpander.h
@@ -5,7 +5,6 @@
#include "utils_global.h"
-#include <QCoreApplication>
#include <QList>
#include <functional>
diff --git a/src/libs/utils/namevalueitem.cpp b/src/libs/utils/namevalueitem.cpp
index 3f4cdbd4b2..cb34f8a550 100644
--- a/src/libs/utils/namevalueitem.cpp
+++ b/src/libs/utils/namevalueitem.cpp
@@ -19,6 +19,10 @@ EnvironmentItems EnvironmentItem::fromStringList(const QStringList &list)
{
EnvironmentItems result;
for (const QString &string : list) {
+ if (string.startsWith("##")) {
+ result.append({string.mid(2), {}, EnvironmentItem::Comment});
+ continue;
+ }
int pos = string.indexOf("+=");
if (pos != -1) {
result.append({string.left(pos), string.mid(pos + 2), EnvironmentItem::Append});
@@ -59,6 +63,8 @@ QStringList EnvironmentItem::toStringList(const EnvironmentItems &list)
return QString('#' + item.name + '=' + item.value);
case EnvironmentItem::SetEnabled:
return QString(item.name + '=' + item.value);
+ case EnvironmentItem::Comment:
+ return QString("##" + item.name);
}
return QString();
});
@@ -170,6 +176,8 @@ void EnvironmentItem::apply(NameValueDictionary *dictionary, Operation op) const
apply(dictionary, SetEnabled);
}
} break;
+ case Comment: // ignore comments when applying to environment
+ break;
}
}
@@ -195,6 +203,9 @@ QDebug operator<<(QDebug debug, const EnvironmentItem &i)
case EnvironmentItem::Append:
debug << "append to \"" << i.name << "\":\"" << i.value << '"';
break;
+ case EnvironmentItem::Comment:
+ debug << "comment:" << i.name;
+ break;
}
debug << ')';
return debug;
diff --git a/src/libs/utils/namevalueitem.h b/src/libs/utils/namevalueitem.h
index 6ad5ed0379..c518b17e66 100644
--- a/src/libs/utils/namevalueitem.h
+++ b/src/libs/utils/namevalueitem.h
@@ -16,7 +16,7 @@ namespace Utils {
class QTCREATOR_UTILS_EXPORT EnvironmentItem
{
public:
- enum Operation : char { SetEnabled, Unset, Prepend, Append, SetDisabled };
+ enum Operation : char { SetEnabled, Unset, Prepend, Append, SetDisabled, Comment };
EnvironmentItem() = default;
EnvironmentItem(const QString &key, const QString &value, Operation operation = SetEnabled)
: name(key)
diff --git a/src/libs/utils/namevaluesdialog.cpp b/src/libs/utils/namevaluesdialog.cpp
index 35587f8791..5a45c54a75 100644
--- a/src/libs/utils/namevaluesdialog.cpp
+++ b/src/libs/utils/namevaluesdialog.cpp
@@ -45,7 +45,11 @@ signals:
void lostFocus();
private:
- void focusOutEvent(QFocusEvent *) override { emit lostFocus(); }
+ void focusOutEvent(QFocusEvent *e) override
+ {
+ QPlainTextEdit::focusOutEvent(e);
+ emit lostFocus();
+ }
};
} // namespace Internal
@@ -56,11 +60,12 @@ NameValueItemsWidget::NameValueItemsWidget(QWidget *parent)
const QString helpText = Tr::tr(
"Enter one environment variable per line.\n"
"To set or change a variable, use VARIABLE=VALUE.\n"
+ "To disable a variable, prefix this line with \"#\".\n"
"To append to a variable, use VARIABLE+=VALUE.\n"
"To prepend to a variable, use VARIABLE=+VALUE.\n"
"Existing variables can be referenced in a VALUE with ${OTHER}.\n"
"To clear a variable, put its name on a line with nothing else on it.\n"
- "To disable a variable, prefix the line with \"#\".");
+ "Lines starting with \"##\" will be treated as comments.");
m_editor = new Internal::TextEditHelper(this);
auto layout = new QVBoxLayout(this);
@@ -137,7 +142,7 @@ bool NameValueItemsWidget::editVariable(const QString &name, Selection selection
skipWhiteSpace();
if (offset < line.length()) {
QChar nextChar = line.at(offset);
- if (nextChar.isLetterOrNumber())
+ if (nextChar.isLetterOrNumber() || nextChar == '_')
continue;
if (nextChar == '=') {
if (++offset < line.length() && line.at(offset) == '+')
diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp
index f8b3d8cec7..d36662f4d3 100644
--- a/src/libs/utils/outputformatter.cpp
+++ b/src/libs/utils/outputformatter.cpp
@@ -58,7 +58,7 @@ Link OutputLineParser::parseLinkTarget(const QString &target)
return {};
return Link(FilePath::fromString(parts.first()),
parts.length() > 1 ? parts.at(1).toInt() : 0,
- parts.length() > 2 ? parts.at(2).toInt() : 0);
+ parts.length() > 2 ? parts.at(2).toInt() - 1 : 0);
}
// The redirection mechanism is needed for broken build tools (e.g. xcodebuild) that get invoked
@@ -141,26 +141,39 @@ FilePath OutputLineParser::absoluteFilePath(const FilePath &filePath) const
return filePath;
}
-void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
- const FilePath &filePath, int lineNo, int pos, int len)
+void OutputLineParser::addLinkSpecForAbsoluteFilePath(
+ OutputLineParser::LinkSpecs &linkSpecs,
+ const FilePath &filePath,
+ int lineNo,
+ int column,
+ int pos,
+ int len)
{
if (filePath.toFileInfo().isAbsolute())
- linkSpecs.append({pos, len, createLinkTarget(filePath, lineNo)});
+ linkSpecs.append({pos, len, createLinkTarget(filePath, lineNo, column)});
}
-void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
- const FilePath &filePath, int lineNo, const QRegularExpressionMatch &match,
- int capIndex)
+void OutputLineParser::addLinkSpecForAbsoluteFilePath(
+ OutputLineParser::LinkSpecs &linkSpecs,
+ const FilePath &filePath,
+ int lineNo,
+ int column,
+ const QRegularExpressionMatch &match,
+ int capIndex)
{
- addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match.capturedStart(capIndex),
- match.capturedLength(capIndex));
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, column,
+ match.capturedStart(capIndex), match.capturedLength(capIndex));
}
-void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
- const FilePath &filePath, int lineNo, const QRegularExpressionMatch &match,
- const QString &capName)
+void OutputLineParser::addLinkSpecForAbsoluteFilePath(
+ OutputLineParser::LinkSpecs &linkSpecs,
+ const FilePath &filePath,
+ int lineNo,
+ int column,
+ const QRegularExpressionMatch &match,
+ const QString &capName)
{
- addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match.capturedStart(capName),
- match.capturedLength(capName));
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, column,
+ match.capturedStart(capName), match.capturedLength(capName));
}
bool Utils::OutputLineParser::fileExists(const FilePath &fp) const
@@ -342,6 +355,7 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out
= d->nextParser->handleLine(text, outputTypeForParser(d->nextParser, format));
switch (res.status) {
case OutputLineParser::Status::Done:
+ d->nextParser->flush();
d->nextParser = nullptr;
return res;
case OutputLineParser::Status::InProgress:
@@ -359,6 +373,7 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out
= parser->handleLine(text, outputTypeForParser(parser, format));
switch (res.status) {
case OutputLineParser::Status::Done:
+ parser->flush();
involvedParsers << parser;
return res;
case OutputLineParser::Status::InProgress:
@@ -402,23 +417,32 @@ const QList<FormattedText> OutputFormatter::linkifiedText(
}
for (int nextLocalTextPos = 0; nextLocalTextPos < t.text.size(); ) {
-
- // There are no more links in this part, so copy the rest of the text as-is.
- if (nextLinkSpecIndex >= linkSpecs.size()) {
+ const auto copyRestOfSegmentAsIs = [&] {
linkified << FormattedText(t.text.mid(nextLocalTextPos), t.format);
totalTextLengthSoFar += t.text.length() - nextLocalTextPos;
+ };
+
+ // We are out of links.
+ if (nextLinkSpecIndex >= linkSpecs.size()) {
+ copyRestOfSegmentAsIs();
break;
}
const OutputLineParser::LinkSpec &linkSpec = linkSpecs.at(nextLinkSpecIndex);
const int localLinkStartPos = linkSpec.startPos - totalPreviousTextLength;
+
+ // There are more links, but not in this segment.
+ if (localLinkStartPos >= t.text.size()) {
+ copyRestOfSegmentAsIs();
+ break;
+ }
+
++nextLinkSpecIndex;
// We ignore links that would cross format boundaries.
if (localLinkStartPos < nextLocalTextPos
|| localLinkStartPos + linkSpec.length > t.text.length()) {
- linkified << FormattedText(t.text.mid(nextLocalTextPos), t.format);
- totalTextLengthSoFar += t.text.length() - nextLocalTextPos;
+ copyRestOfSegmentAsIs();
break;
}
@@ -456,7 +480,7 @@ void OutputFormatter::append(const QString &text, const QTextCharFormat &format)
QTextCharFormat OutputFormatter::linkFormat(const QTextCharFormat &inputFormat, const QString &href)
{
QTextCharFormat result = inputFormat;
- result.setForeground(creatorTheme()->color(Theme::TextColorLink));
+ result.setForeground(creatorColor(Theme::TextColorLink));
result.setUnderlineStyle(QTextCharFormat::SingleUnderline);
result.setAnchor(true);
result.setAnchorHref(href);
@@ -491,14 +515,13 @@ void OutputFormatter::initFormats()
if (!plainTextEdit())
return;
- Theme *theme = creatorTheme();
- d->formats[NormalMessageFormat].setForeground(theme->color(Theme::OutputPanes_NormalMessageTextColor));
- d->formats[ErrorMessageFormat].setForeground(theme->color(Theme::OutputPanes_ErrorMessageTextColor));
- d->formats[LogMessageFormat].setForeground(theme->color(Theme::OutputPanes_WarningMessageTextColor));
- d->formats[StdOutFormat].setForeground(theme->color(Theme::OutputPanes_StdOutTextColor));
- d->formats[StdErrFormat].setForeground(theme->color(Theme::OutputPanes_StdErrTextColor));
- d->formats[DebugFormat].setForeground(theme->color(Theme::OutputPanes_DebugTextColor));
- d->formats[GeneralMessageFormat].setForeground(theme->color(Theme::OutputPanes_DebugTextColor));
+ d->formats[NormalMessageFormat].setForeground(creatorColor(Theme::OutputPanes_NormalMessageTextColor));
+ d->formats[ErrorMessageFormat].setForeground(creatorColor(Theme::OutputPanes_ErrorMessageTextColor));
+ d->formats[LogMessageFormat].setForeground(creatorColor(Theme::OutputPanes_WarningMessageTextColor));
+ d->formats[StdOutFormat].setForeground(creatorColor(Theme::OutputPanes_StdOutTextColor));
+ d->formats[StdErrFormat].setForeground(creatorColor(Theme::OutputPanes_StdErrTextColor));
+ d->formats[DebugFormat].setForeground(creatorColor(Theme::OutputPanes_DebugTextColor));
+ d->formats[GeneralMessageFormat].setForeground(creatorColor(Theme::OutputPanes_DebugTextColor));
setBoldFontEnabled(d->boldFontEnabled);
}
diff --git a/src/libs/utils/outputformatter.h b/src/libs/utils/outputformatter.h
index d26dc64327..da4dfe8a3a 100644
--- a/src/libs/utils/outputformatter.h
+++ b/src/libs/utils/outputformatter.h
@@ -93,12 +93,13 @@ protected:
Utils::FilePath absoluteFilePath(const Utils::FilePath &filePath) const;
static QString createLinkTarget(const FilePath &filePath, int line, int column);
static void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
- int lineNo, int pos, int len);
+ int lineNo, int column, int pos, int len);
static void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
- int lineNo, const QRegularExpressionMatch &match,
- int capIndex);
+ int lineNo, int column,
+ const QRegularExpressionMatch &match, int capIndex);
static void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
- int lineNo, const QRegularExpressionMatch &match,
+ int lineNo, int column,
+ const QRegularExpressionMatch &match,
const QString &capName);
bool fileExists(const Utils::FilePath &fp) const;
diff --git a/src/libs/utils/overlaywidget.cpp b/src/libs/utils/overlaywidget.cpp
index 720c5a90e1..950dc4b5dc 100644
--- a/src/libs/utils/overlaywidget.cpp
+++ b/src/libs/utils/overlaywidget.cpp
@@ -8,16 +8,34 @@
#include <QEvent>
#include <QPainter>
+namespace Utils::Internal {
+class OverlayWidgetPrivate
+{
+public:
+ OverlayWidget::PaintFunction m_paint;
+ OverlayWidget::ResizeFunction m_resize;
+};
+} // namespace Utils::Internal
+
Utils::OverlayWidget::OverlayWidget(QWidget *parent)
+ : d(new Internal::OverlayWidgetPrivate)
{
setAttribute(Qt::WA_TransparentForMouseEvents);
if (parent)
attachToWidget(parent);
+ d->m_resize = [](QWidget *w, const QSize &size) { w->setGeometry(QRect(QPoint(0, 0), size)); };
}
+Utils::OverlayWidget::~OverlayWidget() = default;
+
void Utils::OverlayWidget::setPaintFunction(const Utils::OverlayWidget::PaintFunction &paint)
{
- m_paint = paint;
+ d->m_paint = paint;
+}
+
+void Utils::OverlayWidget::setResizeFunction(const ResizeFunction &resize)
+{
+ d->m_resize = resize;
}
bool Utils::OverlayWidget::eventFilter(QObject *obj, QEvent *ev)
@@ -29,9 +47,9 @@ bool Utils::OverlayWidget::eventFilter(QObject *obj, QEvent *ev)
void Utils::OverlayWidget::paintEvent(QPaintEvent *ev)
{
- if (m_paint) {
+ if (d->m_paint) {
QPainter p(this);
- m_paint(this, p, ev);
+ d->m_paint(this, p, ev);
}
}
@@ -50,5 +68,6 @@ void Utils::OverlayWidget::attachToWidget(QWidget *parent)
void Utils::OverlayWidget::resizeToParent()
{
QTC_ASSERT(parentWidget(), return );
- setGeometry(QRect(QPoint(0, 0), parentWidget()->size()));
+ if (d->m_resize)
+ d->m_resize(this, parentWidget()->size());
}
diff --git a/src/libs/utils/overlaywidget.h b/src/libs/utils/overlaywidget.h
index 8bc5b7d1eb..c5686ad171 100644
--- a/src/libs/utils/overlaywidget.h
+++ b/src/libs/utils/overlaywidget.h
@@ -8,18 +8,26 @@
#include <QWidget>
#include <functional>
+#include <memory>
namespace Utils {
+namespace Internal {
+class OverlayWidgetPrivate;
+}
+
class QTCREATOR_UTILS_EXPORT OverlayWidget : public QWidget
{
public:
using PaintFunction = std::function<void(QWidget *, QPainter &, QPaintEvent *)>;
+ using ResizeFunction = std::function<void(QWidget *, QSize)>;
explicit OverlayWidget(QWidget *parent = nullptr);
+ ~OverlayWidget();
void attachToWidget(QWidget *parent);
void setPaintFunction(const PaintFunction &paint);
+ void setResizeFunction(const ResizeFunction &resize);
protected:
bool eventFilter(QObject *obj, QEvent *ev) override;
@@ -28,7 +36,7 @@ protected:
private:
void resizeToParent();
- PaintFunction m_paint;
+ std::unique_ptr<Internal::OverlayWidgetPrivate> d;
};
} // namespace Utils
diff --git a/src/libs/utils/passworddialog.cpp b/src/libs/utils/passworddialog.cpp
index 7958f7d6bd..94de5f7f0f 100644
--- a/src/libs/utils/passworddialog.cpp
+++ b/src/libs/utils/passworddialog.cpp
@@ -34,10 +34,9 @@ void ShowPasswordButton::paintEvent(QPaintEvent *e)
QRect r(QPoint(), size());
if (m_containsMouse && isEnabled())
- StyleHelper::drawPanelBgRect(&p, r, creatorTheme()->color(Theme::FancyToolButtonHoverColor));
+ StyleHelper::drawPanelBgRect(&p, r, creatorColor(Theme::FancyToolButtonHoverColor));
- QWindow *window = this->window()->windowHandle();
- QSize s = icon.actualSize(window, QSize(32, 16));
+ QSize s = icon.actualSize(QSize(32, 16));
QPixmap px = icon.pixmap(s);
QRect iRect(QPoint(), s);
@@ -61,8 +60,7 @@ void ShowPasswordButton::leaveEvent(QEvent *e)
QSize ShowPasswordButton::sizeHint() const
{
- QWindow *window = this->window()->windowHandle();
- QSize s = Utils::Icons::EYE_OPEN_TOOLBAR.icon().actualSize(window, QSize(32, 16)) + QSize(8, 8);
+ QSize s = Utils::Icons::EYE_OPEN_TOOLBAR.icon().actualSize(QSize(32, 16)) + QSize(8, 8);
if (StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleRelaxed)
s += QSize(5, 5);
diff --git a/src/libs/utils/persistentsettings.cpp b/src/libs/utils/persistentsettings.cpp
index 54a829004b..3702ecf960 100644
--- a/src/libs/utils/persistentsettings.cpp
+++ b/src/libs/utils/persistentsettings.cpp
@@ -106,13 +106,13 @@ const QString keyAttribute("key");
struct ParseValueStackEntry
{
- explicit ParseValueStackEntry(QVariant::Type t = QVariant::Invalid, const QString &k = {}) : type(t), key(k) {}
+ explicit ParseValueStackEntry(QMetaType::Type t = QMetaType::UnknownType, const QString &k = {}) : typeId(t), key(k) {}
explicit ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k);
QVariant value() const;
void addChild(const QString &key, const QVariant &v);
- QVariant::Type type;
+ QMetaType::Type typeId;
QString key;
QVariant simpleValue;
QVariantList listValue;
@@ -120,19 +120,19 @@ struct ParseValueStackEntry
};
ParseValueStackEntry::ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k)
- : type(aSimpleValue.type()), key(k), simpleValue(aSimpleValue)
+ : typeId(QMetaType::Type(aSimpleValue.typeId())), key(k), simpleValue(aSimpleValue)
{
QTC_ASSERT(simpleValue.isValid(), return);
}
QVariant ParseValueStackEntry::value() const
{
- switch (type) {
- case QVariant::Invalid:
+ switch (typeId) {
+ case QMetaType::UnknownType:
return QVariant();
- case QVariant::Map:
+ case QMetaType::QVariantMap:
return QVariant(mapValue);
- case QVariant::List:
+ case QMetaType::QVariantList:
return QVariant(listValue);
default:
break;
@@ -142,16 +142,16 @@ QVariant ParseValueStackEntry::value() const
void ParseValueStackEntry::addChild(const QString &key, const QVariant &v)
{
- switch (type) {
- case QVariant::Map:
+ switch (typeId) {
+ case QMetaType::QVariantMap:
mapValue.insert(key, v);
break;
- case QVariant::List:
+ case QMetaType::QVariantList:
listValue.push_back(v);
break;
default:
qWarning() << "ParseValueStackEntry::Internal error adding " << key << v << " to "
- << QVariant::typeToName(type) << value();
+ << QMetaType(typeId).name() << value();
break;
}
}
@@ -226,14 +226,14 @@ bool ParseContext::handleStartElement(QXmlStreamReader &r)
const QXmlStreamAttributes attributes = r.attributes();
const QString key = attributes.hasAttribute(keyAttribute) ?
attributes.value(keyAttribute).toString() : QString();
- m_valueStack.push_back(ParseValueStackEntry(QVariant::List, key));
+ m_valueStack.push_back(ParseValueStackEntry(QMetaType::QVariantList, key));
return false;
}
if (name == valueMapElement) {
const QXmlStreamAttributes attributes = r.attributes();
const QString key = attributes.hasAttribute(keyAttribute) ?
attributes.value(keyAttribute).toString() : QString();
- m_valueStack.push_back(ParseValueStackEntry(QVariant::Map, key));
+ m_valueStack.push_back(ParseValueStackEntry(QMetaType::QVariantMap, key));
return false;
}
return false;
@@ -369,8 +369,8 @@ static void writeVariantValue(QXmlStreamWriter &w, const QVariant &variant, cons
w.writeAttribute(typeAttribute, QLatin1String(variant.typeName()));
if (!key.isEmpty())
w.writeAttribute(keyAttribute, xmlAttrFromKey(key));
- switch (variant.type()) {
- case QVariant::Rect:
+ switch (variant.typeId()) {
+ case QMetaType::QRect:
w.writeCharacters(rectangleToString(variant.toRect()));
break;
default:
diff --git a/src/libs/utils/projectintropage.cpp b/src/libs/utils/projectintropage.cpp
index 1f32cce6bc..2c8b43b66a 100644
--- a/src/libs/utils/projectintropage.cpp
+++ b/src/libs/utils/projectintropage.cpp
@@ -110,7 +110,8 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
using namespace Layouting;
Form {
- Tr::tr("Name:"), d->m_nameLineEdit, br,
+ Tr::tr("Name:"), d->m_nameLineEdit,
+ br,
d->m_projectLabel, d->m_projectComboBox, br,
Column { Space(12) }, br,
Tr::tr("Create in:"), d->m_pathChooser, br,
@@ -135,7 +136,7 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
connect(d->m_nameLineEdit, &FancyLineEdit::validReturnPressed,
this, &ProjectIntroPage::slotActivated);
connect(d->m_projectComboBox, &QComboBox::currentIndexChanged,
- this, &ProjectIntroPage::slotChanged);
+ this, &ProjectIntroPage::onCurrentProjectIndexChanged);
setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Location"));
registerFieldWithName(QLatin1String("Path"), d->m_pathChooser, "path", SIGNAL(textChanged(QString)));
@@ -192,15 +193,10 @@ bool ProjectIntroPage::isComplete() const
bool ProjectIntroPage::validate()
{
- if (d->m_forceSubProject) {
- int index = d->m_projectComboBox->currentIndex();
- if (index == 0)
- return false;
- d->m_pathChooser->setFilePath(d->m_projectDirectories.at(index));
- }
// Validate and display status
if (!d->m_pathChooser->isValid()) {
- displayStatusMessage(InfoLabel::Error, d->m_pathChooser->errorMessage());
+ if (const QString msg = d->m_pathChooser->errorMessage(); !msg.isEmpty())
+ displayStatusMessage(InfoLabel::Error, msg);
return false;
}
@@ -259,6 +255,25 @@ void ProjectIntroPage::slotActivated()
emit activated();
}
+void ProjectIntroPage::onCurrentProjectIndexChanged(int index)
+{
+ if (d->m_forceSubProject) {
+ const int available = d->m_projectDirectories.size();
+ if (available == 0)
+ return;
+ QTC_ASSERT(index < available, return);
+ if (index < 0)
+ return;
+
+ const FilePath current = d->m_projectDirectories.at(index);
+ const FilePath visible = d->m_pathChooser->filePath();
+ if (visible != current && !visible.isChildOf(current))
+ d->m_pathChooser->setFilePath(current);
+
+ fieldsUpdated();
+ }
+}
+
bool ProjectIntroPage::forceSubProject() const
{
return d->m_forceSubProject;
diff --git a/src/libs/utils/projectintropage.h b/src/libs/utils/projectintropage.h
index 48b2f6b036..562b5b06e4 100644
--- a/src/libs/utils/projectintropage.h
+++ b/src/libs/utils/projectintropage.h
@@ -58,6 +58,7 @@ public slots:
private:
void slotChanged();
void slotActivated();
+ void onCurrentProjectIndexChanged(int index);
bool validate();
void displayStatusMessage(InfoLabel::InfoType t, const QString &);
diff --git a/src/libs/utils/qtcolorbutton.cpp b/src/libs/utils/qtcolorbutton.cpp
index 6a8537bd5f..5be9c9c069 100644
--- a/src/libs/utils/qtcolorbutton.cpp
+++ b/src/libs/utils/qtcolorbutton.cpp
@@ -171,7 +171,7 @@ void QtColorButton::paintEvent(QPaintEvent *event)
constexpr int size = 11;
const qreal horPadding = (width() - size) / 2.0;
const qreal verPadding = (height() - size) / 2.0;
- const QPen pen(creatorTheme()->color(overlayColor), 2);
+ const QPen pen(creatorColor(overlayColor), 2);
p.save();
p.setOpacity(overlayOpacity);
@@ -202,7 +202,7 @@ void QtColorButton::paintEvent(QPaintEvent *event)
p.setPen(pen);
p.setCompositionMode(QPainter::CompositionMode_Difference);
} else {
- p.setPen(creatorTheme()->color(overlayColor));
+ p.setPen(creatorColor(overlayColor));
p.setOpacity(overlayOpacity);
}
p.drawRect(rect().adjusted(0, 0, -1, -1));
diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp
index b399d86b36..dca106a7d5 100644
--- a/src/libs/utils/qtcprocess.cpp
+++ b/src/libs/utils/qtcprocess.cpp
@@ -769,6 +769,7 @@ private:
std::unique_ptr<ProcessInterfaceHandler> m_processHandler;
mutable QMutex m_mutex;
QList<ProcessInterfaceSignal *> m_signals;
+ Guard m_guard;
};
class ProcessPrivate : public QObject
@@ -961,6 +962,10 @@ GeneralProcessBlockingImpl::GeneralProcessBlockingImpl(ProcessPrivate *parent)
bool GeneralProcessBlockingImpl::waitForSignal(ProcessSignalType newSignal, QDeadlineTimer timeout)
{
+ QTC_ASSERT(!m_guard.isLocked(), qWarning("Process::waitForSignal() called recursively. "
+ "The call is being ignored."); return false);
+ GuardLocker locker(m_guard);
+
m_processHandler->setParent(nullptr);
QThread thread;
@@ -1355,7 +1360,7 @@ QString Process::toStandaloneCommandLine() const
parts.append("/usr/bin/env");
if (!d->m_setup.m_workingDirectory.isEmpty()) {
parts.append("-C");
- d->m_setup.m_workingDirectory.path();
+ parts.append(d->m_setup.m_workingDirectory.path());
}
parts.append("-i");
if (d->m_setup.m_environment.hasChanges()) {
diff --git a/src/libs/utils/reloadpromptutils.cpp b/src/libs/utils/reloadpromptutils.cpp
index 5257138245..8a07592d3f 100644
--- a/src/libs/utils/reloadpromptutils.cpp
+++ b/src/libs/utils/reloadpromptutils.cpp
@@ -89,12 +89,10 @@ QTCREATOR_UTILS_EXPORT FileDeletedPromptAnswer
"Do you want to save it under a different name, or close "
"the editor?").arg(QDir::toNativeSeparators(fileName));
QMessageBox box(QMessageBox::Question, title, msg, QMessageBox::NoButton, parent);
- QPushButton *close =
- box.addButton(Tr::tr("&Close"), QMessageBox::RejectRole);
+ QPushButton *saveas = box.addButton(Tr::tr("Save &as..."), QMessageBox::ActionRole);
+ QPushButton *close = box.addButton(Tr::tr("&Close"), QMessageBox::RejectRole);
QPushButton *closeAll =
box.addButton(Tr::tr("C&lose All"), QMessageBox::RejectRole);
- QPushButton *saveas =
- box.addButton(Tr::tr("Save &as..."), QMessageBox::ActionRole);
QPushButton *save =
box.addButton(Tr::tr("&Save"), QMessageBox::AcceptRole);
box.setDefaultButton(saveas);
diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h
index c522a6cae9..e8c3d74b92 100644
--- a/src/libs/utils/smallstring.h
+++ b/src/libs/utils/smallstring.h
@@ -93,7 +93,7 @@ public:
static_cast<std::size_t>(std::distance(begin, end))}
{}
- template<typename Type, typename = std::enable_if_t<std::is_pointer<Type>::value>>
+ template<typename Type, typename std::enable_if_t<std::is_pointer<Type>::value, bool> = true>
BasicSmallString(Type characterPointer) noexcept
: BasicSmallString(characterPointer, std::char_traits<char>::length(characterPointer))
{
@@ -118,7 +118,7 @@ public:
template<typename BeginIterator,
typename EndIterator,
- typename = std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value>>
+ typename std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value, bool> = true>
BasicSmallString(BeginIterator begin, EndIterator end) noexcept
: BasicSmallString(&(*begin), size_type(end - begin))
{}
@@ -354,6 +354,14 @@ public:
return false;
}
+ bool startsWith(QStringView subStringToSearch) const noexcept
+ {
+ if (size() >= Utils::usize(subStringToSearch))
+ return subStringToSearch == QLatin1StringView{data(), subStringToSearch.size()};
+
+ return false;
+ }
+
bool startsWith(char characterToSearch) const noexcept
{
return data()[0] == characterToSearch;
@@ -423,13 +431,55 @@ public:
size_type oldSize = size();
size_type newSize = oldSize + string.size();
- reserve(optimalCapacity(newSize));
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
std::char_traits<char>::copy(std::next(data(), static_cast<std::ptrdiff_t>(oldSize)),
string.data(),
string.size());
setSize(newSize);
}
+ void append(char character) noexcept
+ {
+ size_type oldSize = size();
+ size_type newSize = oldSize + 1;
+
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
+ auto current = std::next(data(), static_cast<std::ptrdiff_t>(oldSize));
+ *current = character;
+ setSize(newSize);
+ }
+
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ void append(Type number)
+ {
+#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+#else
+ if constexpr (std::is_floating_point_v<Type>) {
+ QLocale locale{QLocale::Language::C};
+ append(locale.toString(number));
+ return;
+ } else {
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+ }
+
+#endif
+ }
+
void append(QStringView string) noexcept
{
QStringEncoder encoder{QStringEncoder::Utf8};
@@ -469,6 +519,13 @@ public:
return *this;
}
+ BasicSmallString &operator+=(char character) noexcept
+ {
+ append(character);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(QStringView string) noexcept
{
append(string);
@@ -476,6 +533,14 @@ public:
return *this;
}
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ BasicSmallString &operator+=(Type number) noexcept
+ {
+ append(number);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(std::initializer_list<SmallStringView> list) noexcept
{
appendInitializerList(list, size());
@@ -580,37 +645,12 @@ public:
return joinedString;
}
- static
- BasicSmallString number(int number)
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ static BasicSmallString number(Type number)
{
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
- }
-
- static BasicSmallString number(long long int number) noexcept
- {
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<long long int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
- }
-
- static BasicSmallString number(double number) noexcept
- {
-#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<double>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
-#else
- QLocale locale{QLocale::Language::C};
- return BasicSmallString{locale.toString(number)};
-#endif
+ BasicSmallString string;
+ string.append(number);
+ return string;
}
char &operator[](std::size_t index) noexcept { return *(data() + index); }
@@ -655,7 +695,6 @@ public:
friend BasicSmallString operator+(const BasicSmallString &first,
const char (&second)[ArraySize]) noexcept
{
-
return operator+(first, SmallStringView(second));
}
@@ -687,8 +726,10 @@ unittest_public:
bool fitsNotInCapacity(size_type capacity) const noexcept
{
- return (isShortString() && capacity > shortStringCapacity())
- || (!isShortString() && capacity > m_data.reference.capacity);
+ if (isShortString())
+ return capacity > shortStringCapacity();
+
+ return capacity > m_data.reference.capacity;
}
static size_type optimalHeapCapacity(const size_type size) noexcept
diff --git a/src/libs/utils/store.cpp b/src/libs/utils/store.cpp
index fa8d4232c0..8c0e2a6046 100644
--- a/src/libs/utils/store.cpp
+++ b/src/libs/utils/store.cpp
@@ -50,10 +50,10 @@ static QVariantList mapListFromStoreList(const QVariantList &storeList);
QVariant storeEntryFromMapEntry(const QVariant &mapEntry)
{
- if (mapEntry.type() == QVariant::Map)
+ if (mapEntry.typeId() == QMetaType::QVariantMap)
return QVariant::fromValue(storeFromMap(mapEntry.toMap()));
- if (mapEntry.type() == QVariant::List)
+ if (mapEntry.typeId() == QMetaType::QVariantList)
return QVariant::fromValue(storeListFromMapList(mapEntry.toList()));
return mapEntry;
@@ -64,7 +64,7 @@ QVariant mapEntryFromStoreEntry(const QVariant &storeEntry)
if (storeEntry.metaType() == QMetaType::fromType<Store>())
return QVariant::fromValue(mapFromStore(storeEntry.value<Store>()));
- if (storeEntry.type() == QVariant::List)
+ if (storeEntry.typeId() == QMetaType::QVariantList)
return QVariant::fromValue(mapListFromStoreList(storeEntry.toList()));
return storeEntry;
diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp
index d8de512b32..d21c7cb9bc 100644
--- a/src/libs/utils/stringutils.cpp
+++ b/src/libs/utils/stringutils.cpp
@@ -637,7 +637,7 @@ void MarkdownHighlighter::highlightBlock(const QString &text)
image.fill(QColor(0, 0, 0, 0).rgba());
image.setPixel(0,
height - 1,
- Utils::creatorTheme()->color(Theme::TextColorDisabled).rgba());
+ Utils::creatorColor(Theme::TextColorDisabled).rgba());
h2Brush = QBrush(image);
}
@@ -672,4 +672,10 @@ void MarkdownHighlighter::highlightBlock(const QString &text)
}
}
+QString ansiColoredText(const QString &text, const QColor &color)
+{
+ static const QString formatString("\033[38;2;%1;%2;%3m%4\033[0m");
+ return formatString.arg(color.red()).arg(color.green()).arg(color.blue()).arg(text);
+}
+
} // namespace Utils
diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h
index 3e729e672c..cab6d774f3 100644
--- a/src/libs/utils/stringutils.h
+++ b/src/libs/utils/stringutils.h
@@ -138,4 +138,6 @@ private:
QBrush m_codeBgBrush;
};
+QTCREATOR_UTILS_EXPORT QString ansiColoredText(const QString &text, const QColor &color);
+
} // namespace Utils
diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp
index 69b5e39556..080bb8a382 100644
--- a/src/libs/utils/stylehelper.cpp
+++ b/src/libs/utils/stylehelper.cpp
@@ -107,7 +107,7 @@ QColor StyleHelper::notTooBrightHighlightColor()
QPalette StyleHelper::sidebarFontPalette(const QPalette &original)
{
QPalette palette = original;
- const QColor textColor = creatorTheme()->color(Theme::ProgressBarTitleColor);
+ const QColor textColor = creatorColor(Theme::ProgressBarTitleColor);
palette.setColor(QPalette::WindowText, textColor);
palette.setColor(QPalette::Text, textColor);
return palette;
@@ -137,7 +137,7 @@ QColor StyleHelper::requestedBaseColor()
QColor StyleHelper::toolbarBaseColor(bool lightColored)
{
if (creatorTheme()->flag(Theme::QDSTheme))
- return creatorTheme()->color(Utils::Theme::DStoolbarBackground);
+ return creatorColor(Utils::Theme::DStoolbarBackground);
else
return StyleHelper::baseColor(lightColored);
}
@@ -194,7 +194,7 @@ void StyleHelper::setBaseColor(const QColor &newcolor)
{
s_requestedBaseColor = newcolor;
- const QColor themeBaseColor = creatorTheme()->color(Theme::PanelStatusBarBackgroundColor);
+ const QColor themeBaseColor = creatorColor(Theme::PanelStatusBarBackgroundColor);
const QColor defaultBaseColor = QColor(DEFAULT_BASE_COLOR);
QColor color;
@@ -366,11 +366,11 @@ void StyleHelper::drawArrow(QStyle::PrimitiveElement element, QPainter *painter,
};
if (!enabled) {
- drawCommonStyleArrow(image.rect(), creatorTheme()->color(Theme::IconsDisabledColor));
+ drawCommonStyleArrow(image.rect(), creatorColor(Theme::IconsDisabledColor));
} else {
if (creatorTheme()->flag(Theme::ToolBarIconShadow))
drawCommonStyleArrow(image.rect().translated(0, devicePixelRatio), toolBarDropShadowColor());
- drawCommonStyleArrow(image.rect(), creatorTheme()->color(Theme::IconsBaseColor));
+ drawCommonStyleArrow(image.rect(), creatorColor(Theme::IconsBaseColor));
}
painter.end();
pixmap = QPixmap::fromImage(image);
@@ -463,9 +463,9 @@ void StyleHelper::drawMinimalArrow(QStyle::PrimitiveElement element, QPainter *p
if (enabled) {
if (creatorTheme()->flag(Theme::ToolBarIconShadow))
drawArrow(image.rect().translated(0, devicePixelRatio), toolBarDropShadowColor());
- drawArrow(image.rect(), creatorTheme()->color(Theme::IconsBaseColor));
+ drawArrow(image.rect(), creatorColor(Theme::IconsBaseColor));
} else {
- drawArrow(image.rect(), creatorTheme()->color(Theme::IconsDisabledColor));
+ drawArrow(image.rect(), creatorColor(Theme::IconsDisabledColor));
}
painter.end();
pixmap = QPixmap::fromImage(image);
@@ -551,8 +551,7 @@ void StyleHelper::drawIconWithShadow(const QIcon &icon, const QRect &rect,
// return a high-dpi pixmap, which will in that case have a devicePixelRatio
// different than 1. The shadow drawing caluculations are done in device
// pixels.
- QWindow *window = dynamic_cast<QWidget*>(p->device())->window()->windowHandle();
- QPixmap px = icon.pixmap(window, rect.size(), iconMode);
+ QPixmap px = icon.pixmap(rect.size(), devicePixelRatio, iconMode);
int radius = int(dipRadius * devicePixelRatio);
QPoint offset = dipOffset * devicePixelRatio;
cache = QPixmap(px.size() + QSize(radius * 2, radius * 2));
@@ -563,7 +562,7 @@ void StyleHelper::drawIconWithShadow(const QIcon &icon, const QRect &rect,
const bool hasDisabledState =
icon.availableSizes().count() == icon.availableSizes(QIcon::Disabled).count();
if (!hasDisabledState)
- px = disabledSideBarIcon(icon.pixmap(window, rect.size()));
+ px = disabledSideBarIcon(icon.pixmap(rect.size(), devicePixelRatio));
} else if (creatorTheme()->flag(Theme::ToolBarIconShadow)) {
// Draw shadow
QImage tmp(px.size() + QSize(radius * 2, radius * 2 + 1), QImage::Format_ARGB32_Premultiplied);
@@ -716,12 +715,7 @@ Qt::HighDpiScaleFactorRoundingPolicy StyleHelper::defaultHighDpiScaleFactorRound
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QList<IconFontHelper> &parameters)
{
- QFontDatabase a;
-
- QTC_ASSERT(a.hasFamily(fontName), {});
-
- if (!a.hasFamily(fontName))
- return {};
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), {});
QIcon icon;
@@ -751,38 +745,31 @@ QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QList<Icon
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color)
{
- QFontDatabase a;
-
- QTC_ASSERT(a.hasFamily(fontName), {});
-
- if (a.hasFamily(fontName)) {
-
- QIcon icon;
- QSize size(iconSize, iconSize);
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), {});
- const int maxDpr = qRound(qApp->devicePixelRatio());
- for (int dpr = 1; dpr <= maxDpr; dpr++) {
- QPixmap pixmap(size * dpr);
- pixmap.setDevicePixelRatio(dpr);
- pixmap.fill(Qt::transparent);
+ QIcon icon;
+ QSize size(iconSize, iconSize);
- QFont font(fontName);
- font.setPixelSize(fontSize);
+ const int maxDpr = qRound(qApp->devicePixelRatio());
+ for (int dpr = 1; dpr <= maxDpr; dpr++) {
+ QPixmap pixmap(size * dpr);
+ pixmap.setDevicePixelRatio(dpr);
+ pixmap.fill(Qt::transparent);
- QPainter painter(&pixmap);
- painter.save();
- painter.setPen(color);
- painter.setFont(font);
- painter.drawText(QRectF(QPoint(0, 0), size), Qt::AlignCenter, iconSymbol);
- painter.restore();
+ QFont font(fontName);
+ font.setPixelSize(fontSize);
- icon.addPixmap(pixmap);
- }
+ QPainter painter(&pixmap);
+ painter.save();
+ painter.setPen(color);
+ painter.setFont(font);
+ painter.drawText(QRectF(QPoint(0, 0), size), Qt::AlignCenter, iconSymbol);
+ painter.restore();
- return icon;
+ icon.addPixmap(pixmap);
}
- return {};
+ return icon;
}
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize)
@@ -794,55 +781,47 @@ QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &i
QIcon StyleHelper::getCursorFromIconFont(const QString &fontName, const QString &cursorFill, const QString &cursorOutline,
int fontSize, int iconSize)
{
- QFontDatabase a;
-
- QTC_ASSERT(a.hasFamily(fontName), {});
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), {});
const QColor outlineColor = Qt::black;
const QColor fillColor = Qt::white;
- if (a.hasFamily(fontName)) {
-
- QIcon icon;
- QSize size(iconSize, iconSize);
-
- const int maxDpr = qRound(qApp->devicePixelRatio());
- for (int dpr = 1; dpr <= maxDpr; dpr++) {
- QPixmap pixmap(size * dpr);
- pixmap.setDevicePixelRatio(dpr);
- pixmap.fill(Qt::transparent);
+ QIcon icon;
+ QSize size(iconSize, iconSize);
- QFont font(fontName);
- font.setPixelSize(fontSize);
+ const int maxDpr = qRound(qApp->devicePixelRatio());
+ for (int dpr = 1; dpr <= maxDpr; dpr++) {
+ QPixmap pixmap(size * dpr);
+ pixmap.setDevicePixelRatio(dpr);
+ pixmap.fill(Qt::transparent);
- QPainter painter(&pixmap);
- painter.save();
- painter.setRenderHint(QPainter::Antialiasing, true);
- painter.setRenderHint(QPainter::TextAntialiasing, true);
- painter.setRenderHint(QPainter::LosslessImageRendering, true);
- painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
+ QFont font(fontName);
+ font.setPixelSize(fontSize);
- painter.setFont(font);
- painter.setPen(outlineColor);
- painter.drawText(QRectF(QPointF(0.0, 0.0), size),
- Qt::AlignCenter, cursorOutline);
+ QPainter painter(&pixmap);
+ painter.save();
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setRenderHint(QPainter::TextAntialiasing, true);
+ painter.setRenderHint(QPainter::LosslessImageRendering, true);
+ painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
- painter.setPen(fillColor);
- painter.drawText(QRectF(QPointF(0.0, 0.0), size),
- Qt::AlignCenter, cursorFill);
+ painter.setFont(font);
+ painter.setPen(outlineColor);
+ painter.drawText(QRectF(QPointF(0.0, 0.0), size),
+ Qt::AlignCenter, cursorOutline);
- painter.restore();
+ painter.setPen(fillColor);
+ painter.drawText(QRectF(QPointF(0.0, 0.0), size),
+ Qt::AlignCenter, cursorFill);
- icon.addPixmap(pixmap);
- }
+ painter.restore();
- return icon;
+ icon.addPixmap(pixmap);
}
- return {};
+ return icon;
}
-
QString StyleHelper::dpiSpecificImageFile(const QString &fileName)
{
// See QIcon::addFile()
@@ -970,6 +949,8 @@ static const UiFontMetrics& uiFontMetrics(StyleHelper::UiElement element)
{StyleHelper::UiElementBody2, {12, 20, QFont::Light}},
{StyleHelper::UiElementButtonMedium, {12, 16, QFont::Bold}},
{StyleHelper::UiElementButtonSmall, {10, 12, QFont::Bold}},
+ {StyleHelper::UiElementLabelMedium, {12, 16, QFont::DemiBold}},
+ {StyleHelper::UiElementLabelSmall, {10, 12, QFont::DemiBold}},
{StyleHelper::UiElementCaptionStrong, {10, 12, QFont::DemiBold}},
{StyleHelper::UiElementCaption, {10, 12, QFont::Normal}},
{StyleHelper::UiElementIconStandard, {12, 16, QFont::Medium}},
@@ -1010,7 +991,13 @@ QFont StyleHelper::uiFont(UiElement element)
const qreal qrealPointSize = metrics.pixelSize * pixelsToPointSizeFactor;
font.setPointSizeF(qrealPointSize);
- font.setWeight(metrics.weight);
+ // Intermediate font weights can produce blurry rendering and are harder to read.
+ // For "non-retina" screens, apply the weight only for some fonts.
+ static const bool isHighDpi = qApp->devicePixelRatio() >= 2;
+ const bool setWeight = isHighDpi || element == UiElementCaptionStrong
+ || element <= UiElementH4;
+ if (setWeight)
+ font.setWeight(metrics.weight);
return font;
}
diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h
index 7d434925e6..ecc2eea2b9 100644
--- a/src/libs/utils/stylehelper.h
+++ b/src/libs/utils/stylehelper.h
@@ -79,6 +79,8 @@ enum ToolbarStyle {
};
constexpr ToolbarStyle defaultToolbarStyle = ToolbarStyleCompact;
+// Keep in sync with:
+// SyleHelper::uiFontMetrics, ICore::uiConfigInformation, tst_manual_widgets_uifonts::main
enum UiElement {
UiElementH1,
UiElementH2,
@@ -91,6 +93,8 @@ enum UiElement {
UiElementBody2,
UiElementButtonMedium,
UiElementButtonSmall,
+ UiElementLabelMedium,
+ UiElementLabelSmall,
UiElementCaptionStrong,
UiElementCaption,
UiElementIconStandard,
diff --git a/src/libs/utils/terminalinterface.cpp b/src/libs/utils/terminalinterface.cpp
index 0d5a3be020..6f7bb11f81 100644
--- a/src/libs/utils/terminalinterface.cpp
+++ b/src/libs/utils/terminalinterface.cpp
@@ -334,22 +334,12 @@ void TerminalInterface::start()
Environment finalEnv = m_setup.m_environment;
- if (HostOsInfo::isWindowsHost()) {
- if (!finalEnv.hasKey("PATH")) {
- const QString path = qtcEnvironmentVariable("PATH");
- if (!path.isEmpty())
- finalEnv.set("PATH", path);
- }
- if (!finalEnv.hasKey("SystemRoot")) {
- const QString systemRoot = qtcEnvironmentVariable("SystemRoot");
- if (!systemRoot.isEmpty())
- finalEnv.set("SystemRoot", systemRoot);
- }
- } else if (HostOsInfo::isMacHost()) {
+ if (HostOsInfo::isMacHost())
finalEnv.set("TERM", "xterm-256color");
- }
if (finalEnv.hasChanges()) {
+ finalEnv = finalEnv.appliedToEnvironment(Environment::systemEnvironment());
+
d->envListFile = std::make_unique<QTemporaryFile>(this);
if (!d->envListFile->open()) {
cleanupAfterStartFailure(msgCannotCreateTempFile(d->envListFile->errorString()));
@@ -402,7 +392,7 @@ void TerminalInterface::start()
m_setup.m_commandLine.executable().fileName());
if (m_setup.m_runAsRoot && !HostOsInfo::isWindowsHost()) {
- CommandLine rootCommand("sudo", {});
+ CommandLine rootCommand("sudo");
rootCommand.addCommandLineAsArgs(cmd);
stubSetupData.m_commandLine = rootCommand;
} else {
diff --git a/src/libs/utils/theme/theme.cpp b/src/libs/utils/theme/theme.cpp
index 9b24078f86..ef20ddddb7 100644
--- a/src/libs/utils/theme/theme.cpp
+++ b/src/libs/utils/theme/theme.cpp
@@ -18,6 +18,7 @@
namespace Utils {
static Theme *m_creatorTheme = nullptr;
+static std::optional<QPalette> m_initialPalette;
ThemePrivate::ThemePrivate()
{
@@ -37,9 +38,31 @@ Theme *proxyTheme()
return new Theme(m_creatorTheme);
}
+// Convenience
+QColor creatorColor(Theme::Color role)
+{
+ return m_creatorTheme->color(role);
+}
+
+static bool paletteIsDark(const QPalette &pal)
+{
+ return pal.color(QPalette::Window).lightnessF() < pal.color(QPalette::WindowText).lightnessF();
+}
+
+static bool isOverridingPalette(const Theme *theme)
+{
+ if (theme->flag(Theme::DerivePaletteFromTheme))
+ return true;
+ if (theme->flag(Theme::DerivePaletteFromThemeIfNeeded)
+ && paletteIsDark(Theme::initialPalette()) != theme->flag(Theme::DarkUserInterface)) {
+ return true;
+ }
+ return false;
+}
+
void setThemeApplicationPalette()
{
- if (m_creatorTheme && m_creatorTheme->flag(Theme::ApplyThemePaletteGlobally))
+ if (m_creatorTheme && isOverridingPalette(m_creatorTheme))
QApplication::setPalette(m_creatorTheme->palette());
}
@@ -185,7 +208,7 @@ void Theme::setDisplayName(const QString &name)
void Theme::readSettingsInternal(QSettings &settings)
{
- const QStringList includes = settings.value("Includes").toString().split(",", Qt::SkipEmptyParts);
+ const QStringList includes = settings.value("Includes").toStringList();
for (const QString &include : includes) {
FilePath path = FilePath::fromString(d->fileName);
@@ -311,7 +334,13 @@ bool Theme::systemUsesDarkMode()
if (HostOsInfo::isMacHost())
return macOSSystemIsDark();
- return false;
+ // Avoid enforcing the initial palette.
+ // The initial palette must be set after setting the macOS appearance in setInitialPalette,
+ // but systemUsesDarkMode is used to determine the default theme, which is in turn required
+ // for the setInitialPalette call
+ if (m_initialPalette)
+ return paletteIsDark(*m_initialPalette);
+ return paletteIsDark(QApplication::palette());
}
// If you copy QPalette, default values stay at default, even if that default is different
@@ -347,14 +376,17 @@ void Theme::setHelpMenu(QMenu *menu)
QPalette Theme::initialPalette()
{
- static QPalette palette = copyPalette(QApplication::palette());
- return palette;
+ if (!m_initialPalette) {
+ m_initialPalette = copyPalette(QApplication::palette());
+ QApplication::setPalette(*m_initialPalette);
+ }
+ return *m_initialPalette;
}
QPalette Theme::palette() const
{
QPalette pal = initialPalette();
- if (!flag(DerivePaletteFromTheme))
+ if (!isOverridingPalette(this))
return pal;
const static struct {
diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h
index 6b3bf97d45..8d7e922e79 100644
--- a/src/libs/utils/theme/theme.h
+++ b/src/libs/utils/theme/theme.h
@@ -247,6 +247,10 @@ public:
Token_Notification_Success,
Token_Notification_Neutral,
Token_Notification_Danger,
+ Token_Gradient01_Start,
+ Token_Gradient01_End,
+ Token_Gradient02_Start,
+ Token_Gradient02_End,
/* Timeline Library */
Timeline_TextColor,
@@ -527,7 +531,7 @@ public:
DrawToolBarBorders,
ComboBoxDrawTextShadow,
DerivePaletteFromTheme,
- ApplyThemePaletteGlobally,
+ DerivePaletteFromThemeIfNeeded,
FlatToolBars,
FlatSideBarIcons,
FlatProjectsMode,
@@ -577,5 +581,6 @@ private:
QTCREATOR_UTILS_EXPORT Theme *creatorTheme();
QTCREATOR_UTILS_EXPORT Theme *proxyTheme();
+QTCREATOR_UTILS_EXPORT QColor creatorColor(Theme::Color role);
} // namespace Utils
diff --git a/src/libs/utils/threadutils.cpp b/src/libs/utils/threadutils.cpp
index 344779dc93..cfb18059a6 100644
--- a/src/libs/utils/threadutils.cpp
+++ b/src/libs/utils/threadutils.cpp
@@ -10,7 +10,12 @@ namespace Utils {
bool isMainThread()
{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
+ // TODO: Remove threadutils.h and replace all usages with:
+ return QThread::isMainThread();
+#else
return QThread::currentThread() == qApp->thread();
+#endif
}
} // namespace Utils
diff --git a/src/libs/utils/tooltip/tooltip.cpp b/src/libs/utils/tooltip/tooltip.cpp
index 93f4c1d2ff..da11540c08 100644
--- a/src/libs/utils/tooltip/tooltip.cpp
+++ b/src/libs/utils/tooltip/tooltip.cpp
@@ -288,9 +288,11 @@ void ToolTip::hideTipWithDelay()
void ToolTip::hideTipImmediately()
{
if (m_tip) {
- m_tip->close();
- m_tip->deleteLater();
- m_tip = nullptr;
+ TipLabel *tip = m_tip.data();
+ m_tip.clear();
+
+ tip->close();
+ tip->deleteLater();
}
m_showTimer.stop();
m_hideDelayTimer.stop();
diff --git a/src/libs/utils/treemodel.cpp b/src/libs/utils/treemodel.cpp
index 4649bb43c8..edddb2d484 100644
--- a/src/libs/utils/treemodel.cpp
+++ b/src/libs/utils/treemodel.cpp
@@ -459,13 +459,13 @@ void ModelTest::data()
// General Purpose roles that should return a QString
QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole);
if (variant.isValid())
- Q_ASSERT(variant.canConvert(QVariant::String));
+ Q_ASSERT(variant.canConvert(QMetaType::QString));
variant = model->data(model->index(0, 0), Qt::StatusTipRole);
if (variant.isValid())
- Q_ASSERT(variant.canConvert(QVariant::String));
+ Q_ASSERT(variant.canConvert(QMetaType::QString));
variant = model->data(model->index(0, 0), Qt::WhatsThisRole);
if (variant.isValid())
- Q_ASSERT(variant.canConvert(QVariant::String));
+ Q_ASSERT(variant.canConvert(QMetaType::QString));
// General Purpose roles that should return a QSize
variant = model->data(model->index(0, 0), Qt::SizeHintRole);
diff --git a/src/libs/utils/unarchiver.cpp b/src/libs/utils/unarchiver.cpp
index 8436565425..d9092e4ca9 100644
--- a/src/libs/utils/unarchiver.cpp
+++ b/src/libs/utils/unarchiver.cpp
@@ -43,6 +43,7 @@ static const QList<Tool> &sTools()
{
static QList<Tool> tools;
if (tools.isEmpty()) {
+ // clang-format off
if (HostOsInfo::isWindowsHost()) {
tools << Tool{{"powershell", "-command Expand-Archive -Force '%{src}' '%{dest}'", CommandLine::Raw},
{"application/zip"},
@@ -74,6 +75,10 @@ static const QList<Tool> &sTools()
tools << Tool{{"cmake", {"-E", "tar", "xvjf", "%{src}"}},
{"application/x-bzip-compressed-tar"},
additionalCMakeDirs};
+ // Keep this at the end so its only used as last resort. Otherwise it might be used for
+ // .tar.gz files.
+ tools << Tool{{"gzip", {"-d", "%{src}", "-c"}}, {"application/gzip"}, {}};
+ // clang-format on
}
return tools;
}
@@ -147,6 +152,45 @@ void Unarchiver::start()
m_sourceAndCommand->m_sourceFile, m_destDir);
m_destDir.ensureWritableDir();
+ if (command.executable().fileName() == "gzip") {
+ std::shared_ptr<QFile> outputFile = std::make_shared<QFile>(
+ (m_destDir / m_gzipFileDestName).toFSPathString());
+
+ if (!outputFile->open(QIODevice::WriteOnly)) {
+ emit outputReceived(Tr::tr("Failed to open output file."));
+ emit done(DoneResult::Error);
+ return;
+ }
+
+ m_process.reset(new Process);
+ QObject::connect(m_process.get(), &Process::readyReadStandardOutput, this, [this, outputFile] {
+ const QByteArray data = m_process->readAllRawStandardOutput();
+ if (outputFile->write(data) != data.size()) {
+ emit outputReceived(Tr::tr("Failed to write output file."));
+ emit done(DoneResult::Error);
+ }
+ });
+ QObject::connect(m_process.get(), &Process::readyReadStandardError, this, [this] {
+ emit outputReceived(m_process->readAllStandardError());
+ });
+ QObject::connect(m_process.get(), &Process::done, this, [outputFile, this] {
+ outputFile->close();
+ const bool success = m_process->result() == ProcessResult::FinishedWithSuccess;
+ if (!success) {
+ outputFile->remove();
+ emit outputReceived(Tr::tr("Command failed."));
+ }
+ emit done(toDoneResult(success));
+ });
+ emit outputReceived(
+ Tr::tr("Running %1\nin \"%2\".\n\n", "Running <cmd> in <workingdirectory>")
+ .arg(command.toUserOutput(), m_destDir.toUserOutput()));
+ m_process->setCommand(command);
+ m_process->setWorkingDirectory(m_destDir);
+ m_process->start();
+ return;
+ }
+
m_process.reset(new Process);
m_process->setProcessChannelMode(QProcess::MergedChannels);
QObject::connect(m_process.get(), &Process::readyReadStandardOutput, this, [this] {
diff --git a/src/libs/utils/unarchiver.h b/src/libs/utils/unarchiver.h
index 61818318bb..23fcad07a8 100644
--- a/src/libs/utils/unarchiver.h
+++ b/src/libs/utils/unarchiver.h
@@ -32,7 +32,10 @@ public:
void setSourceAndCommand(const SourceAndCommand &data) { m_sourceAndCommand = data; }
void setDestDir(const FilePath &destDir) { m_destDir = destDir; }
-
+ void setGZipFileDestName(const QString &gzipFileDestName)
+ {
+ m_gzipFileDestName = gzipFileDestName;
+ }
void start();
signals:
@@ -43,6 +46,7 @@ private:
std::optional<SourceAndCommand> m_sourceAndCommand;
FilePath m_destDir;
std::unique_ptr<Process> m_process;
+ QString m_gzipFileDestName;
};
class QTCREATOR_UTILS_EXPORT UnarchiverTaskAdapter : public Tasking::TaskAdapter<Unarchiver>
diff --git a/src/libs/utils/utility.h b/src/libs/utils/utility.h
new file mode 100644
index 0000000000..27de88d339
--- /dev/null
+++ b/src/libs/utils/utility.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace Utils {
+
+template<typename Enumeration>
+[[nodiscard]] constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
+{
+ return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index 211d184f73..afc0f202aa 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -185,6 +185,8 @@ QtcLibrary {
"link.h",
"listmodel.h",
"listutils.h",
+ "lua.cpp",
+ "lua.h",
"macroexpander.cpp",
"macroexpander.h",
"mathutils.cpp",
@@ -374,8 +376,7 @@ QtcLibrary {
"fsenginehandler.cpp",
"fsenginehandler.h",
"fsengine_impl.cpp",
- "fsengine_impl.h",
- "rootinjectfsengine.h",
+ "fsengine_impl.h"
]
}
diff --git a/src/libs/utils/wizard.cpp b/src/libs/utils/wizard.cpp
index 10d801c521..29bfb787a6 100644
--- a/src/libs/utils/wizard.cpp
+++ b/src/libs/utils/wizard.cpp
@@ -45,7 +45,9 @@ public:
m_indicatorPixmap(indicatorPixmap)
{
m_indicatorLabel = new QLabel(this);
- m_indicatorLabel->setFixedSize(m_indicatorPixmap.size());
+ const QSizeF indicatorSize = m_indicatorPixmap.deviceIndependentSize();
+ m_indicatorLabel->setFixedSize(
+ {qCeil(indicatorSize.width()), qCeil(indicatorSize.height())});
m_titleLabel = new QLabel(title, this);
auto l = new QHBoxLayout(this);
l->setContentsMargins(0, 0, 0, 0);
@@ -276,6 +278,7 @@ public:
bool m_automaticProgressCreation = true;
WizardProgress *m_wizardProgress = nullptr;
QSet<QString> m_fieldNames;
+ bool m_skipForSubproject = false;
};
Wizard::Wizard(QWidget *parent, Qt::WindowFlags flags) :
@@ -365,8 +368,8 @@ QHash<QString, QVariant> Wizard::variables() const
QString typeOf(const QVariant &v)
{
QString result;
- switch (v.type()) {
- case QVariant::Map:
+ switch (v.typeId()) {
+ case QMetaType::QVariantMap:
result = QLatin1String("Object");
break;
default:
@@ -525,6 +528,32 @@ void Wizard::_q_pageRemoved(int pageId)
d->m_wizardProgress->removeItem(item);
}
+void Wizard::setSkipForSubprojects(bool skip)
+{
+ Q_D(Wizard);
+ d->m_skipForSubproject = skip;
+}
+
+int Wizard::nextId() const
+{
+ Q_D(const Wizard);
+ if (!d->m_skipForSubproject)
+ return QWizard::nextId();
+
+ const QList<int> allIds = pageIds();
+ int index = allIds.indexOf(currentId());
+ QTC_ASSERT(index > -1, return QWizard::nextId());
+
+ while (++index < allIds.size()) {
+ if (auto wp = qobject_cast<WizardPage *>(page(index))) {
+ if (!wp->skipForSubprojects())
+ return index;
+ }
+ }
+ QTC_CHECK(false); // should not happen
+ return QWizard::nextId();
+}
+
class WizardProgressPrivate
{
WizardProgress *q_ptr;
diff --git a/src/libs/utils/wizard.h b/src/libs/utils/wizard.h
index 35abff2f6d..ac3ad795bf 100644
--- a/src/libs/utils/wizard.h
+++ b/src/libs/utils/wizard.h
@@ -50,6 +50,10 @@ public:
void showVariables();
+ // allows to skip pages
+ void setSkipForSubprojects(bool skip);
+ int nextId() const override;
+
protected:
virtual QString stringify(const QVariant &v) const;
virtual QString evaluate(const QVariant &v) const;
diff --git a/src/libs/utils/wizardpage.h b/src/libs/utils/wizardpage.h
index 76d3039a67..1ab23f92b5 100644
--- a/src/libs/utils/wizardpage.h
+++ b/src/libs/utils/wizardpage.h
@@ -74,6 +74,9 @@ public:
void registerFieldWithName(const QString &name, QWidget *widget,
const char *property = nullptr, const char *changedSignal = nullptr);
+ void setSkipForSubprojects(bool skip) { m_skipForSubproject = skip; }
+ bool skipForSubprojects() const { return m_skipForSubproject; }
+
virtual bool handleReject();
virtual bool handleAccept();
@@ -85,6 +88,7 @@ private:
void registerFieldName(const QString &name);
QSet<QString> m_toRegister;
+ bool m_skipForSubproject = false;
};
} // namespace Utils