Merge remote-tracking branch 'origin/11.0'

Change-Id: I8be5a00000a6699346ed1c44c0711d1f017462a7
This commit is contained in:
Eike Ziller
2023-06-27 15:20:18 +02:00
20 changed files with 531 additions and 91 deletions

View File

@@ -70,4 +70,9 @@ macro.see = "\\sa"
macro.function = "\\fn" macro.function = "\\fn"
navigation.landingpage = "$IDE_DISPLAY_NAME Manual" navigation.landingpage = "$IDE_DISPLAY_NAME Manual"
# Auto-generate navigation linking based on "All Topics":
navigation.toctitles = "All Topics"
navigation.toctitles.inclusive = false
buildversion = "$IDE_DISPLAY_NAME Manual $QTC_VERSION" buildversion = "$IDE_DISPLAY_NAME Manual $QTC_VERSION"

View File

@@ -2003,10 +2003,20 @@ bool Check::visit(TypeOfExpression *ast)
/// ### Maybe put this into the context as a helper function. /// ### Maybe put this into the context as a helper function.
const Value *Check::checkScopeObjectMember(const UiQualifiedId *id) const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
{ {
if (!_importsOk) if (!_importsOk)
return nullptr; return nullptr;
if (!id)
return nullptr; // ### error?
if (id->name.isEmpty()) // possible after error recovery
return nullptr;
QString propertyName = id->name.toString();
if (propertyName == "id" && !id->next)
return nullptr; // ### should probably be a special value
QList<const ObjectValue *> scopeObjects = _scopeChain.qmlScopeObjects(); QList<const ObjectValue *> scopeObjects = _scopeChain.qmlScopeObjects();
if (scopeObjects.isEmpty()) if (scopeObjects.isEmpty())
return nullptr; return nullptr;
@@ -2021,24 +2031,9 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
return isAttachedProperty; return isAttachedProperty;
}; };
if (! id)
return nullptr; // ### error?
if (id->name.isEmpty()) // possible after error recovery
return nullptr;
QString propertyName = id->name.toString();
if (propertyName == "id" && !id->next)
return nullptr; // ### should probably be a special value
// attached properties // attached properties
bool isAttachedProperty = getAttachedTypes(propertyName); bool isAttachedProperty = getAttachedTypes(propertyName);
if (scopeObjects.isEmpty())
return nullptr;
// global lookup for first part of id // global lookup for first part of id
const Value *value = nullptr; const Value *value = nullptr;
for (int i = scopeObjects.size() - 1; i >= 0; --i) { for (int i = scopeObjects.size() - 1; i >= 0; --i) {
@@ -2053,6 +2048,13 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
return nullptr; return nullptr;
if (!value) { if (!value) {
// We omit M16 messages if the type using ImmediateProperties
// Ideally, we should obtain them through metaobject information
const bool omitMessage = !m_typeStack.isEmpty()
&& ((m_typeStack.last() == "PropertyChanges")
|| m_typeStack.last() == "Binding")
&& !m_idStack.isEmpty() && m_idStack.last().contains(propertyName);
if (!omitMessage)
addMessage(ErrInvalidPropertyName, id->identifierToken, propertyName); addMessage(ErrInvalidPropertyName, id->identifierToken, propertyName);
return nullptr; return nullptr;
} }

View File

@@ -82,7 +82,7 @@ public:
"is not reachable in the running tree. " "is not reachable in the running tree. "
"It is possible that no barrier was added to the tree, " "It is possible that no barrier was added to the tree, "
"or the storage is not reachable from where it is referenced. " "or the storage is not reachable from where it is referenced. "
"The WaitForBarrier task will finish with error. "); "The WaitForBarrier task finishes with an error. ");
return SetupResult::StopWithError; return SetupResult::StopWithError;
} }
Barrier *activeSharedBarrier = activeBarrier->barrier(); Barrier *activeSharedBarrier = activeBarrier->barrier();

View File

@@ -138,6 +138,369 @@ private:
Returns the const pointer to the associated \c Task instance. Returns the const pointer to the associated \c Task instance.
*/ */
/*!
\class Tasking::GroupItem
\inheaderfile solutions/tasking/tasktree.h
\inmodule TaskingSolution
\brief GroupItem represents the basic element that may be a part of any
\l {Tasking::Group} {Group}.
GroupItem is a basic element that may be a part of any \l {Tasking::Group} {Group}.
It encapsulates the functionality provided by any GroupItem's subclass.
It is a value type and it is safe to copy the GroupItem instance,
even when it is originally created via the subclass' constructor.
There are four main kinds of GroupItem:
\table
\header
\li GroupItem Kind
\li Brief Description
\row
\li \l CustomTask
\li Defines asynchronous task type and task's start, done, and error handlers.
Aliased with a unique task name, such as, \c ConcurrentCallTask<ResultType>
or \l NetworkQueryTask. Asynchronous tasks are the main reason for using a task tree.
\row
\li \l Group
\li A container for other group items. Since the group is of the GroupItem type,
it's possible to nest it inside another group. The group is seen by its parent
as a single asynchronous task.
\row
\li \l Storage
\li Enables the child tasks of a group to exchange data.
When Storage is placed inside a group, the task tree instantiates
the storage object just before the group is entered,
and destroys it just after the group is finished.
\row
\li Other group control items
\li The items returned by \l {Tasking::parallelLimit()} {parallelLimit()} or
\l {Tasking::workflowPolicy()} {workflowPolicy()} influence the group's behavior.
The items returned by \l {Tasking::onGroupSetup()} {onGroupSetup()},
\l {Tasking::onGroupDone()} {onGroupDone()} or
\l {Tasking::onGroupError()} {onGroupError()} define custom handlers called when
the group starts or ends execution.
\endtable
*/
/*!
\class Tasking::Group
\inheaderfile solutions/tasking/tasktree.h
\inmodule TaskingSolution
\brief Group represents the basic element for composing declarative recipes describing
how to execute and handle a nested tree of asynchronous tasks.
Group is a container for other group items. It encloses child tasks into one unit,
which is seen by the group's parent as a single, asynchronous task.
Since Group is of the GroupItem type, it may also be a child of Group.
Insert child tasks into the group by using aliased custom task names, such as,
\c ConcurrentCallTask<ResultType> or \c NetworkQueryTask:
\code
const Group group {
NetworkQueryTask(...),
ConcurrentCallTask<int>(...)
};
\endcode
The group's behavior may be customized by inserting the items returned by
\l {Tasking::parallelLimit()} {parallelLimit()} or
\l {Tasking::workflowPolicy()} {workflowPolicy()} functions:
\code
const Group group {
parallel,
continueOnError,
NetworkQueryTask(...),
NetworkQueryTask(...)
};
\endcode
The group may contain nested groups:
\code
const Group group {
finishAllAndDone,
NetworkQueryTask(...),
Group {
NetworkQueryTask(...),
Group {
parallel,
NetworkQueryTask(...),
NetworkQueryTask(...),
}
ConcurrentCallTask<QString>(...)
}
};
\endcode
The group may dynamically instantiate a custom storage structure, which may be used for
inter-task data exchange:
\code
struct MyCustomStruct { QByteArray data; };
TreeStorage<MyCustomStruct> storage;
const auto onFirstSetup = [](NetworkQuery &task) { ... };
const auto onFirstDone = [storage](const NetworkQuery &task) {
// storage-> gives a pointer to MyCustomStruct instance,
// created dynamically by the running task tree.
storage->data = task.reply()->readAll();
};
const auto onSecondSetup = [storage](ConcurrentCall<QImage> &task) {
// storage-> gives a pointer to MyCustomStruct. Since the group is sequential,
// the stored MyCustomStruct was already updated inside the onFirstDone handler.
const QByteArray storedData = storage->data;
};
const Group group {
// When the group is entered by a running task tree, it creates MyCustomStruct
// instance dynamically. It is later accessible from all handlers via
// the *storage or storage-> operators.
sequential,
Storage(storage),
NetworkQueryTask(onFirstSetup, onFirstDone),
ConcurrentCallTask<QImage>(onSecondSetup)
};
\endcode
*/
/*!
\fn Group::Group(const QList<GroupItem> &children)
Constructs a group with a given list of \a children.
This constructor is useful when the child items of the group are not known at compile time,
but later, at runtime:
\code
const QStringList sourceList = ...;
QList<GroupItem> groupItems { parallel };
for (const QString &source : sourceList) {
const NetworkQueryTask task(...); // use source for setup handler
groupItems << task;
}
const Group group(groupItems);
\endcode
*/
/*!
\fn Group::Group(std::initializer_list<GroupItem> children)
Constructs a group from std::initializer_list given by \a children.
This constructor is useful when all child items of the group are known at compile time:
\code
const Group group {
finishAllAndDone,
NetworkQueryTask(...),
Group {
NetworkQueryTask(...),
Group {
parallel,
NetworkQueryTask(...),
NetworkQueryTask(...),
}
ConcurrentCallTask<QString>(...)
}
};
\endcode
*/
/*!
\fn GroupItem Group::withTimeout(std::chrono::milliseconds timeout, const GroupEndHandler &handler) const
Attaches \c TimeoutTask to a copy of \c this group, elapsing after \a timeout in milliseconds,
with an optionally provided timeout \a handler, and returns the coupled item.
When the group finishes before \a timeout passes,
the returned item finishes immediately with the group's result.
Otherwise, the \a handler is invoked (if provided), the group is stopped,
and the returned item finishes with an error.
*/
/*!
\class Tasking::CustomTask
\inheaderfile solutions/tasking/tasktree.h
\inmodule TaskingSolution
\brief A class template used for declaring task items and defining their setup,
done, and error handlers.
The CustomTask class template is used inside TaskTree for describing custom task items.
Custom task names are aliased with unique names inside the \l Tasking namespace
via the TASKING_DECLARE_TASK or TASKING_DECLARE_TEMPLATE_TASK macros.
For example, \c ConcurrentCallTask<T> is an alias to the CustomTask that is defined
to work with \c ConcurrentCall<T> as an associated task class.
The following table contains all the built-in tasks and their associated task classes:
\table
\header
\li Aliased Task Name (Tasking Namespace)
\li Associated Task Class
\li Brief Description
\row
\li ConcurrentCallTask<ReturnType>
\li ConcurrentCall<ReturnType>
\li Starts an asynchronous task. Runs in a separate thread.
\row
\li NetworkQueryTask
\li NetworkQuery
\li Sends a network query.
\row
\li TaskTreeTask
\li TaskTree
\li Starts a nested task tree.
\row
\li TimeoutTask
\li \c std::chrono::milliseconds
\li Starts a timer.
\row
\li WaitForBarrierTask
\li MultiBarrier<Limit>
\li Starts an asynchronous task waiting for the barrier to pass.
\endtable
*/
/*!
\typealias CustomTask::Task
Type alias for \c Adapter::Type.
This is the associated task's type.
*/
/*!
\typealias CustomTask::EndHandler
Type alias for \c std::function<void(const Task &)>.
*/
/*!
\fn template <typename Adapter> template <typename SetupHandler> CustomTask<Adapter>::CustomTask<Adapter>(SetupHandler &&setup, const EndHandler &done, const EndHandler &error)
Constructs the CustomTask instance and attaches the \a setup, \a done, and \a error
handlers to the task. When the running task tree is about to start the task,
it instantiates the associated \l Task object, invokes \a setup handler with a \e reference
to the created task, and starts it. When the running task finishes with success or an error,
the task tree invokes \a done or \a error handler, respectively,
with a \e {const reference} to the created task.
The passed \a setup handler is either of the \c std::function<SetupResult(Task &)> or
\c std::function<void(Task &)> type. For example:
\code
static void parseAndLog(const QString &input);
...
const QString input = ...;
const auto onFirstSetup = [input](ConcurrentCall<void> &task) {
if (input == "Skip")
return SetupResult::StopWithDone; // This task won't start, the next one will
if (input == "Error")
return SetupResult::StopWithError; // This task and the next one won't start
task.setConcurrentCallData(parseAndLog, input);
// This task will start, and the next one will start after this one finished with success
return SetupResult::Continue;
};
const auto onSecondSetup = [input](ConcurrentCall<void> &task) {
task.setConcurrentCallData(parseAndLog, input);
};
const Group group {
ConcurrentCallTask<void>(onFirstSetup),
ConcurrentCallTask<void>(onSecondSetup)
};
\endcode
When the passed \a setup handler is of the \c std::function<SetupResult(Task &)> type,
the return value of the handler instructs the running tree on how to proceed after
the handler's invocation is finished. The default return value of SetupResult::Continue
instructs the tree to continue running, i.e. to execute the associated \c Task.
The return value of SetupResult::StopWithDone or SetupResult::StopWithError instructs
the tree to skip the task's execution and finish immediately with success or an error,
respectively.
When the return type is either SetupResult::StopWithDone or SetupResult::StopWithError,
the task's \a done or \a error handler (even if provided) are not called afterwards.
The \a setup handler may be of a shortened form of std::function<void(Task &)>,
i.e. the return value is void. In this case it's assumed that the return value is
SetupResult::Continue by default.
When the running task finishes, one of \a done or \a error handlers is called,
depending on whether it finished with success or an error, respectively.
Both handlers are of std::function<void(const Task &)> type.
\sa onSetup(), onDone(), onError()
*/
/*!
\fn template <typename Adapter> template <typename SetupHandler> CustomTask<Adapter> &CustomTask<Adapter>::onSetup(SetupHandler &&handler)
Attaches the setup \a handler to \c this task.
The \a handler is invoked when the task is about to be started.
This function enables defining the task's details with a
\l {https://en.wikipedia.org/wiki/Fluent_interface}{fluent interface} style:
\code
const auto onQuerySetup = [](NetworkQuery &task) { ... };
const auto onQueryError = [](const NetworkQuery &task) { ... };
const Group group {
NetworkQueryTask(onQuerySetup, {}, onQueryError),
NetworkQueryTask().onSetup(onQuerySetup).onError(onQueryError), // fluent interface style
NetworkQueryTask(onQuerySetup, {}, onQueryError).withTimeout(500ms)
}
\endcode
\sa CustomTask(), onDone(), onError()
*/
/*!
\fn template <typename Adapter> CustomTask<Adapter> &CustomTask<Adapter>::onDone(const EndHandler &handler)
Attaches the done \a handler to \c this task.
The handler is invoked when the task finishes with success.
This function enables defining the task's details with a fluent interface style.
\sa CustomTask(), onSetup(), onError()
*/
/*!
\fn template <typename Adapter> CustomTask<Adapter> &CustomTask<Adapter>::onError(const EndHandler &handler)
Attaches the error \a handler to \c this task.
The handler is invoked when the task finishes with an error.
This function enables defining the task's details with a fluent interface style.
\sa CustomTask(), onSetup(), onDone()
*/
/*!
\fn template <typename Adapter> GroupItem CustomTask<Adapter>::withTimeout(std::chrono::milliseconds timeout, const GroupItem::GroupEndHandler &handler) const
Attaches \c TimeoutTask to a copy of \c this task, elapsing after \a timeout in milliseconds,
with an optionally provided timeout \a handler, and returns the coupled item.
When the task finishes before \a timeout passes,
the returned item finishes immediately with the task's result.
Otherwise, the \a handler is invoked (if provided), the task is stopped,
and the returned item finishes with an error.
\sa onSetup()
*/
/*! /*!
\macro TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass) \macro TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass)
\relates Tasking \relates Tasking
@@ -158,13 +521,6 @@ private:
For more information on implementing the custom task adapters, refer to \l {Task Adapters}. For more information on implementing the custom task adapters, refer to \l {Task Adapters}.
*/ */
/*!
\class Tasking::GroupItem
\inheaderfile solutions/tasking/tasktree.h
\inmodule TaskingSolution
\brief The GroupItem class represents the basic element for composing nested tree structures.
*/
/*! /*!
\enum Tasking::WorkflowPolicy \enum Tasking::WorkflowPolicy

View File

@@ -321,14 +321,14 @@ public:
using EndHandler = std::function<void(const Task &)>; using EndHandler = std::function<void(const Task &)>;
static Adapter *createAdapter() { return new Adapter; } static Adapter *createAdapter() { return new Adapter; }
CustomTask() : GroupItem({&createAdapter}) {} CustomTask() : GroupItem({&createAdapter}) {}
template <typename SetupFunction> template <typename SetupHandler>
CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {}) CustomTask(SetupHandler &&setup, const EndHandler &done = {}, const EndHandler &error = {})
: GroupItem({&createAdapter, wrapSetup(std::forward<SetupFunction>(function)), : GroupItem({&createAdapter, wrapSetup(std::forward<SetupHandler>(setup)),
wrapEnd(done), wrapEnd(error)}) {} wrapEnd(done), wrapEnd(error)}) {}
template <typename SetupFunction> template <typename SetupHandler>
CustomTask &onSetup(SetupFunction &&function) { CustomTask &onSetup(SetupHandler &&handler) {
setTaskSetupHandler(wrapSetup(std::forward<SetupFunction>(function))); setTaskSetupHandler(wrapSetup(std::forward<SetupHandler>(handler)));
return *this; return *this;
} }
CustomTask &onDone(const EndHandler &handler) { CustomTask &onDone(const EndHandler &handler) {
@@ -346,20 +346,20 @@ public:
} }
private: private:
template<typename SetupFunction> template<typename SetupHandler>
static GroupItem::TaskSetupHandler wrapSetup(SetupFunction &&function) { static GroupItem::TaskSetupHandler wrapSetup(SetupHandler &&handler) {
static constexpr bool isDynamic = std::is_same_v<SetupResult, static constexpr bool isDynamic = std::is_same_v<SetupResult,
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>; std::invoke_result_t<std::decay_t<SetupHandler>, typename Adapter::Type &>>;
constexpr bool isVoid = std::is_same_v<void, constexpr bool isVoid = std::is_same_v<void,
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>; std::invoke_result_t<std::decay_t<SetupHandler>, typename Adapter::Type &>>;
static_assert(isDynamic || isVoid, static_assert(isDynamic || isVoid,
"Task setup handler needs to take (Task &) as an argument and has to return " "Task setup handler needs to take (Task &) as an argument and has to return "
"void or SetupResult. The passed handler doesn't fulfill these requirements."); "void or SetupResult. The passed handler doesn't fulfill these requirements.");
return [=](TaskInterface &taskInterface) { return [=](TaskInterface &taskInterface) {
Adapter &adapter = static_cast<Adapter &>(taskInterface); Adapter &adapter = static_cast<Adapter &>(taskInterface);
if constexpr (isDynamic) if constexpr (isDynamic)
return std::invoke(function, *adapter.task()); return std::invoke(handler, *adapter.task());
std::invoke(function, *adapter.task()); std::invoke(handler, *adapter.task());
return SetupResult::Continue; return SetupResult::Continue;
}; };
}; };

View File

@@ -66,22 +66,22 @@ BoostTestSettings::BoostTestSettings(Id settingsId)
seed.setEnabler(&randomize); seed.setEnabler(&randomize);
randomize.setSettingsKey("Randomize"); randomize.setSettingsKey("Randomize");
randomize.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); randomize.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
randomize.setLabelText(Tr::tr("Randomize")); randomize.setLabelText(Tr::tr("Randomize"));
randomize.setToolTip(Tr::tr("Randomize execution order.")); randomize.setToolTip(Tr::tr("Randomize execution order."));
systemErrors.setSettingsKey("SystemErrors"); systemErrors.setSettingsKey("SystemErrors");
systemErrors.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); systemErrors.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
systemErrors.setLabelText(Tr::tr("Catch system errors")); systemErrors.setLabelText(Tr::tr("Catch system errors"));
systemErrors.setToolTip(Tr::tr("Catch or ignore system errors.")); systemErrors.setToolTip(Tr::tr("Catch or ignore system errors."));
fpExceptions.setSettingsKey("FPExceptions"); fpExceptions.setSettingsKey("FPExceptions");
fpExceptions.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); fpExceptions.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
fpExceptions.setLabelText(Tr::tr("Floating point exceptions")); fpExceptions.setLabelText(Tr::tr("Floating point exceptions"));
fpExceptions.setToolTip(Tr::tr("Enable floating point exception traps.")); fpExceptions.setToolTip(Tr::tr("Enable floating point exception traps."));
memLeaks.setSettingsKey("MemoryLeaks"); memLeaks.setSettingsKey("MemoryLeaks");
memLeaks.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBox); memLeaks.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
memLeaks.setDefaultValue(true); memLeaks.setDefaultValue(true);
memLeaks.setLabelText(Tr::tr("Detect memory leaks")); memLeaks.setLabelText(Tr::tr("Detect memory leaks"));
memLeaks.setToolTip(Tr::tr("Enable memory leak detection.")); memLeaks.setToolTip(Tr::tr("Enable memory leak detection."));

View File

@@ -44,6 +44,7 @@ CTestSettings::CTestSettings(Id settingsId)
outputOnFail.setSettingsKey("OutputOnFail"); outputOnFail.setSettingsKey("OutputOnFail");
outputOnFail.setLabelText(Tr::tr("Output on failure")); outputOnFail.setLabelText(Tr::tr("Output on failure"));
outputOnFail.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
outputOnFail.setDefaultValue(true); outputOnFail.setDefaultValue(true);
outputMode.setSettingsKey("OutputMode"); outputMode.setSettingsKey("OutputMode");
@@ -70,9 +71,11 @@ CTestSettings::CTestSettings(Id settingsId)
scheduleRandom.setSettingsKey("ScheduleRandom"); scheduleRandom.setSettingsKey("ScheduleRandom");
scheduleRandom.setLabelText(Tr::tr("Schedule random")); scheduleRandom.setLabelText(Tr::tr("Schedule random"));
scheduleRandom.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
stopOnFailure.setSettingsKey("StopOnFail"); stopOnFailure.setSettingsKey("StopOnFail");
stopOnFailure.setLabelText(Tr::tr("Stop on failure")); stopOnFailure.setLabelText(Tr::tr("Stop on failure"));
stopOnFailure.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
parallel.setSettingsKey("Parallel"); parallel.setSettingsKey("Parallel");
parallel.setToolTip(Tr::tr("Run tests in parallel mode using given number of jobs.")); parallel.setToolTip(Tr::tr("Run tests in parallel mode using given number of jobs."));

View File

@@ -26,6 +26,7 @@ GTestSettings::GTestSettings(Id settingsId)
setLayouter([this] { setLayouter([this] {
return Row { Form { return Row { Form {
runDisabled, br, runDisabled, br,
throwOnFailure, br,
breakOnFailure, br, breakOnFailure, br,
repeat, iterations, br, repeat, iterations, br,
shuffle, seed, br, shuffle, seed, br,
@@ -49,24 +50,29 @@ GTestSettings::GTestSettings(Id settingsId)
runDisabled.setSettingsKey("RunDisabled"); runDisabled.setSettingsKey("RunDisabled");
runDisabled.setLabelText(Tr::tr("Run disabled tests")); runDisabled.setLabelText(Tr::tr("Run disabled tests"));
runDisabled.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
runDisabled.setToolTip(Tr::tr("Executes disabled tests when performing a test run.")); runDisabled.setToolTip(Tr::tr("Executes disabled tests when performing a test run."));
shuffle.setSettingsKey("Shuffle"); shuffle.setSettingsKey("Shuffle");
shuffle.setLabelText(Tr::tr("Shuffle tests")); shuffle.setLabelText(Tr::tr("Shuffle tests"));
shuffle.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
shuffle.setToolTip(Tr::tr("Shuffles tests automatically on every iteration by the given seed.")); shuffle.setToolTip(Tr::tr("Shuffles tests automatically on every iteration by the given seed."));
repeat.setSettingsKey("Repeat"); repeat.setSettingsKey("Repeat");
repeat.setLabelText(Tr::tr("Repeat tests")); repeat.setLabelText(Tr::tr("Repeat tests"));
repeat.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
repeat.setToolTip(Tr::tr("Repeats a test run (you might be required to increase the timeout to " repeat.setToolTip(Tr::tr("Repeats a test run (you might be required to increase the timeout to "
"avoid canceling the tests).")); "avoid canceling the tests)."));
throwOnFailure.setSettingsKey("ThrowOnFailure"); throwOnFailure.setSettingsKey("ThrowOnFailure");
throwOnFailure.setLabelText(Tr::tr("Throw on failure")); throwOnFailure.setLabelText(Tr::tr("Throw on failure"));
throwOnFailure.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
throwOnFailure.setToolTip(Tr::tr("Turns assertion failures into C++ exceptions.")); throwOnFailure.setToolTip(Tr::tr("Turns assertion failures into C++ exceptions."));
breakOnFailure.setSettingsKey("BreakOnFailure"); breakOnFailure.setSettingsKey("BreakOnFailure");
breakOnFailure.setDefaultValue(true); breakOnFailure.setDefaultValue(true);
breakOnFailure.setLabelText(Tr::tr("Break on failure while debugging")); breakOnFailure.setLabelText(Tr::tr("Break on failure while debugging"));
breakOnFailure.setLabelPlacement(BoolAspect::LabelPlacement::AtCheckBoxWithoutDummyLabel);
breakOnFailure.setToolTip(Tr::tr("Turns failures into debugger breakpoints.")); breakOnFailure.setToolTip(Tr::tr("Turns failures into debugger breakpoints."));
groupMode.setSettingsKey("GroupMode"); groupMode.setSettingsKey("GroupMode");

View File

@@ -296,9 +296,8 @@ void TestResultModel::addTestResult(const TestResult &testResult, bool autoExpan
if (parentItem) { if (parentItem) {
parentItem->appendChild(newItem); parentItem->appendChild(newItem);
if (autoExpand) { if (autoExpand) {
parentItem->expand(); QMetaObject::invokeMethod(this, [parentItem]{ parentItem->expand(); },
newItem->expand(); Qt::QueuedConnection);
newItem->forAllChildren([](TreeItem *it) { it->expand(); });
} }
updateParent(newItem); updateParent(newItem);
} else { } else {

View File

@@ -142,7 +142,7 @@ TestResultsPane::TestResultsPane(QObject *parent) :
onCopyItemTriggered(getTestResult(m_treeView->currentIndex())); onCopyItemTriggered(getTestResult(m_treeView->currentIndex()));
}); });
connect(m_model, &TestResultModel::requestExpansion, this, [this](const QModelIndex &idx) { connect(m_model, &TestResultModel::requestExpansion, this, [this](const QModelIndex &idx) {
m_treeView->expand(m_filterModel->mapFromSource(idx)); m_treeView->expandRecursively(m_filterModel->mapFromSource(idx));
}); });
connect(TestRunner::instance(), &TestRunner::testRunStarted, connect(TestRunner::instance(), &TestRunner::testRunStarted,
this, &TestResultsPane::onTestRunStarted); this, &TestResultsPane::onTestRunStarted);

View File

@@ -152,14 +152,17 @@ CppEditor::CppCodeStyleSettings ClangFormatFile::toCppCodeStyleSettings(
// to be false // to be false
settings.indentAccessSpecifiers = (style.AccessModifierOffset != -1 * int(style.IndentWidth)); settings.indentAccessSpecifiers = (style.AccessModifierOffset != -1 * int(style.IndentWidth));
settings.indentNamespaceBody = style.NamespaceIndentation if (style.NamespaceIndentation == FormatStyle::NamespaceIndentationKind::NI_All) {
== FormatStyle::NamespaceIndentationKind::NI_All; settings.indentNamespaceBody = true;
settings.indentNamespaceBraces = settings.indentNamespaceBody; settings.indentNamespaceBraces = settings.indentNamespaceBody;
}
settings.indentClassBraces = style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths; if (style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths) {
settings.indentClassBraces = true;
settings.indentEnumBraces = settings.indentClassBraces; settings.indentEnumBraces = settings.indentClassBraces;
settings.indentBlockBraces = settings.indentClassBraces; settings.indentBlockBraces = settings.indentClassBraces;
settings.indentFunctionBraces = settings.indentClassBraces; settings.indentFunctionBraces = settings.indentClassBraces;
}
settings.indentSwitchLabels = style.IndentCaseLabels; settings.indentSwitchLabels = style.IndentCaseLabels;
#if LLVM_VERSION_MAJOR >= 11 #if LLVM_VERSION_MAJOR >= 11

View File

@@ -207,13 +207,13 @@ void fromCppCodeStyleSettings(clang::format::FormatStyle &style,
else else
style.AccessModifierOffset = -1 * style.IndentWidth; style.AccessModifierOffset = -1 * style.IndentWidth;
if (settings.indentNamespaceBody || settings.indentNamespaceBraces) if (settings.indentNamespaceBody && settings.indentNamespaceBraces)
style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_All; style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_All;
else else
style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_None; style.NamespaceIndentation = FormatStyle::NamespaceIndentationKind::NI_None;
if (settings.indentClassBraces || settings.indentEnumBraces || settings.indentBlockBraces if (settings.indentClassBraces && settings.indentEnumBraces && settings.indentBlockBraces
|| settings.indentFunctionBraces) && settings.indentFunctionBraces)
style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths; style.BreakBeforeBraces = FormatStyle::BS_Whitesmiths;
else else
style.BreakBeforeBraces = FormatStyle::BS_Custom; style.BreakBeforeBraces = FormatStyle::BS_Custom;

View File

@@ -64,9 +64,8 @@ static QString appendYamlSuffix(const char *filePathFragment)
void ReadExportedDiagnosticsTest::testTidy() void ReadExportedDiagnosticsTest::testTidy()
{ {
const FilePath sourceFile = filePath("tidy.modernize-use-nullptr.cpp"); const FilePath sourceFile = filePath("tidy.modernize-use-nullptr.cpp");
const FilePath exportedFile = createFile( const FilePath exportedFile
filePath(appendYamlSuffix("tidy.modernize-use-nullptr")).toString(), = createFile(filePath(appendYamlSuffix("tidy.modernize-use-nullptr")), sourceFile);
sourceFile.toString());
Diagnostic expectedDiag; Diagnostic expectedDiag;
expectedDiag.name = "modernize-use-nullptr"; expectedDiag.name = "modernize-use-nullptr";
expectedDiag.location = {sourceFile, 2, 25}; expectedDiag.location = {sourceFile, 2, 25};
@@ -86,8 +85,8 @@ void ReadExportedDiagnosticsTest::testTidy()
void ReadExportedDiagnosticsTest::testAcceptDiagsFromFilePaths_None() void ReadExportedDiagnosticsTest::testAcceptDiagsFromFilePaths_None()
{ {
const QString sourceFile = filePath("tidy.modernize-use-nullptr.cpp").toString(); const FilePath sourceFile = filePath("tidy.modernize-use-nullptr.cpp");
const FilePath exportedFile = createFile(filePath("tidy.modernize-use-nullptr.yaml").toString(), const FilePath exportedFile = createFile(filePath("tidy.modernize-use-nullptr.yaml"),
sourceFile); sourceFile);
const auto acceptNone = [](const FilePath &) { return false; }; const auto acceptNone = [](const FilePath &) { return false; };
const expected_str<Diagnostics> diags const expected_str<Diagnostics> diags
@@ -101,8 +100,7 @@ void ReadExportedDiagnosticsTest::testTidy_ClangAnalyzer()
{ {
const FilePath sourceFile = filePath("clang-analyzer.dividezero.cpp"); const FilePath sourceFile = filePath("clang-analyzer.dividezero.cpp");
const FilePath exportedFile const FilePath exportedFile
= createFile(filePath(appendYamlSuffix("clang-analyzer.dividezero")).toString(), = createFile(filePath(appendYamlSuffix("clang-analyzer.dividezero")), sourceFile);
sourceFile.toString());
Diagnostic expectedDiag; Diagnostic expectedDiag;
expectedDiag.name = "clang-analyzer-core.DivideZero"; expectedDiag.name = "clang-analyzer-core.DivideZero";
expectedDiag.location = {sourceFile, 4, 15}; expectedDiag.location = {sourceFile, 4, 15};
@@ -134,8 +132,8 @@ void ReadExportedDiagnosticsTest::testTidy_ClangAnalyzer()
void ReadExportedDiagnosticsTest::testClazy() void ReadExportedDiagnosticsTest::testClazy()
{ {
const FilePath sourceFile = filePath("clazy.qgetenv.cpp"); const FilePath sourceFile = filePath("clazy.qgetenv.cpp");
const FilePath exportedFile = createFile(filePath(appendYamlSuffix("clazy.qgetenv")).toString(), const FilePath exportedFile = createFile(filePath(appendYamlSuffix("clazy.qgetenv")),
sourceFile.toString()); sourceFile);
Diagnostic expectedDiag; Diagnostic expectedDiag;
expectedDiag.name = "clazy-qgetenv"; expectedDiag.name = "clazy-qgetenv";
expectedDiag.location = {sourceFile, 7, 5}; expectedDiag.location = {sourceFile, 7, 5};
@@ -259,17 +257,16 @@ void ReadExportedDiagnosticsTest::testOffsetMultiByteCodePoint2()
} }
// Replace FILE_PATH with a real absolute file path in the *.yaml files. // Replace FILE_PATH with a real absolute file path in the *.yaml files.
FilePath ReadExportedDiagnosticsTest::createFile(const QString &yamlFilePath, FilePath ReadExportedDiagnosticsTest::createFile(const Utils::FilePath &yamlFilePath,
const QString &filePathToInject) const const Utils::FilePath &filePathToInject) const
{ {
QTC_ASSERT(QDir::isAbsolutePath(filePathToInject), return {}); QTC_ASSERT(filePathToInject.isAbsolutePath(), return {});
const FilePath newFileName = m_baseDir->absolutePath(QFileInfo(yamlFilePath).fileName()); const FilePath newFileName = m_baseDir->filePath().resolvePath(yamlFilePath);
FileReader reader; FileReader reader;
if (QTC_GUARD(reader.fetch(FilePath::fromString(yamlFilePath), if (QTC_GUARD(reader.fetch(yamlFilePath, QIODevice::ReadOnly | QIODevice::Text))) {
QIODevice::ReadOnly | QIODevice::Text))) {
QByteArray contents = reader.data(); QByteArray contents = reader.data();
contents.replace("FILE_PATH", filePathToInject.toLocal8Bit()); contents.replace("FILE_PATH", filePathToInject.toString().toLocal8Bit());
FileSaver fileSaver(newFileName, QIODevice::WriteOnly | QIODevice::Text); FileSaver fileSaver(newFileName, QIODevice::WriteOnly | QIODevice::Text);
QTC_CHECK(fileSaver.write(contents)); QTC_CHECK(fileSaver.write(contents));

View File

@@ -44,7 +44,8 @@ private slots:
void testOffsetMultiByteCodePoint2(); void testOffsetMultiByteCodePoint2();
private: private:
Utils::FilePath createFile(const QString &yamlFilePath, const QString &filePathToInject) const; Utils::FilePath createFile(const Utils::FilePath &yamlFilePath,
const Utils::FilePath &filePathToInject) const;
Utils::FilePath filePath(const QString &fileName) const; Utils::FilePath filePath(const QString &fileName) const;
CppEditor::Tests::TemporaryCopiedDir * const m_baseDir; CppEditor::Tests::TemporaryCopiedDir * const m_baseDir;

View File

@@ -298,10 +298,6 @@ ProjectWizardPage::ProjectWizardPage(QWidget *parent)
scrollArea, scrollArea,
}.attachTo(this); }.attachTo(this);
connect(m_projectComboBox, &QComboBox::currentIndexChanged,
this, &ProjectWizardPage::projectChanged);
connect(m_addToVersionControlComboBox, &QComboBox::currentIndexChanged,
this, &ProjectWizardPage::versionControlChanged);
connect(m_vcsManageButton, &QAbstractButton::clicked, this, &ProjectWizardPage::manageVcs); connect(m_vcsManageButton, &QAbstractButton::clicked, this, &ProjectWizardPage::manageVcs);
setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Summary")); setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Summary"));
@@ -376,6 +372,7 @@ void ProjectWizardPage::initializeVersionControls()
// 2) Directory is managed and VCS does not support "Add" -> None available // 2) Directory is managed and VCS does not support "Add" -> None available
// 3) Directory is not managed -> Offer all VCS that support "CreateRepository" // 3) Directory is not managed -> Offer all VCS that support "CreateRepository"
m_addToVersionControlComboBox->disconnect();
QList<IVersionControl *> versionControls = VcsManager::versionControls(); QList<IVersionControl *> versionControls = VcsManager::versionControls();
if (versionControls.isEmpty()) if (versionControls.isEmpty())
hideVersionControlUiElements(); hideVersionControlUiElements();
@@ -419,6 +416,9 @@ void ProjectWizardPage::initializeVersionControls()
int newIdx = m_activeVersionControls.indexOf(currentSelection) + 1; int newIdx = m_activeVersionControls.indexOf(currentSelection) + 1;
setVersionControlIndex(newIdx); setVersionControlIndex(newIdx);
} }
connect(m_addToVersionControlComboBox, &QComboBox::currentIndexChanged,
this, &ProjectWizardPage::versionControlChanged);
} }
bool ProjectWizardPage::runVersionControl(const QList<GeneratedFile> &files, QString *errorMessage) bool ProjectWizardPage::runVersionControl(const QList<GeneratedFile> &files, QString *errorMessage)
@@ -457,6 +457,7 @@ void ProjectWizardPage::initializeProjectTree(Node *context, const FilePaths &pa
IWizardFactory::WizardKind kind, IWizardFactory::WizardKind kind,
ProjectAction action) ProjectAction action)
{ {
m_projectComboBox->disconnect();
BestNodeSelector selector(m_commonDirectory, paths); BestNodeSelector selector(m_commonDirectory, paths);
TreeItem *root = m_model.rootItem(); TreeItem *root = m_model.rootItem();
@@ -489,6 +490,8 @@ void ProjectWizardPage::initializeProjectTree(Node *context, const FilePaths &pa
setAddingSubProject(action == AddSubProject); setAddingSubProject(action == AddSubProject);
m_projectComboBox->setEnabled(m_model.rowCount(QModelIndex()) > 1); m_projectComboBox->setEnabled(m_model.rowCount(QModelIndex()) > 1);
connect(m_projectComboBox, &QComboBox::currentIndexChanged,
this, &ProjectWizardPage::projectChanged);
} }
void ProjectWizardPage::setNoneLabel(const QString &label) void ProjectWizardPage::setNoneLabel(const QString &label)

View File

@@ -31,7 +31,7 @@ add_qtc_library(QmlDesignerUtils STATIC
) )
extend_qtc_library(QmlDesignerUtils extend_qtc_library(QmlDesignerUtils
CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
) )
@@ -84,7 +84,7 @@ endif()
extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore
CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
) )
@@ -481,7 +481,7 @@ add_qtc_plugin(QmlDesigner
) )
extend_qtc_plugin(QmlDesigner extend_qtc_plugin(QmlDesigner
CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
) )
@@ -1088,7 +1088,7 @@ add_qtc_plugin(assetexporterplugin
) )
extend_qtc_plugin(assetexporterplugin extend_qtc_plugin(assetexporterplugin
CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
) )
@@ -1121,7 +1121,7 @@ add_qtc_plugin(componentsplugin
) )
extend_qtc_plugin(componentsplugin extend_qtc_plugin(componentsplugin
CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
) )
@@ -1141,7 +1141,7 @@ add_qtc_plugin(qmlpreviewplugin
) )
extend_qtc_plugin(qmlpreviewplugin extend_qtc_plugin(qmlpreviewplugin
CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
) )
@@ -1163,7 +1163,7 @@ add_qtc_plugin(qtquickplugin
) )
extend_qtc_plugin(qtquickplugin extend_qtc_plugin(qtquickplugin
CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
) )

View File

@@ -1,6 +1,12 @@
env_with_default("QDS_DISABLE_COMPILE_WARNING_AS_ERROR" ENV_QDS_DISABLE_COMPILE_WARNING_AS_ERROR OFF) if(BUILD_DESIGNSTUDIO AND ($<CONFIG:Debug> OR WITH_TESTS))
option(DISABLE_COMPILE_WARNING_AS_ERROR "Dont treat warnings as errors" ${ENV_QDS_DISABLE_COMPILE_WARNING_AS_ERROR}) set(ENABLE_COMPILE_WARNING_AS_ERROR_DEFAULT ON)
add_feature_info("Treat warnings as errors" ${DISABLE_COMPILE_WARNING_AS_ERROR} "") else()
set(ENABLE_COMPILE_WARNING_AS_ERROR_DEFAULT OFF)
endif()
env_with_default("QDS_ENABLE_COMPILE_WARNING_AS_ERROR" ENV_ENABLE_COMPILE_WARNING_AS_ERROR
${ENABLE_COMPILE_WARNING_AS_ERROR_DEFAULT})
option(ENABLE_COMPILE_WARNING_AS_ERROR "Treat warnings as errors in QmlDesigner" ${ENV_ENABLE_COMPILE_WARNING_AS_ERROR})
add_feature_info("Treat warnings as errors in QmlDesigner" ${ENABLE_COMPILE_WARNING_AS_ERROR} "")
add_qtc_plugin(QmlDesignerBase add_qtc_plugin(QmlDesignerBase
CONDITION TARGET Qt::QuickWidgets CONDITION TARGET Qt::QuickWidgets
@@ -12,7 +18,7 @@ add_qtc_plugin(QmlDesignerBase
) )
extend_qtc_plugin(QmlDesignerBase extend_qtc_plugin(QmlDesignerBase
CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
) )

View File

@@ -25,7 +25,7 @@ add_qtc_plugin(QmlProjectManager
) )
extend_qtc_plugin(QmlProjectManager extend_qtc_plugin(QmlProjectManager
CONDITION NOT DISABLE_COMPILE_WARNING_AS_ERROR CONDITION ENABLE_COMPILE_WARNING_AS_ERROR
PROPERTIES COMPILE_WARNING_AS_ERROR ON PROPERTIES COMPILE_WARNING_AS_ERROR ON
) )

View File

@@ -301,6 +301,21 @@ expected_str<QList<ExampleItem *>> parseExamples(const QByteArray &manifestData,
return items; return items;
} }
// ordered list of "known" categories
// TODO this should be defined in the manifest
Q_GLOBAL_STATIC_WITH_ARGS(QList<QString>,
defaultOrder,
{QStringList() << "Application Examples"
<< "Desktop"
<< "Mobile"
<< "Embedded"
<< "Graphics"
<< "Input/Output"
<< "Connectivity"
<< "Networking"
<< "Positioning & Location"
<< "Internationalization"});
static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second) static bool sortByHighlightedAndName(ExampleItem *first, ExampleItem *second)
{ {
if (first->isHighlighted && !second->isHighlighted) if (first->isHighlighted && !second->isHighlighted)
@@ -350,15 +365,20 @@ QList<std::pair<Core::Section, QList<ExampleItem *>>> getCategories(const QList<
} else { } else {
// All original items have been copied into a category or other, delete. // All original items have been copied into a category or other, delete.
qDeleteAll(items); qDeleteAll(items);
static const int defaultOrderSize = defaultOrder->size();
int index = 0; int index = 0;
const auto end = categoryMap.constKeyValueEnd(); const auto end = categoryMap.constKeyValueEnd();
for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) { for (auto it = categoryMap.constKeyValueBegin(); it != end; ++it) {
categories.append({{it->first, index, /*maxRows=*/index == 0 ? 2 : 1}, it->second}); // order "known" categories as wanted, others come afterwards
const int defaultIndex = defaultOrder->indexOf(it->first);
const int priority = defaultIndex >= 0 ? defaultIndex : (index + defaultOrderSize);
categories.append({{it->first, priority, /*maxRows=*/index == 0 ? 2 : 1}, it->second});
++index; ++index;
} }
if (!other.isEmpty()) if (!other.isEmpty())
categories.append({{otherDisplayName, index, /*maxRows=*/1}, other}); categories.append({{otherDisplayName, index + defaultOrderSize, /*maxRows=*/1}, other});
} }
const auto end = categories.end(); const auto end = categories.end();
for (auto it = categories.begin(); it != end; ++it) for (auto it = categories.begin(); it != end; ++it)
sort(it->second, sortByHighlightedAndName); sort(it->second, sortByHighlightedAndName);

View File

@@ -0,0 +1,39 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
import QtQuick
import QtQuick.Window
Window {
id: root
width: 640
height: 480
visible: true
title: qsTr("Hello World")
Rectangle {
id: rect
Rectangle {
id: innerRect
}
}
Text {
id: myText
width: 50
wrapMode: Text.WordWrap
text: "a text string that is longer than 50 pixels"
Text {
id: innerText
}
states: State {
name: "widerText"
PropertyChanges { myText.width: undefined }
AnchorChanges { innerRect.width: undefined } // 16 29 37
}
}
Binding {rect.width: innerText.width}
}