diff options
Diffstat (limited to 'examples/qml/tutorials')
116 files changed, 3775 insertions, 162 deletions
diff --git a/examples/qml/tutorials/CMakeLists.txt b/examples/qml/tutorials/CMakeLists.txt index 1a82ed75af..ecfec86a25 100644 --- a/examples/qml/tutorials/CMakeLists.txt +++ b/examples/qml/tutorials/CMakeLists.txt @@ -1,6 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -if(TARGET Qt::Quick) +if(TARGET Qt6::Quick) add_subdirectory(extending-qml) endif() diff --git a/examples/qml/tutorials/extending-qml-advanced/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/CMakeLists.txt new file mode 100644 index 0000000000..4b47f135a3 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if(NOT ANDROID) + qt_internal_add_example(advanced1-Base-project) + qt_internal_add_example(advanced2-Inheritance-and-coercion) + qt_internal_add_example(advanced3-Default-properties) +endif(NOT ANDROID) +qt_internal_add_example(advanced4-Grouped-properties) +qt_internal_add_example(advanced5-Attached-properties) +qt_internal_add_example(advanced6-Property-value-source) +qt_internal_add_example(advanced7-Extension-objects) diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/CMakeLists.txt new file mode 100644 index 0000000000..45dfbc0b12 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/CMakeLists.txt @@ -0,0 +1,50 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(baseproject LANGUAGES CXX) + +if (ANDROID) + message(FATAL_ERROR "This project cannot be built on Android.") +endif() + +find_package(Qt6 REQUIRED COMPONENTS Core Qml) +qt_standard_project_setup() + +qt_policy(SET QTP0001 NEW) + +qt_add_executable(baseproject + birthdayparty.cpp birthdayparty.h + main.cpp + person.cpp person.h +) + +set_target_properties(baseproject PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(baseproject PUBLIC + Qt6::Core + Qt6::Qml +) + +qt_add_qml_module(baseproject + URI People + QML_FILES Main.qml +) + +install(TARGETS baseproject + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET baseproject + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml new file mode 100644 index 0000000000..c917d5b1f1 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import People + +BirthdayParty { + host: Person { + name: "Bob Jones" + shoeSize: 12 + } + guests: [ + Person { name: "Leo Hodges" }, + Person { name: "Jack Smith" }, + Person { name: "Anne Brown" } + ] +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.pro b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.pro new file mode 100644 index 0000000000..26c865fbba --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.pro @@ -0,0 +1,13 @@ +QT = core 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 += baseproject.qrc diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.qrc new file mode 100644 index 0000000000..b1eeb489e2 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/baseproject.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/advanced1-Base-project/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.cpp new file mode 100644 index 0000000000..ad38f284e7 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/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/advanced1-Base-project/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.h new file mode 100644 index 0000000000..1a28ef2632 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.h @@ -0,0 +1,48 @@ +// 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) + 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> *, 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/advanced1-Base-project/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.cpp new file mode 100644 index 0000000000..eac94016d2 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/main.cpp @@ -0,0 +1,32 @@ +// 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!\n" + "They are inviting:"; + for (qsizetype ii = 0; ii < party->guestCount(); ++ii) + qInfo() << " " << party->guest(ii)->name(); + return EXIT_SUCCESS; + } + + qWarning() << component.errors(); + return EXIT_FAILURE; +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.cpp new file mode 100644 index 0000000000..f8f4b1d2f4 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.cpp @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "person.h" + +QString Person::name() const +{ + return m_name; +} + +void Person::setName(const QString &name) +{ + if (m_name != name) { + m_name = name; + emit nameChanged(); + } +} + +int Person::shoeSize() const +{ + return m_shoeSize; +} + +void Person::setShoeSize(int shoeSize) +{ + if (m_shoeSize != shoeSize) { + m_shoeSize = shoeSize; + emit shoeSizeChanged(); + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.h new file mode 100644 index 0000000000..ded272626a --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/person.h @@ -0,0 +1,34 @@ +// 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 <QObject> + +class Person : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) + Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL) + QML_ELEMENT +public: + using QObject::QObject; + + QString name() const; + void setName(const QString &); + + int shoeSize() const; + void setShoeSize(int); + +signals: + void nameChanged(); + void shoeSizeChanged(); + +private: + QString m_name; + int m_shoeSize = 0; +}; + +#endif // PERSON_H diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/qmldir.in new file mode 100644 index 0000000000..70cde3c958 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced1-Base-project/qmldir.in @@ -0,0 +1,4 @@ +module People +typeinfo birthdayparty.qmltypes +prefer :/qt/qml/People/ +Main 254.0 Main.qml diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/CMakeLists.txt new file mode 100644 index 0000000000..a2fa4812eb --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/CMakeLists.txt @@ -0,0 +1,50 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(coercion LANGUAGES CXX) + +if (ANDROID) + message(FATAL_ERROR "This project cannot be built on Android.") +endif() + +find_package(Qt6 REQUIRED COMPONENTS Core Qml) +qt_standard_project_setup() + +qt_policy(SET QTP0001 NEW) + +qt_add_executable(coercion + birthdayparty.cpp birthdayparty.h + main.cpp + person.cpp person.h +) + +set_target_properties(coercion PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(coercion PUBLIC + Qt6::Core + Qt6::Qml +) + +qt_add_qml_module(coercion + URI People + QML_FILES Main.qml +) + +install(TARGETS coercion + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET coercion + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/Main.qml new file mode 100644 index 0000000000..1c3fe141ca --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/Main.qml @@ -0,0 +1,16 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import People + +BirthdayParty { + host: Boy { + name: "Bob Jones" + shoeSize: 12 + } + guests: [ + Boy { name: "Leo Hodges" }, + Boy { name: "Jack Smith" }, + Girl { name: "Anne Brown" } + ] +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.cpp new file mode 100644 index 0000000000..ad38f284e7 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/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/advanced2-Inheritance-and-coercion/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.h new file mode 100644 index 0000000000..f9a5c126e3 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.h @@ -0,0 +1,48 @@ +// 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) + 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/advanced2-Inheritance-and-coercion/coercion.pro b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.pro new file mode 100644 index 0000000000..1ba8194965 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.pro @@ -0,0 +1,12 @@ +QT = core 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 += coercion.qrc diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.qrc new file mode 100644 index 0000000000..b1eeb489e2 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/coercion.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/advanced2-Inheritance-and-coercion/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.cpp new file mode 100644 index 0000000000..fa26448f44 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/main.cpp @@ -0,0 +1,37 @@ +// 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:"; + + for (qsizetype ii = 0; ii < party->guestCount(); ++ii) + qInfo() << " " << party->guest(ii)->name(); + + return EXIT_SUCCESS; + } + + qWarning() << component.errors(); + return EXIT_FAILURE; +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.cpp new file mode 100644 index 0000000000..f8f4b1d2f4 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.cpp @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "person.h" + +QString Person::name() const +{ + return m_name; +} + +void Person::setName(const QString &name) +{ + if (m_name != name) { + m_name = name; + emit nameChanged(); + } +} + +int Person::shoeSize() const +{ + return m_shoeSize; +} + +void Person::setShoeSize(int shoeSize) +{ + if (m_shoeSize != shoeSize) { + m_shoeSize = shoeSize; + emit shoeSizeChanged(); + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h new file mode 100644 index 0000000000..99fc9209ab --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h @@ -0,0 +1,51 @@ +// 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 <QObject> + +class Person : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) + Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL) + QML_ELEMENT + QML_UNCREATABLE("Person is an abstract base class.") +public: + using QObject::QObject; + + QString name() const; + void setName(const QString &); + + int shoeSize() const; + void setShoeSize(int); + +signals: + void nameChanged(); + void shoeSizeChanged(); + +private: + QString m_name; + int m_shoeSize = 0; +}; + +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/advanced2-Inheritance-and-coercion/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/qmldir.in new file mode 100644 index 0000000000..3ccd68f7cc --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/qmldir.in @@ -0,0 +1,4 @@ +module People +typeinfo coercion.qmltypes +prefer :/qt/qml/People/ +Main 254.0 Main.qml diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/CMakeLists.txt new file mode 100644 index 0000000000..0682e487c8 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/CMakeLists.txt @@ -0,0 +1,50 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(default LANGUAGES CXX) + +if (ANDROID) + message(FATAL_ERROR "This project cannot be built on Android.") +endif() + +find_package(Qt6 REQUIRED COMPONENTS Core Qml) +qt_standard_project_setup() + +qt_policy(SET QTP0001 NEW) + +qt_add_executable(default + birthdayparty.cpp birthdayparty.h + main.cpp + person.cpp person.h +) + +set_target_properties(default PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(default PUBLIC + Qt6::Core + Qt6::Qml +) + +qt_add_qml_module(default + URI People + QML_FILES Main.qml +) + +install(TARGETS default + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET default + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml new file mode 100644 index 0000000000..1070427cb0 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml @@ -0,0 +1,15 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import People + +BirthdayParty { + host: Boy { + name: "Bob Jones" + shoeSize: 12 + } + + Boy { name: "Leo Hodges" } + Boy { name: "Jack Smith" } + Girl { name: "Anne Brown" } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.cpp new file mode 100644 index 0000000000..ad38f284e7 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-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/advanced3-Default-properties/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.h new file mode 100644 index 0000000000..4d7e61a487 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-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/advanced3-Default-properties/default.pro b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.pro new file mode 100644 index 0000000000..65dd27213b --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.pro @@ -0,0 +1,12 @@ +QT = core 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 += default.qrc diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.qrc new file mode 100644 index 0000000000..b1eeb489e2 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/default.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/advanced3-Default-properties/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.cpp new file mode 100644 index 0000000000..fa26448f44 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/main.cpp @@ -0,0 +1,37 @@ +// 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:"; + + for (qsizetype ii = 0; ii < party->guestCount(); ++ii) + qInfo() << " " << party->guest(ii)->name(); + + return EXIT_SUCCESS; + } + + qWarning() << component.errors(); + return EXIT_FAILURE; +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.cpp new file mode 100644 index 0000000000..f8f4b1d2f4 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.cpp @@ -0,0 +1,30 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "person.h" + +QString Person::name() const +{ + return m_name; +} + +void Person::setName(const QString &name) +{ + if (m_name != name) { + m_name = name; + emit nameChanged(); + } +} + +int Person::shoeSize() const +{ + return m_shoeSize; +} + +void Person::setShoeSize(int shoeSize) +{ + if (m_shoeSize != shoeSize) { + m_shoeSize = shoeSize; + emit shoeSizeChanged(); + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.h new file mode 100644 index 0000000000..99fc9209ab --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/person.h @@ -0,0 +1,51 @@ +// 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 <QObject> + +class Person : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) + Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL) + QML_ELEMENT + QML_UNCREATABLE("Person is an abstract base class.") +public: + using QObject::QObject; + + QString name() const; + void setName(const QString &); + + int shoeSize() const; + void setShoeSize(int); + +signals: + void nameChanged(); + void shoeSizeChanged(); + +private: + QString m_name; + int m_shoeSize = 0; +}; + +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/advanced3-Default-properties/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/qmldir.in new file mode 100644 index 0000000000..da1b995d64 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced3-Default-properties/qmldir.in @@ -0,0 +1,4 @@ +module People +typeinfo default.qmltypes +prefer :/qt/qml/People/ +Main 254.0 Main.qml 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..8f66d1ad07 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/CMakeLists.txt @@ -0,0 +1,49 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(grouped LANGUAGES CXX) + +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 + Qt6::Core + Qt6::Qml + Qt6::Gui +) + +qt_add_qml_module(grouped + URI People + QML_FILES Main.qml + DEPENDENCIES + QtQuick +) + +install(TARGETS grouped + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET grouped + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) 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..53cec6b192 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.cpp @@ -0,0 +1,96 @@ +// 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(); + } +} + +bool ShoeDescription::operatorEqualsImpl(const ShoeDescription &lhs, const ShoeDescription &rhs) +{ + return lhs.m_size == rhs.m_size && lhs.m_color == rhs.m_color && lhs.m_brand == rhs.m_brand + && lhs.m_price == rhs.m_price; +} + +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 (!shoe) + return; + + 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..4f040e491b --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h @@ -0,0 +1,98 @@ +// 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); + + friend bool operator==(const ShoeDescription &lhs, const ShoeDescription &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + friend bool operator!=(const ShoeDescription &lhs, const ShoeDescription &rhs) + { + return !operatorEqualsImpl(lhs, rhs); + } + +signals: + void shoeChanged(); + +private: + static bool operatorEqualsImpl(const ShoeDescription &, const ShoeDescription &); + + 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 diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/CMakeLists.txt new file mode 100644 index 0000000000..c7b30376b6 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/CMakeLists.txt @@ -0,0 +1,49 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(attached LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick) +qt_standard_project_setup() + +qt_policy(SET QTP0001 NEW) + +qt_add_executable(attached + birthdayparty.cpp birthdayparty.h + main.cpp + person.cpp person.h +) + +set_target_properties(attached PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(attached PUBLIC + Qt6::Core + Qt6::Qml + Qt6::Quick +) + +qt_add_qml_module(attached + URI People + QML_FILES Main.qml + DEPENDENCIES + QtQuick +) + +install(TARGETS attached + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET attached + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/Main.qml new file mode 100644 index 0000000000..8175eae209 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/Main.qml @@ -0,0 +1,23 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import People +import QtQuick // For QColor + +BirthdayParty { + Boy { + name: "Robert Campbell" + BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-01", "yyyy-MM-dd") + } + + Boy { + name: "Leo Hodges" + shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 } + BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-03", "yyyy-MM-dd") + } + + host: Boy { + name: "Jack Smith" + shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 } + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.pro b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.pro new file mode 100644 index 0000000000..ab154eb48b --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.pro @@ -0,0 +1,12 @@ +QT += qml quick + +CONFIG += qmltypes +QML_IMPORT_NAME = People +QML_IMPORT_MAJOR_VERSION = 1 + +SOURCES += main.cpp \ + person.cpp \ + birthdayparty.cpp +HEADERS += person.h \ + birthdayparty.h +RESOURCES += attached.qrc diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.qrc new file mode 100644 index 0000000000..b1eeb489e2 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/attached.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/advanced5-Attached-properties/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.cpp new file mode 100644 index 0000000000..0379a7accf --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "birthdayparty.h" + +QDate BirthdayPartyAttached::rsvp() const +{ + return m_rsvp; +} + +void BirthdayPartyAttached::setRsvp(QDate rsvpDate) +{ + if (m_rsvp != rsvpDate) { + m_rsvp = rsvpDate; + emit rsvpChanged(); + } +} + +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(); +} + +BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object) +{ + return new BirthdayPartyAttached(object); +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h new file mode 100644 index 0000000000..1b2503895a --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h @@ -0,0 +1,72 @@ +// 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 <QDate> +#include <QObject> +#include <qqml.h> + +class BirthdayPartyAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged FINAL) + QML_ANONYMOUS +public: + using QObject::QObject; + + QDate rsvp() const; + void setRsvp(QDate); + +signals: + void rsvpChanged(); + +private: + QDate m_rsvp; +}; + +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 + QML_ATTACHED(BirthdayPartyAttached) + +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(); + + static BirthdayPartyAttached *qmlAttachedProperties(QObject *); + +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/advanced5-Attached-properties/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.cpp new file mode 100644 index 0000000000..09691f3b6a --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/main.cpp @@ -0,0 +1,48 @@ +// 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:"; + + for (qsizetype ii = 0; ii < party->guestCount(); ++ii) { + Person *guest = party->guest(ii); + + QDate rsvpDate; + QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false); + + if (attached) + rsvpDate = attached->property("rsvp").toDate(); + if (rsvpDate.isNull()) + qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd"; + else + qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString(); + } + + return EXIT_SUCCESS; + } + + qWarning() << component.errors(); + return EXIT_FAILURE; +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.cpp new file mode 100644 index 0000000000..53cec6b192 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.cpp @@ -0,0 +1,96 @@ +// 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(); + } +} + +bool ShoeDescription::operatorEqualsImpl(const ShoeDescription &lhs, const ShoeDescription &rhs) +{ + return lhs.m_size == rhs.m_size && lhs.m_color == rhs.m_color && lhs.m_brand == rhs.m_brand + && lhs.m_price == rhs.m_price; +} + +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 (!shoe) + return; + + if (*m_shoe != *shoe) { + m_shoe = shoe; + emit shoeChanged(); + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.h new file mode 100644 index 0000000000..4f040e491b --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/person.h @@ -0,0 +1,98 @@ +// 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); + + friend bool operator==(const ShoeDescription &lhs, const ShoeDescription &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + friend bool operator!=(const ShoeDescription &lhs, const ShoeDescription &rhs) + { + return !operatorEqualsImpl(lhs, rhs); + } + +signals: + void shoeChanged(); + +private: + static bool operatorEqualsImpl(const ShoeDescription &, const ShoeDescription &); + + 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/advanced5-Attached-properties/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/qmldir.in new file mode 100644 index 0000000000..1038298c01 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced5-Attached-properties/qmldir.in @@ -0,0 +1,5 @@ +module People +typeinfo attached.qmltypes +prefer :/qt/qml/People/ +Main 254.0 Main.qml +depends QtQuick diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/CMakeLists.txt new file mode 100644 index 0000000000..e6aa49e5cb --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/CMakeLists.txt @@ -0,0 +1,50 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(valuesource LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick) +qt_standard_project_setup() + +qt_policy(SET QTP0001 NEW) + +qt_add_executable(valuesource + birthdayparty.cpp birthdayparty.h + happybirthdaysong.cpp happybirthdaysong.h + main.cpp + person.cpp person.h +) + +set_target_properties(valuesource PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(valuesource PUBLIC + Qt6::Core + Qt6::Qml + Qt6::Quick +) + +qt_add_qml_module(valuesource + URI People + QML_FILES Main.qml + DEPENDENCIES + QtQuick +) + +install(TARGETS valuesource + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET valuesource + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/Main.qml new file mode 100644 index 0000000000..7f79d0a0c4 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/Main.qml @@ -0,0 +1,38 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import People +import QtQuick // For QColor + +BirthdayParty { + id: party + HappyBirthdaySong on announcement { + name: party.host.name + } + + onPartyStarted: (time) => { console.log("This party started rockin' at " + time); } + + + host: Boy { + name: "Bob Jones" + shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 } + } + + Boy { + name: "Leo Hodges" + BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-01", "yyyy-MM-dd") + shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 } + } + Boy { + name: "Jack Smith" + shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 } + } + Girl { + name: "Anne Brown" + BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-03", "yyyy-MM-dd") + shoe.size: 7 + shoe.color: "red" + shoe.brand: "Marc Jacobs" + shoe.price: 99.99 + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.cpp new file mode 100644 index 0000000000..b14f7ef315 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.cpp @@ -0,0 +1,137 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "birthdayparty.h" + +QDate BirthdayPartyAttached::rsvp() const +{ + return m_rsvp; +} + +void BirthdayPartyAttached::setRsvp(QDate rsvpDate) +{ + if (m_rsvp != rsvpDate) { + m_rsvp = rsvpDate; + emit rsvpChanged(); + } +} + +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(); +} + +void BirthdayParty::startParty() +{ + QDateTime time = QDateTime::currentDateTime(); + emit partyStarted(time); +} + +QString BirthdayParty::announcement() const +{ + return m_announcement; +} + +void BirthdayParty::setAnnouncement(const QString &announcement) +{ + if (m_announcement != announcement) { + m_announcement = announcement; + emit announcementChanged(); + } + qInfo().noquote() << announcement; +} + +BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object) +{ + return new BirthdayPartyAttached(object); +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.h new file mode 100644 index 0000000000..799a3fa969 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/birthdayparty.h @@ -0,0 +1,82 @@ +// 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 <QDate> +#include <QDebug> +#include <QObject> +#include <QQmlListProperty> +#include <qqml.h> + +class BirthdayPartyAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged FINAL) + QML_ANONYMOUS +public: + using QObject::QObject; + + QDate rsvp() const; + void setRsvp(QDate); + +signals: + void rsvpChanged(); + +private: + QDate m_rsvp; +}; + +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_PROPERTY(QString announcement READ announcement WRITE setAnnouncement NOTIFY announcementChanged FINAL) + Q_CLASSINFO("DefaultProperty", "guests") + QML_ELEMENT + QML_ATTACHED(BirthdayPartyAttached) +public: + using QObject::QObject; + + Person *host() const; + void setHost(Person *); + + QString announcement() const; + void setAnnouncement(const QString &); + + QQmlListProperty<Person> guests(); + void appendGuest(Person *); + qsizetype guestCount() const; + Person *guest(qsizetype) const; + void clearGuests(); + void replaceGuest(qsizetype, Person *); + void removeLastGuest(); + + static BirthdayPartyAttached *qmlAttachedProperties(QObject *); + + void startParty(); + +signals: + void hostChanged(); + void guestsChanged(); + void partyStarted(QDateTime time); + void announcementChanged(); + +private: + static void appendGuest(QQmlListProperty<Person> *, 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; + QString m_announcement; +}; + +#endif // BIRTHDAYPARTY_H diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.cpp new file mode 100644 index 0000000000..7a756a4a71 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "happybirthdaysong.h" + +#include <QTimer> + +HappyBirthdaySong::HappyBirthdaySong(QObject *parent) : QObject(parent) +{ + auto *timer = new QTimer(this); + QObject::connect(timer, &QTimer::timeout, this, &HappyBirthdaySong::advance); + timer->start(1000); +} + +void HappyBirthdaySong::setTarget(const QQmlProperty &target) +{ + m_target = target; +} + +QString HappyBirthdaySong::name() const +{ + return m_name; +} + +void HappyBirthdaySong::setName(const QString &name) +{ + if (m_name != name) { + m_name = name; + emit nameChanged(); + } + + m_lyrics.clear(); + m_lyrics << "Happy birthday to you,"; + m_lyrics << "Happy birthday to you,"; + m_lyrics << "Happy birthday dear " + m_name + ","; + m_lyrics << "Happy birthday to you!"; + m_lyrics << ""; +} + +void HappyBirthdaySong::advance() +{ + m_line = (m_line + 1) % m_lyrics.count(); + + m_target.write(m_lyrics.at(m_line)); +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h new file mode 100644 index 0000000000..13907d5485 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef HAPPYBIRTHDAYSONG_H +#define HAPPYBIRTHDAYSONG_H + +#include <QQmlProperty> +#include <QQmlPropertyValueSource> +#include <qqml.h> +#include <QStringList> + +class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource +{ + Q_OBJECT + Q_INTERFACES(QQmlPropertyValueSource) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) + QML_ELEMENT +public: + explicit HappyBirthdaySong(QObject *parent = nullptr); + + void setTarget(const QQmlProperty &) override; + + QString name() const; + void setName(const QString &); + +signals: + void nameChanged(); + +private slots: + void advance(); + +private: + qsizetype m_line = -1; + QStringList m_lyrics; + QQmlProperty m_target; + QString m_name; +}; + +#endif // HAPPYBIRTHDAYSONG_H diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.cpp new file mode 100644 index 0000000000..6d67b6179e --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/main.cpp @@ -0,0 +1,49 @@ +// 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:"; + + for (qsizetype ii = 0; ii < party->guestCount(); ++ii) { + Person *guest = party->guest(ii); + + QDate rsvpDate; + QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false); + if (attached) + rsvpDate = attached->property("rsvp").toDate(); + + if (rsvpDate.isNull()) + qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd"; + else + qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString(); + } + + party->startParty(); + return QCoreApplication::exec(); + } + + qWarning() << component.errors(); + return EXIT_FAILURE; +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.cpp new file mode 100644 index 0000000000..53cec6b192 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.cpp @@ -0,0 +1,96 @@ +// 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(); + } +} + +bool ShoeDescription::operatorEqualsImpl(const ShoeDescription &lhs, const ShoeDescription &rhs) +{ + return lhs.m_size == rhs.m_size && lhs.m_color == rhs.m_color && lhs.m_brand == rhs.m_brand + && lhs.m_price == rhs.m_price; +} + +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 (!shoe) + return; + + if (*m_shoe != *shoe) { + m_shoe = shoe; + emit shoeChanged(); + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.h new file mode 100644 index 0000000000..4f040e491b --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/person.h @@ -0,0 +1,98 @@ +// 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); + + friend bool operator==(const ShoeDescription &lhs, const ShoeDescription &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + friend bool operator!=(const ShoeDescription &lhs, const ShoeDescription &rhs) + { + return !operatorEqualsImpl(lhs, rhs); + } + +signals: + void shoeChanged(); + +private: + static bool operatorEqualsImpl(const ShoeDescription &, const ShoeDescription &); + + 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/advanced6-Property-value-source/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/qmldir.in new file mode 100644 index 0000000000..4c63c729d6 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/qmldir.in @@ -0,0 +1,5 @@ +module People +typeinfo valuesource.qmltypes +prefer :/qt/qml/People/ +Main 254.0 Main.qml +depends QtQuick diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.pro b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.pro new file mode 100644 index 0000000000..c55299cecf --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.pro @@ -0,0 +1,14 @@ +QT += qml quick + +CONFIG += qmltypes +QML_IMPORT_NAME = People +QML_IMPORT_MAJOR_VERSION = 1 + +SOURCES += main.cpp \ + person.cpp \ + birthdayparty.cpp \ + happybirthdaysong.cpp +HEADERS += person.h \ + birthdayparty.h \ + happybirthdaysong.h +RESOURCES += valuesource.qrc diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.qrc new file mode 100644 index 0000000000..b1eeb489e2 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced6-Property-value-source/valuesource.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/advanced7-Foreign-objects-integration/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/CMakeLists.txt new file mode 100644 index 0000000000..ffdb5b0949 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/CMakeLists.txt @@ -0,0 +1,63 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(foreign LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Qml Quick Gui) +qt_standard_project_setup() + +qt_policy(SET QTP0001 NEW) + +add_subdirectory(library/) + +qt_add_executable(foreign + birthdayparty.cpp + birthdayparty.h + foreigndisplay.h + happybirthdaysong.cpp + happybirthdaysong.h + person.cpp + person.h + main.cpp +) + +target_link_libraries(foreign PUBLIC + Qt6::Core + Qt6::Qml + Qt6::Gui + library +) + +set_target_properties(foreign PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_include_directories(foreign PUBLIC + "${PROJECT_BINARY_DIR}" + "${PROJECT_SOURCE_DIR}/library" +) + +qt_add_qml_module(foreign + URI People + QML_FILES Main.qml + SOURCES foreigndisplay.h + DEPENDENCIES + QtQuick +) + +install(TARGETS foreign + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET foreign + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/Main.qml b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/Main.qml new file mode 100644 index 0000000000..988bea49f8 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/Main.qml @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import People +import QtQuick // For QColor + +BirthdayParty { + id: party + HappyBirthdaySong on announcement { + name: party.host.name + } + + display: ThirdPartyDisplay { + foregroundColor: "black" + backgroundColor: "white" + } + + onPartyStarted: (time) => { console.log("This party started rockin' at " + time); } + + + host: Boy { + name: "Bob Jones" + shoe { size: 12; color: "white"; brand: "Nike"; price: 90.0 } + } + + Boy { + name: "Leo Hodges" + BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-01", "yyyy-MM-dd") + shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 } + } + Boy { + name: "Jack Smith" + shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 } + } + Girl { + name: "Anne Brown" + BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-03", "yyyy-MM-dd") + shoe.size: 7 + shoe.color: "red" + shoe.brand: "Marc Jacobs" + shoe.price: 99.99 + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/application.pro b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/application.pro new file mode 100644 index 0000000000..b53397d49c --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/application.pro @@ -0,0 +1,26 @@ +TEMPLATE = app + +CONFIG += console + +QT += core qml + +DEPENDPATH += library +INCLUDEPATH += library +LIBS += -Llibrary/ -llibrary + +SOURCES += \ + birthdayparty.cpp \ + happybirthdaysong.cpp \ + main.cpp \ + person.cpp +HEADERS += \ + birthdayparty.h \ + foreigndisplay.h \ + happybirthdaysong.h \ + person.h + +CONFIG += qmltypes +QML_IMPORT_NAME = People +QML_IMPORT_MAJOR_VERSION = 1 + +RESOURCES += foreign.qrc diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.cpp new file mode 100644 index 0000000000..7a9debe195 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.cpp @@ -0,0 +1,150 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "birthdayparty.h" + +QDate BirthdayPartyAttached::rsvp() const +{ + return m_rsvp; +} + +void BirthdayPartyAttached::setRsvp(QDate rsvpDate) +{ + if (m_rsvp != rsvpDate) { + m_rsvp = rsvpDate; + emit rsvpChanged(); + } +} + +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(); +} + +void BirthdayParty::startParty() +{ + QDateTime time = QDateTime::currentDateTime(); + emit partyStarted(time); +} + +QString BirthdayParty::announcement() const +{ + return m_announcement; +} + +void BirthdayParty::setAnnouncement(const QString &announcement) +{ + if (m_announcement != announcement) { + m_announcement = announcement; + emit announcementChanged(); + } + m_display->setContent(announcement); +} + +ThirdPartyDisplay *BirthdayParty::display() const +{ + return m_display; +} + +void BirthdayParty::setDisplay(ThirdPartyDisplay *display) +{ + if (m_display != display) { + m_display = display; + emit displayChanged(); + } +} + +BirthdayPartyAttached *BirthdayParty::qmlAttachedProperties(QObject *object) +{ + return new BirthdayPartyAttached(object); +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.h new file mode 100644 index 0000000000..59c53f2484 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.h @@ -0,0 +1,90 @@ +// 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 "ThirdPartyDisplay.h" + +#include <QDate> +#include <QDebug> +#include <QObject> +#include <qqml.h> + +#include <memory> + +class BirthdayPartyAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged FINAL) + QML_ANONYMOUS +public: + using QObject::QObject; + + QDate rsvp() const; + void setRsvp(QDate); + +signals: + void rsvpChanged(); + +private: + QDate m_rsvp; +}; + +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_PROPERTY(QString announcement READ announcement WRITE setAnnouncement NOTIFY announcementChanged FINAL) + Q_PROPERTY(ThirdPartyDisplay *display READ display WRITE setDisplay NOTIFY displayChanged FINAL) + Q_CLASSINFO("DefaultProperty", "guests") + QML_ELEMENT + QML_ATTACHED(BirthdayPartyAttached) +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(); + + QString announcement() const; + void setAnnouncement(const QString &); + + ThirdPartyDisplay *display() const; + void setDisplay(ThirdPartyDisplay *); + + static BirthdayPartyAttached *qmlAttachedProperties(QObject *); + + void startParty(); + +signals: + void hostChanged(); + void guestsChanged(); + void partyStarted(QDateTime time); + void announcementChanged(); + void displayChanged(); + +private: + static void appendGuest(QQmlListProperty<Person> *, 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; + QString m_announcement; + ThirdPartyDisplay *m_display = nullptr; +}; + +#endif // BIRTHDAYPARTY_H diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.pro b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.pro new file mode 100644 index 0000000000..b637cb0840 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + application.pro \ + library + +application.depends = library diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.qrc b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.qrc new file mode 100644 index 0000000000..b1eeb489e2 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreign.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/advanced7-Foreign-objects-integration/foreigndisplay.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h new file mode 100644 index 0000000000..ee42ca965c --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h @@ -0,0 +1,20 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef FOREIGNDISPLAY_H +#define FOREIGNDISPLAY_H + +#include "ThirdPartyDisplay.h" + +#include <QColor> +#include <QObject> +#include <qqml.h> + +class ForeignDisplay : public QObject +{ + Q_OBJECT + QML_NAMED_ELEMENT(ThirdPartyDisplay) + QML_FOREIGN(ThirdPartyDisplay) +}; + +#endif // FOREIGNDISPLAY_H diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.cpp new file mode 100644 index 0000000000..7a756a4a71 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "happybirthdaysong.h" + +#include <QTimer> + +HappyBirthdaySong::HappyBirthdaySong(QObject *parent) : QObject(parent) +{ + auto *timer = new QTimer(this); + QObject::connect(timer, &QTimer::timeout, this, &HappyBirthdaySong::advance); + timer->start(1000); +} + +void HappyBirthdaySong::setTarget(const QQmlProperty &target) +{ + m_target = target; +} + +QString HappyBirthdaySong::name() const +{ + return m_name; +} + +void HappyBirthdaySong::setName(const QString &name) +{ + if (m_name != name) { + m_name = name; + emit nameChanged(); + } + + m_lyrics.clear(); + m_lyrics << "Happy birthday to you,"; + m_lyrics << "Happy birthday to you,"; + m_lyrics << "Happy birthday dear " + m_name + ","; + m_lyrics << "Happy birthday to you!"; + m_lyrics << ""; +} + +void HappyBirthdaySong::advance() +{ + m_line = (m_line + 1) % m_lyrics.count(); + + m_target.write(m_lyrics.at(m_line)); +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.h new file mode 100644 index 0000000000..f87521a760 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/happybirthdaysong.h @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef HAPPYBIRTHDAYSONG_H +#define HAPPYBIRTHDAYSONG_H + +#include <QQmlPropertyValueSource> +#include <QQmlProperty> +#include <qqml.h> +#include <QStringList> + +class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource +{ + Q_OBJECT + Q_INTERFACES(QQmlPropertyValueSource) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) + QML_ELEMENT +public: + explicit HappyBirthdaySong(QObject *parent = nullptr); + + void setTarget(const QQmlProperty &) override; + + QString name() const; + void setName(const QString &); + +signals: + void nameChanged(); + +private slots: + void advance(); + +private: + qsizetype m_line = -1; + QStringList m_lyrics; + QQmlProperty m_target; + QString m_name; +}; + +#endif // HAPPYBIRTHDAYSONG_H diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/CMakeLists.txt b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/CMakeLists.txt new file mode 100644 index 0000000000..7fa6d6a81a --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) + +project(library) + +add_library(library ThirdPartyDisplay.cpp ThirdPartyDisplay.h) + +qt_extract_metatypes(library) + +target_link_libraries(library PUBLIC + Qt6::Core + Qt6::Qml + Qt6::Quick + Qt6::Gui +) diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.cpp new file mode 100644 index 0000000000..5fc4eb2e8f --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "ThirdPartyDisplay.h" +#include <QDebug> + +const QString &ThirdPartyDisplay::content() const +{ + return m_content; +} + +void ThirdPartyDisplay::setContent(const QString &content) +{ + if (m_content != content) { + m_content = content; + emit contentChanged(); + } + qInfo() << QStringLiteral("[Fancy ThirdPartyDisplay] ") + content; +} + +QColor ThirdPartyDisplay::foregroundColor() const +{ + return m_foregroundColor; +} + +void ThirdPartyDisplay::setForegroundColor(QColor color) +{ + if (m_foregroundColor != color) { + m_foregroundColor = color; + emit colorsChanged(); + } +} + +QColor ThirdPartyDisplay::backgroundColor() const +{ + return m_backgroundColor; +} + +void ThirdPartyDisplay::setBackgroundColor(QColor color) +{ + if (m_backgroundColor != color) { + m_backgroundColor = color; + emit colorsChanged(); + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h new file mode 100644 index 0000000000..525c9f72cf --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h @@ -0,0 +1,36 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef THIRDPARTYDISPLAY_H +#define THIRDPARTYDISPLAY_H + +#include <QColor> +#include <QObject> + +class Q_DECL_EXPORT ThirdPartyDisplay : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString content READ content WRITE setContent NOTIFY contentChanged FINAL) + Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor NOTIFY colorsChanged FINAL) + Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY colorsChanged FINAL) +public: + const QString &content() const; + void setContent(const QString &content); + + QColor foregroundColor() const; + void setForegroundColor(QColor); + + QColor backgroundColor() const; + void setBackgroundColor(QColor); + +signals: + void contentChanged(); + void colorsChanged(); + +private: + QString m_content; + QColor m_foregroundColor; + QColor m_backgroundColor; +}; + +#endif // THIRDPARTYDISPLAY_H diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/library.pro b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/library.pro new file mode 100644 index 0000000000..f7009c46c9 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/library.pro @@ -0,0 +1,8 @@ +TEMPLATE = lib + +CONFIG += static + +SOURCES += ThirdPartyDisplay.cpp +HEADERS += ThirdPartyDisplay.h + +QT += core qml gui diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/main.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/main.cpp new file mode 100644 index 0000000000..9c6f6bcc2f --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/main.cpp @@ -0,0 +1,50 @@ +// 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 <QQmlEngine> +#include <QQmlComponent> +#include <QDebug> +#include <QFile> + +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:"; + + for (qsizetype ii = 0; ii < party->guestCount(); ++ii) { + Person *guest = party->guest(ii); + + QDate rsvpDate; + QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false); + if (attached) + rsvpDate = attached->property("rsvp").toDate(); + + if (rsvpDate.isNull()) + qInfo() << " " << guest->name() << "RSVP date: Hasn't RSVP'd"; + else + qInfo() << " " << guest->name() << "RSVP date:" << rsvpDate.toString(); + } + + party->startParty(); + return QCoreApplication::exec(); + } + + qWarning() << component.errors(); + return EXIT_FAILURE; +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.cpp b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.cpp new file mode 100644 index 0000000000..53cec6b192 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.cpp @@ -0,0 +1,96 @@ +// 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(); + } +} + +bool ShoeDescription::operatorEqualsImpl(const ShoeDescription &lhs, const ShoeDescription &rhs) +{ + return lhs.m_size == rhs.m_size && lhs.m_color == rhs.m_color && lhs.m_brand == rhs.m_brand + && lhs.m_price == rhs.m_price; +} + +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 (!shoe) + return; + + if (*m_shoe != *shoe) { + m_shoe = shoe; + emit shoeChanged(); + } +} diff --git a/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.h b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.h new file mode 100644 index 0000000000..03c2dab953 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/person.h @@ -0,0 +1,98 @@ +// 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 <QObject> +#include <QColor> + +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); + + friend bool operator==(const ShoeDescription &lhs, const ShoeDescription &rhs) + { + return operatorEqualsImpl(lhs, rhs); + } + friend bool operator!=(const ShoeDescription &lhs, const ShoeDescription &rhs) + { + return !operatorEqualsImpl(lhs, rhs); + } + +signals: + void shoeChanged(); + +private: + static bool operatorEqualsImpl(const ShoeDescription &, const ShoeDescription &); + + 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/advanced7-Foreign-objects-integration/qmldir.in b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/qmldir.in new file mode 100644 index 0000000000..5289a31938 --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/qmldir.in @@ -0,0 +1,5 @@ +module People +typeinfo foreign.qmltypes +prefer :/qt/qml/People/ +Main 254.0 Main.qml +depends QtQuick diff --git a/examples/qml/tutorials/extending-qml-advanced/extending-qml-advanced.pro b/examples/qml/tutorials/extending-qml-advanced/extending-qml-advanced.pro new file mode 100644 index 0000000000..387d880a7d --- /dev/null +++ b/examples/qml/tutorials/extending-qml-advanced/extending-qml-advanced.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + advanced1-Base-project \ + advanced2-Inheritance-and-coercion \ + advanced3-Default-properties \ + advanced4-Grouped-properties + advanced5-Attached-properties \ + advanced6-Property-value-source \ + advanced7-Extension-objects \ diff --git a/examples/qml/tutorials/extending-qml/CMakeLists.txt b/examples/qml/tutorials/extending-qml/CMakeLists.txt index f5fc6e5cfe..87e3eaf54d 100644 --- a/examples/qml/tutorials/extending-qml/CMakeLists.txt +++ b/examples/qml/tutorials/extending-qml/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause qt_internal_add_example(chapter1-basics) qt_internal_add_example(chapter2-methods) diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml b/examples/qml/tutorials/extending-qml/chapter1-basics/App.qml index ed89ce97c4..ed89ce97c4 100644 --- a/examples/qml/tutorials/extending-qml/chapter1-basics/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter1-basics/App.qml diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter1-basics/CMakeLists.txt index 02ed972ef5..0080197766 100644 --- a/examples/qml/tutorials/extending-qml/chapter1-basics/CMakeLists.txt +++ b/examples/qml/tutorials/extending-qml/chapter1-basics/CMakeLists.txt @@ -1,19 +1,13 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(chapter1-basics LANGUAGES CXX) -set(CMAKE_AUTOMOC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") -endif() - -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter1-basics") - find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) +qt_standard_project_setup(REQUIRES 6.5) + qt_add_executable(chapter1-basics main.cpp piechart.cpp piechart.h @@ -25,21 +19,29 @@ set_target_properties(chapter1-basics PROPERTIES ) target_link_libraries(chapter1-basics PUBLIC - Qt::Core - Qt::Gui - Qt::Qml - Qt::Quick + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick ) #![0] qt_add_qml_module(chapter1-basics URI Charts - QML_FILES app.qml - NO_RESOURCE_TARGET_PATH + QML_FILES App.qml DEPENDENCIES QtQuick ) #![0] install(TARGETS chapter1-basics - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET chapter1-basics + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM ) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.qrc b/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.qrc index f1168aef3b..c72f203e67 100644 --- a/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.qrc +++ b/examples/qml/tutorials/extending-qml/chapter1-basics/chapter1-basics.qrc @@ -1,5 +1,6 @@ <RCC> - <qresource prefix="/"> - <file>app.qml</file> + <qresource prefix="/qt/qml/Charts"> + <file>App.qml</file> + <file>qmldir</file> </qresource> </RCC> diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/main.cpp b/examples/qml/tutorials/extending-qml/chapter1-basics/main.cpp index f2eaab03bc..bc4bbba809 100644 --- a/examples/qml/tutorials/extending-qml/chapter1-basics/main.cpp +++ b/examples/qml/tutorials/extending-qml/chapter1-basics/main.cpp @@ -11,7 +11,7 @@ int main(int argc, char *argv[]) QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); - view.setSource(QUrl("qrc:///app.qml")); + view.loadFromModule("Charts", "App"); view.show(); return QGuiApplication::exec(); } diff --git a/examples/qml/tutorials/extending-qml/chapter1-basics/qmldir b/examples/qml/tutorials/extending-qml/chapter1-basics/qmldir new file mode 100644 index 0000000000..f69878a753 --- /dev/null +++ b/examples/qml/tutorials/extending-qml/chapter1-basics/qmldir @@ -0,0 +1,6 @@ +module Charts +typeinfo chapter1-basics.qmltypes +depends QtQuick +prefer :/qt/qml/Charts/ +App 254.0 App.qml + diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/app.qml b/examples/qml/tutorials/extending-qml/chapter2-methods/App.qml index 56597c3f42..56597c3f42 100644 --- a/examples/qml/tutorials/extending-qml/chapter2-methods/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter2-methods/App.qml diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter2-methods/CMakeLists.txt index f0906cf380..3fc7d3ffbf 100644 --- a/examples/qml/tutorials/extending-qml/chapter2-methods/CMakeLists.txt +++ b/examples/qml/tutorials/extending-qml/chapter2-methods/CMakeLists.txt @@ -1,19 +1,13 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(chapter2-methods LANGUAGES CXX) -set(CMAKE_AUTOMOC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") -endif() - -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter2-methods") - find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) +qt_standard_project_setup(REQUIRES 6.5) + qt_add_executable(chapter2-methods main.cpp piechart.cpp piechart.h @@ -25,21 +19,29 @@ set_target_properties(chapter2-methods PROPERTIES ) target_link_libraries(chapter2-methods PUBLIC - Qt::Core - Qt::Gui - Qt::Qml - Qt::Quick + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick ) qt_add_qml_module(chapter2-methods URI Charts - QML_FILES app.qml - NO_RESOURCE_TARGET_PATH + QML_FILES App.qml DEPENDENCIES QtQuick ) install(TARGETS chapter2-methods - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET chapter2-methods + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM ) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.qrc b/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.qrc index f1168aef3b..c72f203e67 100644 --- a/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.qrc +++ b/examples/qml/tutorials/extending-qml/chapter2-methods/chapter2-methods.qrc @@ -1,5 +1,6 @@ <RCC> - <qresource prefix="/"> - <file>app.qml</file> + <qresource prefix="/qt/qml/Charts"> + <file>App.qml</file> + <file>qmldir</file> </qresource> </RCC> diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/main.cpp b/examples/qml/tutorials/extending-qml/chapter2-methods/main.cpp index f2eaab03bc..bc4bbba809 100644 --- a/examples/qml/tutorials/extending-qml/chapter2-methods/main.cpp +++ b/examples/qml/tutorials/extending-qml/chapter2-methods/main.cpp @@ -11,7 +11,7 @@ int main(int argc, char *argv[]) QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); - view.setSource(QUrl("qrc:///app.qml")); + view.loadFromModule("Charts", "App"); view.show(); return QGuiApplication::exec(); } diff --git a/examples/qml/tutorials/extending-qml/chapter2-methods/qmldir b/examples/qml/tutorials/extending-qml/chapter2-methods/qmldir new file mode 100644 index 0000000000..e8b0c98311 --- /dev/null +++ b/examples/qml/tutorials/extending-qml/chapter2-methods/qmldir @@ -0,0 +1,6 @@ +module Charts +typeinfo chapter2-methods.qmltypes +depends QtQuick +prefer :/qt/qml/Charts/ +App 254.0 App.qml + diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml b/examples/qml/tutorials/extending-qml/chapter3-bindings/App.qml index d29eaf4b9f..d29eaf4b9f 100644 --- a/examples/qml/tutorials/extending-qml/chapter3-bindings/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/App.qml diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter3-bindings/CMakeLists.txt index 05c567fd30..a842eb48ca 100644 --- a/examples/qml/tutorials/extending-qml/chapter3-bindings/CMakeLists.txt +++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/CMakeLists.txt @@ -1,19 +1,13 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(chapter3-bindings LANGUAGES CXX) -set(CMAKE_AUTOMOC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") -endif() - -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter3-bindings") - find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) +qt_standard_project_setup(REQUIRES 6.5) + qt_add_executable(chapter3-bindings main.cpp piechart.cpp piechart.h @@ -25,21 +19,29 @@ set_target_properties(chapter3-bindings PROPERTIES ) target_link_libraries(chapter3-bindings PUBLIC - Qt::Core - Qt::Gui - Qt::Qml - Qt::Quick + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick ) qt_add_qml_module(chapter3-bindings URI Charts - QML_FILES app.qml - NO_RESOURCE_TARGET_PATH + QML_FILES App.qml DEPENDENCIES QtQuick ) install(TARGETS chapter3-bindings - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET chapter3-bindings + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM ) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.qrc b/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.qrc index f1168aef3b..c72f203e67 100644 --- a/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.qrc +++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/chapter3-bindings.qrc @@ -1,5 +1,6 @@ <RCC> - <qresource prefix="/"> - <file>app.qml</file> + <qresource prefix="/qt/qml/Charts"> + <file>App.qml</file> + <file>qmldir</file> </qresource> </RCC> diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/main.cpp b/examples/qml/tutorials/extending-qml/chapter3-bindings/main.cpp index f2eaab03bc..bc4bbba809 100644 --- a/examples/qml/tutorials/extending-qml/chapter3-bindings/main.cpp +++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/main.cpp @@ -11,7 +11,7 @@ int main(int argc, char *argv[]) QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); - view.setSource(QUrl("qrc:///app.qml")); + view.loadFromModule("Charts", "App"); view.show(); return QGuiApplication::exec(); } diff --git a/examples/qml/tutorials/extending-qml/chapter3-bindings/qmldir b/examples/qml/tutorials/extending-qml/chapter3-bindings/qmldir new file mode 100644 index 0000000000..f0911d3c89 --- /dev/null +++ b/examples/qml/tutorials/extending-qml/chapter3-bindings/qmldir @@ -0,0 +1,6 @@ +module Charts +typeinfo chapter3-bindings.qmltypes +depends QtQuick +prefer :/qt/qml/Charts/ +App 254.0 App.qml + diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/App.qml index 2edcda568e..2edcda568e 100644 --- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/App.qml diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt index 3914819640..fd6428093b 100644 --- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt +++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt @@ -1,18 +1,13 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(chapter4-customPropertyTypes LANGUAGES CXX) -set(CMAKE_AUTOMOC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") -endif() +find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter4-customPropertyTypes") +qt_standard_project_setup(REQUIRES 6.5) -find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) #![0] qt_add_executable(chapter4-customPropertyTypes main.cpp @@ -26,21 +21,29 @@ set_target_properties(chapter4-customPropertyTypes PROPERTIES ) target_link_libraries(chapter4-customPropertyTypes PUBLIC - Qt::Core - Qt::Gui - Qt::Qml - Qt::Quick + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick ) #![1] qt_add_qml_module(chapter4-customPropertyTypes URI Charts - QML_FILES app.qml - NO_RESOURCE_TARGET_PATH + QML_FILES App.qml DEPENDENCIES QtQuick ) #![1] install(TARGETS chapter4-customPropertyTypes - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET chapter4-customPropertyTypes + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM ) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.qrc b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.qrc index f1168aef3b..c72f203e67 100644 --- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.qrc +++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.qrc @@ -1,5 +1,6 @@ <RCC> - <qresource prefix="/"> - <file>app.qml</file> + <qresource prefix="/qt/qml/Charts"> + <file>App.qml</file> + <file>qmldir</file> </qresource> </RCC> diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/main.cpp b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/main.cpp index 8833750ebf..09a025ae85 100644 --- a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/main.cpp +++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/main.cpp @@ -14,7 +14,7 @@ int main(int argc, char *argv[]) QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); - view.setSource(QUrl("qrc:///app.qml")); + view.loadFromModule("Charts", "App"); view.show(); return QGuiApplication::exec(); diff --git a/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/qmldir b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/qmldir new file mode 100644 index 0000000000..14c64f5269 --- /dev/null +++ b/examples/qml/tutorials/extending-qml/chapter4-customPropertyTypes/qmldir @@ -0,0 +1,6 @@ +module Charts +typeinfo chapter4-customPropertyTypes.qmltypes +depends QtQuick +prefer :/qt/qml/Charts/ +App 254.0 App.qml + diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml b/examples/qml/tutorials/extending-qml/chapter5-listproperties/App.qml index 9e200a9b60..9e200a9b60 100644 --- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/App.qml diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter5-listproperties/CMakeLists.txt index d3b70f2017..73984849c0 100644 --- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/CMakeLists.txt +++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/CMakeLists.txt @@ -1,19 +1,13 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(chapter5-listproperties LANGUAGES CXX) -set(CMAKE_AUTOMOC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") -endif() - -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter5-listproperties") - find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) +qt_standard_project_setup(REQUIRES 6.5) + qt_add_executable(chapter5-listproperties main.cpp piechart.cpp piechart.h @@ -26,21 +20,29 @@ set_target_properties(chapter5-listproperties PROPERTIES ) target_link_libraries(chapter5-listproperties PUBLIC - Qt::Core - Qt::Gui - Qt::Qml - Qt::Quick + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick ) qt_add_qml_module(chapter5-listproperties URI Charts - QML_FILES app.qml - NO_RESOURCE_TARGET_PATH + QML_FILES App.qml DEPENDENCIES QtQuick ) install(TARGETS chapter5-listproperties - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_qml_app_script( + TARGET chapter5-listproperties + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM ) +install(SCRIPT ${deploy_script}) diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.qrc b/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.qrc index f1168aef3b..c72f203e67 100644 --- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.qrc +++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/chapter5-listproperties.qrc @@ -1,5 +1,6 @@ <RCC> - <qresource prefix="/"> - <file>app.qml</file> + <qresource prefix="/qt/qml/Charts"> + <file>App.qml</file> + <file>qmldir</file> </qresource> </RCC> diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/main.cpp b/examples/qml/tutorials/extending-qml/chapter5-listproperties/main.cpp index 3d11a689a6..12d4d49438 100644 --- a/examples/qml/tutorials/extending-qml/chapter5-listproperties/main.cpp +++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/main.cpp @@ -12,7 +12,7 @@ int main(int argc, char *argv[]) QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView); - view.setSource(QUrl("qrc:///app.qml")); + view.loadFromModule("Charts", "App"); view.show(); return QGuiApplication::exec(); } diff --git a/examples/qml/tutorials/extending-qml/chapter5-listproperties/qmldir b/examples/qml/tutorials/extending-qml/chapter5-listproperties/qmldir new file mode 100644 index 0000000000..cebd0320ab --- /dev/null +++ b/examples/qml/tutorials/extending-qml/chapter5-listproperties/qmldir @@ -0,0 +1,6 @@ +module Charts +typeinfo chapter5-listproperties.qmltypes +depends QtQuick +prefer :/qt/qml/Charts/ +App 254.0 App.qml + diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml b/examples/qml/tutorials/extending-qml/chapter6-plugins/App.qml index 1d1cb74176..1d1cb74176 100644 --- a/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qml +++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/App.qml diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter6-plugins/CMakeLists.txt index f1a7ef1b51..bd88766e0c 100644 --- a/examples/qml/tutorials/extending-qml/chapter6-plugins/CMakeLists.txt +++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/CMakeLists.txt @@ -1,19 +1,13 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause cmake_minimum_required(VERSION 3.16) project(chapter6-plugins LANGUAGES CXX) -set(CMAKE_AUTOMOC ON) - -if(NOT DEFINED INSTALL_EXAMPLESDIR) - set(INSTALL_EXAMPLESDIR "examples") -endif() - -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/qml/tutorials/extending-qml/chapter6-plugins") - find_package(Qt6 REQUIRED COMPONENTS Qml Quick) +qt_standard_project_setup(REQUIRES 6.5) + qt_add_executable(chapter6-plugins main.cpp ) @@ -24,20 +18,29 @@ set_target_properties(chapter6-plugins PROPERTIES ) target_link_libraries(chapter6-plugins PRIVATE - Qt::Qml - Qt::Quick + Qt6::Qml + Qt6::Quick ) qt_add_qml_module(chapter6-plugins URI ChartsApp - QML_FILES app.qml - NO_RESOURCE_TARGET_PATH + QML_FILES App.qml ) +add_subdirectory(Charts) + install(TARGETS chapter6-plugins - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) -add_subdirectory(Charts) +qt_generate_deploy_qml_app_script( + TARGET chapter6-plugins + OUTPUT_SCRIPT deploy_script + MACOS_BUNDLE_POST_BUILD + NO_UNSUPPORTED_PLATFORM_ERROR + DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM +) +install(SCRIPT ${deploy_script}) + diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt index 4a8f2e0b7b..a0fed166e0 100644 --- a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt +++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause qt6_policy(SET QTP0001 NEW) qt6_add_qml_module(chartsplugin @@ -14,40 +14,16 @@ target_sources(chartsplugin PRIVATE ) target_link_libraries(chartsplugin PRIVATE - Qt::Core - Qt::Gui - Qt::Qml - Qt::Quick + Qt6::Core + Qt6::Gui + Qt6::Qml + Qt6::Quick ) -if(QT6_IS_SHARED_LIBS_BUILD AND APPLE) - get_target_property(is_bundle chapter6-plugins MACOSX_BUNDLE) - if(is_bundle) - # The application's main.cpp adds an explicit QML import path to look for qml modules under - # a PlugIns subdirectory in a macOS bundle. - # Copy the qmldir and shared library qml plugin. - - set(charts_dir "$<TARGET_FILE_DIR:chartsplugin>") - set(chars_qmldir_file "${charts_dir}/qmldir") - set(app_dir "$<TARGET_FILE_DIR:chapter6-plugins>") - set(bundle_charts_dir "${app_dir}/../PlugIns/Charts") - - add_custom_command(TARGET chartsplugin POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${bundle_charts_dir} - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $<TARGET_FILE:chartsplugin> ${bundle_charts_dir} - COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${chars_qmldir_file} ${bundle_charts_dir} - VERBATIM - ) - endif() -endif() - -set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLEDIR}/Charts") install(TARGETS chartsplugin - RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" - BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" - LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts" + LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts" ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qmldir - DESTINATION "${INSTALL_EXAMPLEDIR}") + DESTINATION "${CMAKE_INSTALL_BINDIR}/Charts" +) diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/qmldir b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/qmldir index d9e8471b3c..4536f60e91 100644 --- a/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/qmldir +++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/Charts/qmldir @@ -1,2 +1,5 @@ module Charts -plugin chartsplugin +optional plugin chartsplugin +typeinfo plugins.qmltypes +depends QtQuick +prefer :/qt/qml/Charts/ diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qrc b/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qrc index f1168aef3b..5c4567d0d9 100644 --- a/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qrc +++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/app.qrc @@ -1,5 +1,6 @@ <RCC> - <qresource prefix="/"> - <file>app.qml</file> + <qresource prefix="/qt/qml/ChartsApp"> + <file>App.qml</file> + <file>qmldir</file> </qresource> </RCC> diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/main.cpp b/examples/qml/tutorials/extending-qml/chapter6-plugins/main.cpp index 3475401ebd..7dfb1cf5a7 100644 --- a/examples/qml/tutorials/extending-qml/chapter6-plugins/main.cpp +++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/main.cpp @@ -9,12 +9,12 @@ int main(int argc, char *argv[]) QGuiApplication app(argc, argv); //![0] QQuickView view; -#ifdef Q_OS_OSX +#ifdef Q_OS_MACOS view.engine()->addImportPath(app.applicationDirPath() + "/../PlugIns"); #endif //![0] view.setResizeMode(QQuickView::SizeRootObjectToView); - view.setSource(QUrl("qrc:///app.qml")); + view.loadFromModule("ChartsApp", "App"); view.show(); return app.exec(); } diff --git a/examples/qml/tutorials/extending-qml/chapter6-plugins/qmldir b/examples/qml/tutorials/extending-qml/chapter6-plugins/qmldir new file mode 100644 index 0000000000..d7fa4b260b --- /dev/null +++ b/examples/qml/tutorials/extending-qml/chapter6-plugins/qmldir @@ -0,0 +1,3 @@ +module ChartsApp +prefer :/qt/qml/ChartsApp/ +App 254.0 App.qml |