| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtQuick module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| 21 | ** packaging of this file. Please review the following information to |
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements |
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| 24 | ** |
| 25 | ** GNU General Public License Usage |
| 26 | ** Alternatively, this file may be used under the terms of the GNU |
| 27 | ** General Public License version 2.0 or (at your option) the GNU General |
| 28 | ** Public license version 3 or any later version approved by the KDE Free |
| 29 | ** Qt Foundation. The licenses are as published by the Free Software |
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| 31 | ** included in the packaging of this file. Please review the following |
| 32 | ** information to ensure the GNU General Public License requirements will |
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
| 35 | ** |
| 36 | ** $QT_END_LICENSE$ |
| 37 | ** |
| 38 | ****************************************************************************/ |
| 39 | |
| 40 | #include "qquickstate_p_p.h" |
| 41 | #include "qquickstate_p.h" |
| 42 | |
| 43 | #include "qquickstategroup_p.h" |
| 44 | #include "qquickstatechangescript_p.h" |
| 45 | |
| 46 | #include <private/qqmlglobal_p.h> |
| 47 | |
| 48 | #include <QtCore/qdebug.h> |
| 49 | |
| 50 | QT_BEGIN_NAMESPACE |
| 51 | |
| 52 | DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); |
| 53 | |
| 54 | QQuickStateAction::QQuickStateAction() |
| 55 | : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(nullptr), event(nullptr), |
| 56 | specifiedObject(nullptr) |
| 57 | { |
| 58 | } |
| 59 | |
| 60 | QQuickStateAction::QQuickStateAction(QObject *target, const QString &propertyName, |
| 61 | const QVariant &value) |
| 62 | : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), |
| 63 | property(target, propertyName, qmlEngine(target)), toValue(value), |
| 64 | fromBinding(nullptr), event(nullptr), |
| 65 | specifiedObject(target), specifiedProperty(propertyName) |
| 66 | { |
| 67 | if (property.isValid()) |
| 68 | fromValue = property.read(); |
| 69 | } |
| 70 | |
| 71 | QQuickStateAction::QQuickStateAction(QObject *target, const QQmlProperty &property, const QString &propertyName, const QVariant &value) |
| 72 | : restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), |
| 73 | property(property), toValue(value), |
| 74 | fromBinding(nullptr), event(nullptr), |
| 75 | specifiedObject(target), specifiedProperty(propertyName) |
| 76 | { |
| 77 | if (property.isValid()) |
| 78 | fromValue = property.read(); |
| 79 | } |
| 80 | |
| 81 | |
| 82 | QQuickStateActionEvent::~QQuickStateActionEvent() |
| 83 | { |
| 84 | } |
| 85 | |
| 86 | void QQuickStateActionEvent::execute() |
| 87 | { |
| 88 | } |
| 89 | |
| 90 | bool QQuickStateActionEvent::isReversable() |
| 91 | { |
| 92 | return false; |
| 93 | } |
| 94 | |
| 95 | void QQuickStateActionEvent::reverse() |
| 96 | { |
| 97 | } |
| 98 | |
| 99 | bool QQuickStateActionEvent::changesBindings() |
| 100 | { |
| 101 | return false; |
| 102 | } |
| 103 | |
| 104 | void QQuickStateActionEvent::clearBindings() |
| 105 | { |
| 106 | } |
| 107 | |
| 108 | bool QQuickStateActionEvent::mayOverride(QQuickStateActionEvent *other) |
| 109 | { |
| 110 | Q_UNUSED(other); |
| 111 | return false; |
| 112 | } |
| 113 | |
| 114 | QQuickStateOperation::QQuickStateOperation(QObjectPrivate &dd, QObject *parent) |
| 115 | : QObject(dd, parent) |
| 116 | { |
| 117 | } |
| 118 | |
| 119 | /*! |
| 120 | \qmltype State |
| 121 | \instantiates QQuickState |
| 122 | \inqmlmodule QtQuick |
| 123 | \ingroup qtquick-states |
| 124 | \brief Defines configurations of objects and properties. |
| 125 | |
| 126 | A \e state is a set of batched changes from the default configuration. |
| 127 | |
| 128 | All items have a default state that defines the default configuration of objects |
| 129 | and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to |
| 130 | allow items to switch between different configurations. These configurations |
| 131 | can, for example, be used to apply different sets of property values or execute |
| 132 | different scripts. |
| 133 | |
| 134 | The following example displays a single \l Rectangle. In the default state, the rectangle |
| 135 | is colored black. In the "clicked" state, a PropertyChanges object changes the |
| 136 | rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state |
| 137 | between the default state and the "clicked" state, thus toggling the color of the |
| 138 | rectangle between black and red. |
| 139 | |
| 140 | \snippet qml/state.qml 0 |
| 141 | |
| 142 | Notice the default state is referred to using an empty string (""). |
| 143 | |
| 144 | States are commonly used together with \l{Animation and Transitions in Qt Quick}{Transitions} to provide |
| 145 | animations when state changes occur. |
| 146 | |
| 147 | \note Setting the state of an object from within another state of the same object is |
| 148 | not allowed. |
| 149 | |
| 150 | \sa {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States}, |
| 151 | {Animation and Transitions in Qt Quick}{Transitions}, {Qt QML} |
| 152 | */ |
| 153 | QQuickState::QQuickState(QObject *parent) |
| 154 | : QObject(*(new QQuickStatePrivate), parent) |
| 155 | { |
| 156 | Q_D(QQuickState); |
| 157 | d->transitionManager.setState(this); |
| 158 | } |
| 159 | |
| 160 | QQuickState::~QQuickState() |
| 161 | { |
| 162 | Q_D(QQuickState); |
| 163 | if (d->group) |
| 164 | d->group->removeState(state: this); |
| 165 | } |
| 166 | |
| 167 | /*! |
| 168 | \qmlproperty string QtQuick::State::name |
| 169 | This property holds the name of the state. |
| 170 | |
| 171 | Each state should have a unique name within its item. |
| 172 | */ |
| 173 | QString QQuickState::name() const |
| 174 | { |
| 175 | Q_D(const QQuickState); |
| 176 | return d->name; |
| 177 | } |
| 178 | |
| 179 | void QQuickState::setName(const QString &n) |
| 180 | { |
| 181 | Q_D(QQuickState); |
| 182 | d->name = n; |
| 183 | d->named = true; |
| 184 | } |
| 185 | |
| 186 | bool QQuickState::isNamed() const |
| 187 | { |
| 188 | Q_D(const QQuickState); |
| 189 | return d->named; |
| 190 | } |
| 191 | |
| 192 | bool QQuickState::isWhenKnown() const |
| 193 | { |
| 194 | Q_D(const QQuickState); |
| 195 | return d->whenKnown; |
| 196 | } |
| 197 | |
| 198 | /*! |
| 199 | \qmlproperty bool QtQuick::State::when |
| 200 | This property holds when the state should be applied. |
| 201 | |
| 202 | This should be set to an expression that evaluates to \c true when you want the state to |
| 203 | be applied. For example, the following \l Rectangle changes in and out of the "hidden" |
| 204 | state when the \l MouseArea is pressed: |
| 205 | |
| 206 | \snippet qml/state-when.qml 0 |
| 207 | |
| 208 | If multiple states in a group have \c when clauses that evaluate to \c true |
| 209 | at the same time, the first matching state will be applied. For example, in |
| 210 | the following snippet \c state1 will always be selected rather than |
| 211 | \c state2 when sharedCondition becomes \c true. |
| 212 | \qml |
| 213 | Item { |
| 214 | states: [ |
| 215 | State { name: "state1"; when: sharedCondition }, |
| 216 | State { name: "state2"; when: sharedCondition } |
| 217 | ] |
| 218 | // ... |
| 219 | } |
| 220 | \endqml |
| 221 | */ |
| 222 | bool QQuickState::when() const |
| 223 | { |
| 224 | Q_D(const QQuickState); |
| 225 | return d->when; |
| 226 | } |
| 227 | |
| 228 | void QQuickState::setWhen(bool when) |
| 229 | { |
| 230 | Q_D(QQuickState); |
| 231 | d->whenKnown = true; |
| 232 | d->when = when; |
| 233 | if (d->group) |
| 234 | d->group->updateAutoState(); |
| 235 | } |
| 236 | |
| 237 | /*! |
| 238 | \qmlproperty string QtQuick::State::extend |
| 239 | This property holds the state that this state extends. |
| 240 | |
| 241 | When a state extends another state, it inherits all the changes of that state. |
| 242 | |
| 243 | The state being extended is treated as the base state in regards to |
| 244 | the changes specified by the extending state. |
| 245 | */ |
| 246 | QString QQuickState::extends() const |
| 247 | { |
| 248 | Q_D(const QQuickState); |
| 249 | return d->extends; |
| 250 | } |
| 251 | |
| 252 | void QQuickState::setExtends(const QString &extends) |
| 253 | { |
| 254 | Q_D(QQuickState); |
| 255 | d->extends = extends; |
| 256 | } |
| 257 | |
| 258 | /*! |
| 259 | \qmlproperty list<Change> QtQuick::State::changes |
| 260 | This property holds the changes to apply for this state |
| 261 | \default |
| 262 | |
| 263 | By default these changes are applied against the default state. If the state |
| 264 | extends another state, then the changes are applied against the state being |
| 265 | extended. |
| 266 | */ |
| 267 | QQmlListProperty<QQuickStateOperation> QQuickState::changes() |
| 268 | { |
| 269 | Q_D(QQuickState); |
| 270 | return QQmlListProperty<QQuickStateOperation>(this, &d->operations, |
| 271 | QQuickStatePrivate::operations_append, |
| 272 | QQuickStatePrivate::operations_count, |
| 273 | QQuickStatePrivate::operations_at, |
| 274 | QQuickStatePrivate::operations_clear, |
| 275 | QQuickStatePrivate::operations_replace, |
| 276 | QQuickStatePrivate::operations_removeLast); |
| 277 | } |
| 278 | |
| 279 | int QQuickState::operationCount() const |
| 280 | { |
| 281 | Q_D(const QQuickState); |
| 282 | return d->operations.count(); |
| 283 | } |
| 284 | |
| 285 | QQuickStateOperation *QQuickState::operationAt(int index) const |
| 286 | { |
| 287 | Q_D(const QQuickState); |
| 288 | return d->operations.at(i: index); |
| 289 | } |
| 290 | |
| 291 | QQuickState &QQuickState::operator<<(QQuickStateOperation *op) |
| 292 | { |
| 293 | Q_D(QQuickState); |
| 294 | d->operations.append(t: QQuickStatePrivate::OperationGuard(op, &d->operations)); |
| 295 | return *this; |
| 296 | } |
| 297 | |
| 298 | void QQuickStatePrivate::complete() |
| 299 | { |
| 300 | Q_Q(QQuickState); |
| 301 | |
| 302 | for (int ii = 0; ii < reverting.count(); ++ii) { |
| 303 | for (int jj = 0; jj < revertList.count(); ++jj) { |
| 304 | const QQuickRevertAction &revert = reverting.at(i: ii); |
| 305 | const QQuickSimpleAction &simple = revertList.at(i: jj); |
| 306 | if ((revert.event && simple.event() == revert.event) || |
| 307 | simple.property() == revert.property) { |
| 308 | revertList.removeAt(i: jj); |
| 309 | break; |
| 310 | } |
| 311 | } |
| 312 | } |
| 313 | reverting.clear(); |
| 314 | |
| 315 | if (group) |
| 316 | group->stateAboutToComplete(); |
| 317 | emit q->completed(); |
| 318 | } |
| 319 | |
| 320 | // Generate a list of actions for this state. This includes coelescing state |
| 321 | // actions that this state "extends" |
| 322 | QQuickStateOperation::ActionList |
| 323 | QQuickStatePrivate::generateActionList() const |
| 324 | { |
| 325 | QQuickStateOperation::ActionList applyList; |
| 326 | if (inState) |
| 327 | return applyList; |
| 328 | |
| 329 | // Prevent "extends" recursion |
| 330 | inState = true; |
| 331 | |
| 332 | if (!extends.isEmpty()) { |
| 333 | QList<QQuickState *> states = group ? group->states() : QList<QQuickState *>(); |
| 334 | for (int ii = 0; ii < states.count(); ++ii) |
| 335 | if (states.at(i: ii)->name() == extends) { |
| 336 | qmlExecuteDeferred(states.at(i: ii)); |
| 337 | applyList = static_cast<QQuickStatePrivate*>(states.at(i: ii)->d_func())->generateActionList(); |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | for (QQuickStateOperation *op : operations) |
| 342 | applyList << op->actions(); |
| 343 | |
| 344 | inState = false; |
| 345 | return applyList; |
| 346 | } |
| 347 | |
| 348 | QQuickStateGroup *QQuickState::stateGroup() const |
| 349 | { |
| 350 | Q_D(const QQuickState); |
| 351 | return d->group; |
| 352 | } |
| 353 | |
| 354 | void QQuickState::setStateGroup(QQuickStateGroup *group) |
| 355 | { |
| 356 | Q_D(QQuickState); |
| 357 | d->group = group; |
| 358 | } |
| 359 | |
| 360 | void QQuickState::cancel() |
| 361 | { |
| 362 | Q_D(QQuickState); |
| 363 | d->transitionManager.cancel(); |
| 364 | } |
| 365 | |
| 366 | void QQuickStateAction::deleteFromBinding() |
| 367 | { |
| 368 | if (fromBinding) { |
| 369 | QQmlPropertyPrivate::removeBinding(that: property); |
| 370 | fromBinding = nullptr; |
| 371 | } |
| 372 | } |
| 373 | |
| 374 | bool QQuickState::containsPropertyInRevertList(QObject *target, const QString &name) const |
| 375 | { |
| 376 | Q_D(const QQuickState); |
| 377 | |
| 378 | if (isStateActive()) { |
| 379 | for (const QQuickSimpleAction &simpleAction : d->revertList) { |
| 380 | if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) |
| 381 | return true; |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | return false; |
| 386 | } |
| 387 | |
| 388 | bool QQuickState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue) |
| 389 | { |
| 390 | Q_D(QQuickState); |
| 391 | |
| 392 | if (isStateActive()) { |
| 393 | for (QQuickSimpleAction &simpleAction : d->revertList) { |
| 394 | if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { |
| 395 | simpleAction.setValue(revertValue); |
| 396 | return true; |
| 397 | } |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | return false; |
| 402 | } |
| 403 | |
| 404 | bool QQuickState::changeBindingInRevertList(QObject *target, const QString &name, QQmlAbstractBinding *binding) |
| 405 | { |
| 406 | Q_D(QQuickState); |
| 407 | |
| 408 | if (isStateActive()) { |
| 409 | for (QQuickSimpleAction &simpleAction : d->revertList) { |
| 410 | if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { |
| 411 | simpleAction.setBinding(binding); |
| 412 | return true; |
| 413 | } |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | return false; |
| 418 | } |
| 419 | |
| 420 | bool QQuickState::removeEntryFromRevertList(QObject *target, const QString &name) |
| 421 | { |
| 422 | Q_D(QQuickState); |
| 423 | |
| 424 | if (isStateActive()) { |
| 425 | for (auto it = d->revertList.begin(), end = d->revertList.end(); it != end; ++it) { |
| 426 | QQuickSimpleAction &simpleAction = *it; |
| 427 | if (simpleAction.property().object() == target && simpleAction.property().name() == name) { |
| 428 | QQmlPropertyPrivate::removeBinding(that: simpleAction.property()); |
| 429 | |
| 430 | simpleAction.property().write(simpleAction.value()); |
| 431 | if (simpleAction.binding()) |
| 432 | QQmlPropertyPrivate::setBinding(binding: simpleAction.binding()); |
| 433 | |
| 434 | d->revertList.erase(pos: it); |
| 435 | return true; |
| 436 | } |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | return false; |
| 441 | } |
| 442 | |
| 443 | void QQuickState::addEntryToRevertList(const QQuickStateAction &action) |
| 444 | { |
| 445 | Q_D(QQuickState); |
| 446 | |
| 447 | QQuickSimpleAction simpleAction(action); |
| 448 | |
| 449 | d->revertList.append(t: simpleAction); |
| 450 | } |
| 451 | |
| 452 | void QQuickState::removeAllEntriesFromRevertList(QObject *target) |
| 453 | { |
| 454 | Q_D(QQuickState); |
| 455 | |
| 456 | if (isStateActive()) { |
| 457 | const auto actionMatchesTarget = [target](QQuickSimpleAction &simpleAction) { |
| 458 | if (simpleAction.property().object() == target) { |
| 459 | QQmlPropertyPrivate::removeBinding(that: simpleAction.property()); |
| 460 | |
| 461 | simpleAction.property().write(simpleAction.value()); |
| 462 | if (simpleAction.binding()) |
| 463 | QQmlPropertyPrivate::setBinding(binding: simpleAction.binding()); |
| 464 | |
| 465 | return true; |
| 466 | } |
| 467 | return false; |
| 468 | }; |
| 469 | |
| 470 | d->revertList.erase(first: std::remove_if(first: d->revertList.begin(), last: d->revertList.end(), |
| 471 | pred: actionMatchesTarget), |
| 472 | last: d->revertList.end()); |
| 473 | } |
| 474 | } |
| 475 | |
| 476 | void QQuickState::addEntriesToRevertList(const QList<QQuickStateAction> &actionList) |
| 477 | { |
| 478 | Q_D(QQuickState); |
| 479 | if (isStateActive()) { |
| 480 | QList<QQuickSimpleAction> simpleActionList; |
| 481 | simpleActionList.reserve(size: actionList.count()); |
| 482 | |
| 483 | for (const QQuickStateAction &action : actionList) { |
| 484 | QQuickSimpleAction simpleAction(action); |
| 485 | action.property.write(action.toValue); |
| 486 | if (action.toBinding) |
| 487 | QQmlPropertyPrivate::setBinding(binding: action.toBinding.data()); |
| 488 | |
| 489 | simpleActionList.append(t: simpleAction); |
| 490 | } |
| 491 | |
| 492 | d->revertList.append(t: simpleActionList); |
| 493 | } |
| 494 | } |
| 495 | |
| 496 | QVariant QQuickState::valueInRevertList(QObject *target, const QString &name) const |
| 497 | { |
| 498 | Q_D(const QQuickState); |
| 499 | |
| 500 | if (isStateActive()) { |
| 501 | for (const QQuickSimpleAction &simpleAction : d->revertList) { |
| 502 | if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) |
| 503 | return simpleAction.value(); |
| 504 | } |
| 505 | } |
| 506 | |
| 507 | return QVariant(); |
| 508 | } |
| 509 | |
| 510 | QQmlAbstractBinding *QQuickState::bindingInRevertList(QObject *target, const QString &name) const |
| 511 | { |
| 512 | Q_D(const QQuickState); |
| 513 | |
| 514 | if (isStateActive()) { |
| 515 | for (const QQuickSimpleAction &simpleAction : d->revertList) { |
| 516 | if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) |
| 517 | return simpleAction.binding(); |
| 518 | } |
| 519 | } |
| 520 | |
| 521 | return nullptr; |
| 522 | } |
| 523 | |
| 524 | bool QQuickState::isStateActive() const |
| 525 | { |
| 526 | return stateGroup() && stateGroup()->state() == name(); |
| 527 | } |
| 528 | |
| 529 | void QQuickState::apply(QQuickTransition *trans, QQuickState *revert) |
| 530 | { |
| 531 | Q_D(QQuickState); |
| 532 | |
| 533 | qmlExecuteDeferred(this); |
| 534 | |
| 535 | cancel(); |
| 536 | if (revert) |
| 537 | revert->cancel(); |
| 538 | d->revertList.clear(); |
| 539 | d->reverting.clear(); |
| 540 | |
| 541 | if (revert) { |
| 542 | QQuickStatePrivate *revertPrivate = |
| 543 | static_cast<QQuickStatePrivate*>(revert->d_func()); |
| 544 | d->revertList = revertPrivate->revertList; |
| 545 | revertPrivate->revertList.clear(); |
| 546 | } |
| 547 | |
| 548 | // List of actions caused by this state |
| 549 | QQuickStateOperation::ActionList applyList = d->generateActionList(); |
| 550 | |
| 551 | // List of actions that need to be reverted to roll back (just) this state |
| 552 | QQuickStatePrivate::SimpleActionList additionalReverts; |
| 553 | // First add the reverse of all the applyList actions |
| 554 | for (int ii = 0; ii < applyList.count(); ++ii) { |
| 555 | QQuickStateAction &action = applyList[ii]; |
| 556 | |
| 557 | if (action.event) { |
| 558 | if (!action.event->isReversable()) |
| 559 | continue; |
| 560 | bool found = false; |
| 561 | for (int jj = 0; jj < d->revertList.count(); ++jj) { |
| 562 | QQuickStateActionEvent *event = d->revertList.at(i: jj).event(); |
| 563 | if (event && event->type() == action.event->type()) { |
| 564 | if (action.event->mayOverride(other: event)) { |
| 565 | found = true; |
| 566 | |
| 567 | if (action.event != d->revertList.at(i: jj).event() && action.event->needsCopy()) { |
| 568 | action.event->copyOriginals(d->revertList.at(i: jj).event()); |
| 569 | |
| 570 | QQuickSimpleAction r(action); |
| 571 | additionalReverts << r; |
| 572 | d->revertList.removeAt(i: jj); |
| 573 | --jj; |
| 574 | } else if (action.event->isRewindable()) //###why needed? |
| 575 | action.event->saveCurrentValues(); |
| 576 | |
| 577 | break; |
| 578 | } |
| 579 | } |
| 580 | } |
| 581 | if (!found) { |
| 582 | action.event->saveOriginals(); |
| 583 | // Only need to revert the applyList action if the previous |
| 584 | // state doesn't have a higher priority revert already |
| 585 | QQuickSimpleAction r(action); |
| 586 | additionalReverts << r; |
| 587 | } |
| 588 | } else { |
| 589 | bool found = false; |
| 590 | action.fromBinding = QQmlPropertyPrivate::binding(that: action.property); |
| 591 | |
| 592 | for (int jj = 0; jj < d->revertList.count(); ++jj) { |
| 593 | if (d->revertList.at(i: jj).property() == action.property) { |
| 594 | found = true; |
| 595 | if (d->revertList.at(i: jj).binding() != action.fromBinding.data()) { |
| 596 | action.deleteFromBinding(); |
| 597 | } |
| 598 | break; |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | if (!found) { |
| 603 | if (!action.restore) { |
| 604 | action.deleteFromBinding();; |
| 605 | } else { |
| 606 | // Only need to revert the applyList action if the previous |
| 607 | // state doesn't have a higher priority revert already |
| 608 | QQuickSimpleAction r(action); |
| 609 | additionalReverts << r; |
| 610 | } |
| 611 | } |
| 612 | } |
| 613 | } |
| 614 | |
| 615 | // Any reverts from a previous state that aren't carried forth |
| 616 | // into this state need to be translated into apply actions |
| 617 | for (int ii = 0; ii < d->revertList.count(); ++ii) { |
| 618 | bool found = false; |
| 619 | if (d->revertList.at(i: ii).event()) { |
| 620 | QQuickStateActionEvent *event = d->revertList.at(i: ii).event(); |
| 621 | if (!event->isReversable()) |
| 622 | continue; |
| 623 | for (int jj = 0; !found && jj < applyList.count(); ++jj) { |
| 624 | const QQuickStateAction &action = applyList.at(i: jj); |
| 625 | if (action.event && action.event->type() == event->type()) { |
| 626 | if (action.event->mayOverride(other: event)) |
| 627 | found = true; |
| 628 | } |
| 629 | } |
| 630 | } else { |
| 631 | for (int jj = 0; !found && jj < applyList.count(); ++jj) { |
| 632 | const QQuickStateAction &action = applyList.at(i: jj); |
| 633 | if (action.property == d->revertList.at(i: ii).property()) |
| 634 | found = true; |
| 635 | } |
| 636 | } |
| 637 | if (!found) { |
| 638 | // If revert list contains bindings assigned to deleted objects, we need to |
| 639 | // prevent reverting properties of those objects. |
| 640 | if (d->revertList.at(i: ii).binding() && !d->revertList.at(i: ii).property().object()) { |
| 641 | continue; |
| 642 | } |
| 643 | QVariant cur = d->revertList.at(i: ii).property().read(); |
| 644 | QQmlPropertyPrivate::removeBinding(that: d->revertList.at(i: ii).property()); |
| 645 | |
| 646 | QQuickStateAction a; |
| 647 | a.property = d->revertList.at(i: ii).property(); |
| 648 | a.fromValue = cur; |
| 649 | a.toValue = d->revertList.at(i: ii).value(); |
| 650 | a.toBinding = d->revertList.at(i: ii).binding(); |
| 651 | a.specifiedObject = d->revertList.at(i: ii).specifiedObject(); |
| 652 | a.specifiedProperty = d->revertList.at(i: ii).specifiedProperty(); |
| 653 | a.event = d->revertList.at(i: ii).event(); |
| 654 | a.reverseEvent = d->revertList.at(i: ii).reverseEvent(); |
| 655 | if (a.event && a.event->isRewindable()) |
| 656 | a.event->saveCurrentValues(); |
| 657 | applyList << a; |
| 658 | // Store these special reverts in the reverting list |
| 659 | if (a.event) |
| 660 | d->reverting << a.event; |
| 661 | else |
| 662 | d->reverting << a.property; |
| 663 | } |
| 664 | } |
| 665 | // All the local reverts now become part of the ongoing revertList |
| 666 | d->revertList << additionalReverts; |
| 667 | |
| 668 | #ifndef QT_NO_DEBUG_STREAM |
| 669 | // Output for debugging |
| 670 | if (stateChangeDebug()) { |
| 671 | for (const QQuickStateAction &action : qAsConst(t&: applyList)) { |
| 672 | if (action.event) |
| 673 | qWarning() << " QQuickStateAction event:" << action.event->type(); |
| 674 | else |
| 675 | qWarning() << " QQuickStateAction:" << action.property.object() |
| 676 | << action.property.name() << "From:" << action.fromValue |
| 677 | << "To:" << action.toValue; |
| 678 | } |
| 679 | } |
| 680 | #endif |
| 681 | |
| 682 | d->transitionManager.transition(applyList, transition: trans); |
| 683 | } |
| 684 | |
| 685 | QQuickStateOperation::ActionList QQuickStateOperation::actions() |
| 686 | { |
| 687 | return ActionList(); |
| 688 | } |
| 689 | |
| 690 | QQuickState *QQuickStateOperation::state() const |
| 691 | { |
| 692 | Q_D(const QQuickStateOperation); |
| 693 | return d->m_state; |
| 694 | } |
| 695 | |
| 696 | void QQuickStateOperation::setState(QQuickState *state) |
| 697 | { |
| 698 | Q_D(QQuickStateOperation); |
| 699 | d->m_state = state; |
| 700 | } |
| 701 | |
| 702 | QT_END_NAMESPACE |
| 703 | |
| 704 | #include "moc_qquickstate_p.cpp" |
| 705 | |