diff options
author | Olivier De Cannière <olivier.decanniere@qt.io> | 2023-03-17 15:42:47 +0100 |
---|---|---|
committer | Olivier De Cannière <olivier.decanniere@qt.io> | 2023-03-30 18:02:16 +0200 |
commit | 405bd4299819e39397cea0090a9442fd4b6ce911 (patch) | |
tree | c1254e18a8ab47cc6bde048eb52ad0d89c2492c8 /examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties | |
parent | 03ff348b4942ae29dd5bc2bd563998d7ba82ecd7 (diff) | |
download | qtdeclarative-405bd4299819e39397cea0090a9442fd4b6ce911.tar.gz |
Doc: Revamp "Extending QML" examples into a tutorial
The examples in the "Extending QML" series were often redundant with the
information of the "Writing QML Extensions with C++" tutorial, had
outdated code and sometimes had no documentation. The examples that
covered topics not mentioned in the first tutorial were revamped into a
second "advanced" tutorial extending the first one. The others were
removed. The remaining examples were largely based on the same example
code of a birthday party. This code was slightly adapted and separated
into 7 states, each building upon the previous, with the code change
illustrating the associated feature. A tutorial page, in the style of
the first one, was added documenting the different QML features and
the required code changes in the example project.
Links in the documentation from and to the affected pages were update
as best as possible.
Pick-to: 6.5
Fixes: QTBUG-111033
Change-Id: I9d97e8b32b128c1624d67525996fa14d493909d3
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties')
10 files changed, 469 insertions, 0 deletions
diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/CMakeLists.txt new file mode 100644 index 0000000000..89aff6402f --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(grouped LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties") + +find_package(Qt6 REQUIRED COMPONENTS Core Qml Gui) +qt_standard_project_setup() + +qt_policy(SET QTP0001 NEW) + +qt_add_executable(grouped + birthdayparty.cpp birthdayparty.h + main.cpp + person.cpp person.h +) + +set_target_properties(grouped PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(grouped PUBLIC + Qt::Core + Qt::Qml + Qt::Gui +) + +qt_add_qml_module(grouped + URI People + QML_FILES Main.qml + DEPENDENCIES + QtQuick +) + +install(TARGETS grouped + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml new file mode 100644 index 0000000000..27951b5ea8 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml @@ -0,0 +1,33 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import People +import QtQuick // For QColor + +BirthdayParty { + host: Boy { + name: "Bob Jones" + shoe { size: 12; color: "white"; brand: "Bikey"; price: 90.0 } + } + + Boy { + name: "Leo Hodges" + shoe { size: 10; color: "black"; brand: "Thebok"; price: 59.95 } + } + Boy { + name: "Jack Smith" + shoe { + size: 8 + color: "blue" + brand: "Luma" + price: 19.95 + } + } + Girl { + name: "Anne Brown" + shoe.size: 7 + shoe.color: "red" + shoe.brand: "Job Macobs" + shoe.price: 99.99 + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.cpp new file mode 100644 index 0000000000..ad38f284e7 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.cpp @@ -0,0 +1,99 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "birthdayparty.h" + +Person *BirthdayParty::host() const +{ + return m_host; +} + +void BirthdayParty::setHost(Person *host) +{ + if (m_host != host) { + m_host = host; + emit hostChanged(); + } +} + +QQmlListProperty<Person> BirthdayParty::guests() +{ + return { this, + this, + &BirthdayParty::appendGuest, + &BirthdayParty::guestCount, + &BirthdayParty::guest, + &BirthdayParty::clearGuests, + &BirthdayParty::replaceGuest, + &BirthdayParty::removeLastGuest }; +} + +void BirthdayParty::appendGuest(Person *guest) +{ + m_guests.append(guest); + emit guestsChanged(); +} + +qsizetype BirthdayParty::guestCount() const +{ + return m_guests.count(); +} + +Person *BirthdayParty::guest(qsizetype index) const +{ + return m_guests.at(index); +} + +void BirthdayParty::clearGuests() +{ + if (!m_guests.empty()) { + m_guests.clear(); + emit guestsChanged(); + } +} + +void BirthdayParty::replaceGuest(qsizetype index, Person *guest) +{ + if (m_guests.size() > index) { + m_guests[index] = guest; + emit guestsChanged(); + } +} + +void BirthdayParty::removeLastGuest() +{ + if (!m_guests.empty()) { + m_guests.removeLast(); + emit guestsChanged(); + } +} + +void BirthdayParty::appendGuest(QQmlListProperty<Person> *list, Person *guest) +{ + static_cast<BirthdayParty *>(list->data)->appendGuest(guest); +} + +void BirthdayParty::clearGuests(QQmlListProperty<Person> *list) +{ + static_cast<BirthdayParty *>(list->data)->clearGuests(); +} + +void BirthdayParty::replaceGuest(QQmlListProperty<Person> *list, qsizetype index, Person *guest) +{ + static_cast<BirthdayParty *>(list->data)->replaceGuest(index, guest); +} + +void BirthdayParty::removeLastGuest(QQmlListProperty<Person> *list) +{ + static_cast<BirthdayParty *>(list->data)->removeLastGuest(); +} + +Person *BirthdayParty::guest(QQmlListProperty<Person> *list, qsizetype index) +{ + return static_cast<BirthdayParty *>(list->data)->guest(index); +} + +qsizetype BirthdayParty::guestCount(QQmlListProperty<Person> *list) +{ + return static_cast<BirthdayParty *>(list->data)->guestCount(); +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.h new file mode 100644 index 0000000000..4d7e61a487 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/birthdayparty.h @@ -0,0 +1,49 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef BIRTHDAYPARTY_H +#define BIRTHDAYPARTY_H + +#include "person.h" + +#include <QObject> +#include <QQmlListProperty> + +class BirthdayParty : public QObject +{ + Q_OBJECT + Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL) + Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL) + Q_CLASSINFO("DefaultProperty", "guests") + QML_ELEMENT +public: + using QObject::QObject; + + Person *host() const; + void setHost(Person *); + + QQmlListProperty<Person> guests(); + void appendGuest(Person *); + qsizetype guestCount() const; + Person *guest(qsizetype) const; + void clearGuests(); + void replaceGuest(qsizetype, Person *); + void removeLastGuest(); + +signals: + void hostChanged(); + void guestsChanged(); + +private: + static void appendGuest(QQmlListProperty<Person> *list, Person *); + static qsizetype guestCount(QQmlListProperty<Person> *); + static Person *guest(QQmlListProperty<Person> *, qsizetype); + static void clearGuests(QQmlListProperty<Person> *); + static void replaceGuest(QQmlListProperty<Person> *, qsizetype, Person *); + static void removeLastGuest(QQmlListProperty<Person> *); + + Person *m_host = nullptr; + QList<Person *> m_guests; +}; + +#endif // BIRTHDAYPARTY_H diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.pro b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.pro new file mode 100644 index 0000000000..52e2937edf --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.pro @@ -0,0 +1,12 @@ +QT += qml + +CONFIG += qmltypes +QML_IMPORT_NAME = People +QML_IMPORT_MAJOR_VERSION = 1 + +SOURCES += main.cpp \ + person.cpp \ + birthdayparty.cpp +HEADERS += person.h \ + birthdayparty.h +RESOURCES += grouped.qrc diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.qrc new file mode 100644 index 0000000000..b1eeb489e2 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/grouped.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="/qt/qml/People/"> + <file>Main.qml</file> + <file alias="qmldir">qmldir.in</file> +</qresource> +</RCC> diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.cpp new file mode 100644 index 0000000000..0721d496f0 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/main.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "birthdayparty.h" +#include "person.h" + +#include <QCoreApplication> +#include <QDebug> +#include <QQmlComponent> +#include <QQmlEngine> + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadFromModule("People", "Main"); + std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) }; + + if (party && party->host()) { + qInfo() << party->host()->name() << "is having a birthday!"; + + if (qobject_cast<Boy *>(party->host())) + qInfo() << "He is inviting:"; + else + qInfo() << "She is inviting:"; + + Person *bestShoe = nullptr; + for (qsizetype ii = 0; ii < party->guestCount(); ++ii) { + Person *guest = party->guest(ii); + qInfo() << " " << guest->name(); + + if (!bestShoe || bestShoe->shoe()->price() < guest->shoe()->price()) + bestShoe = guest; + } + if (bestShoe) + qInfo() << bestShoe->name() << "is wearing the best shoes!"; + + return EXIT_SUCCESS; + } + + qWarning() << component.errors(); + return EXIT_FAILURE; +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.cpp new file mode 100644 index 0000000000..fe3d19b58d --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.cpp @@ -0,0 +1,87 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "person.h" + +Person::Person(QObject *parent) : QObject(parent) +{ + m_shoe = new ShoeDescription(this); +} + +int ShoeDescription::size() const +{ + return m_size; +} + +void ShoeDescription::setSize(int size) +{ + if (m_size != size) { + m_size = size; + emit shoeChanged(); + } +} + +QColor ShoeDescription::color() const +{ + return m_color; +} + +void ShoeDescription::setColor(const QColor &color) +{ + if (m_color != color) { + m_color = color; + emit shoeChanged(); + } +} + +QString ShoeDescription::brand() const +{ + return m_brand; +} + +void ShoeDescription::setBrand(const QString &brand) +{ + if (m_brand != brand) { + m_brand = brand; + emit shoeChanged(); + } +} + +qreal ShoeDescription::price() const +{ + return m_price; +} + +void ShoeDescription::setPrice(qreal price) +{ + if (m_price != price) { + m_price = price; + emit shoeChanged(); + } +} + +QString Person::name() const +{ + return m_name; +} + +void Person::setName(const QString &name) +{ + if (m_name != name) { + m_name = name; + emit nameChanged(); + } +} + +ShoeDescription *Person::shoe() const +{ + return m_shoe; +} + +void Person::setShoe(ShoeDescription *shoe) +{ + if (m_shoe != shoe) { + m_shoe = shoe; + emit shoeChanged(); + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h new file mode 100644 index 0000000000..ecb4545097 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h @@ -0,0 +1,87 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef PERSON_H +#define PERSON_H + +#include <QtQml/qqml.h> +#include <QColor> +#include <QObject> + +class ShoeDescription : public QObject +{ + Q_OBJECT + Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged FINAL) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged FINAL) + Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged FINAL) + Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged FINAL) + QML_ANONYMOUS +public: + using QObject::QObject; + + int size() const; + void setSize(int); + + QColor color() const; + void setColor(const QColor &); + + QString brand() const; + void setBrand(const QString &); + + qreal price() const; + void setPrice(qreal); + +signals: + void shoeChanged(); + +private: + int m_size = 0; + QColor m_color; + QString m_brand; + qreal m_price = 0; +}; + +class Person : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) + Q_PROPERTY(ShoeDescription *shoe READ shoe WRITE setShoe NOTIFY shoeChanged FINAL) + QML_ELEMENT + QML_UNCREATABLE("Person is an abstract base class.") +public: + using QObject::QObject; + + Person(QObject *parent = nullptr); + + QString name() const; + void setName(const QString &); + + ShoeDescription *shoe() const; + void setShoe(ShoeDescription *shoe); + +signals: + void nameChanged(); + void shoeChanged(); + +private: + QString m_name; + ShoeDescription *m_shoe = nullptr; +}; + +class Boy : public Person +{ + Q_OBJECT + QML_ELEMENT +public: + using Person::Person; +}; + +class Girl : public Person +{ + Q_OBJECT + QML_ELEMENT +public: + using Person::Person; +}; + +#endif // PERSON_H diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/qmldir.in new file mode 100644 index 0000000000..2e634e41af --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/qmldir.in @@ -0,0 +1,5 @@ +module People +typeinfo grouped.qmltypes +prefer :/qt/qml/People/ +Main 254.0 Main.qml +depends QtQuick |