diff options
author | Mitch Curtis <[email protected]> | 2023-12-18 13:21:39 +0800 |
---|---|---|
committer | Mitch Curtis <[email protected]> | 2024-01-08 10:19:19 +0800 |
commit | c11436857fe2ee6951c97d2d58a9227120c7884c (patch) | |
tree | e162fbd1afa5980b2994929606015dda71478c55 | |
parent | 06e42e733ed6658abbb30ba7be2e571b4533d009 (diff) |
Improve chattutorial example
- Use modern QML type registration.
- Fix qmllint warnings.
- Tidy up code.
- Update copyright year.
Fixes: QTBUG-119986
Change-Id: Ibb47c929a14cd0e786acb7c7496e6cce34f624df
Reviewed-by: Ulf Hermann <[email protected]>
43 files changed, 219 insertions, 187 deletions
diff --git a/examples/quickcontrols/chattutorial/CMakeLists.txt b/examples/quickcontrols/chattutorial/CMakeLists.txt index 12f6ecfd11..2188f9947f 100644 --- a/examples/quickcontrols/chattutorial/CMakeLists.txt +++ b/examples/quickcontrols/chattutorial/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause qt_internal_add_example(chapter1) diff --git a/examples/quickcontrols/chattutorial/chapter1/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter1/CMakeLists.txt index 80be9d275e..eee2bdf6e3 100644 --- a/examples/quickcontrols/chattutorial/chapter1/CMakeLists.txt +++ b/examples/quickcontrols/chattutorial/chapter1/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) @@ -27,9 +27,9 @@ target_link_libraries(chattutorial-chapter1 PRIVATE qt_policy(SET QTP0001 NEW) qt_add_qml_module(chattutorial-chapter1 - URI chapter1 + URI chattutorial QML_FILES - "main.qml" + "Main.qml" ) qt6_add_resources(chattutorial-chapter1 "conf" diff --git a/examples/quickcontrols/chattutorial/chapter1/main.qml b/examples/quickcontrols/chattutorial/chapter1/Main.qml index d0651b5816..19496f44eb 100644 --- a/examples/quickcontrols/chattutorial/chapter1/main.qml +++ b/examples/quickcontrols/chattutorial/chapter1/Main.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick diff --git a/examples/quickcontrols/chattutorial/chapter1/chapter1.pro b/examples/quickcontrols/chattutorial/chapter1/chapter1.pro index 40c7d2ff7d..323b0709d4 100644 --- a/examples/quickcontrols/chattutorial/chapter1/chapter1.pro +++ b/examples/quickcontrols/chattutorial/chapter1/chapter1.pro @@ -1,12 +1,13 @@ TEMPLATE = app QT += qml quick -CONFIG += c++11 SOURCES += main.cpp -resources.files = main.qml -resources.prefix = qt/qml/chapter1/ +resources.files = \ + Main.qml \ + qmldir +resources.prefix = qt/qml/chattutorial/ RESOURCES += resources \ qtquickcontrols2.conf diff --git a/examples/quickcontrols/chattutorial/chapter1/main.cpp b/examples/quickcontrols/chattutorial/chapter1/main.cpp index 7252c0c3ad..73d4bf3b63 100644 --- a/examples/quickcontrols/chattutorial/chapter1/main.cpp +++ b/examples/quickcontrols/chattutorial/chapter1/main.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include <QGuiApplication> @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) QGuiApplication app(argc, argv); QQmlApplicationEngine engine; - engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter1/main.qml"))); + engine.loadFromModule("chattutorial", "Main"); return app.exec(); } diff --git a/examples/quickcontrols/chattutorial/chapter1/qmldir b/examples/quickcontrols/chattutorial/chapter1/qmldir new file mode 100644 index 0000000000..1044557225 --- /dev/null +++ b/examples/quickcontrols/chattutorial/chapter1/qmldir @@ -0,0 +1,2 @@ +module chattutorial +Main 1.0 Main.qml diff --git a/examples/quickcontrols/chattutorial/chapter2/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter2/CMakeLists.txt index bc531ef529..dd2835740c 100644 --- a/examples/quickcontrols/chattutorial/chapter2/CMakeLists.txt +++ b/examples/quickcontrols/chattutorial/chapter2/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) @@ -27,9 +27,9 @@ target_link_libraries(chattutorial-chapter2 PRIVATE qt_policy(SET QTP0001 NEW) qt_add_qml_module(chattutorial-chapter2 - URI chapter2 + URI chattutorial QML_FILES - "main.qml" + "Main.qml" RESOURCES "images/Albert_Einstein.png" "images/[email protected]" diff --git a/examples/quickcontrols/chattutorial/chapter2/main.qml b/examples/quickcontrols/chattutorial/chapter2/Main.qml index 851b482b01..daa4360fb9 100644 --- a/examples/quickcontrols/chattutorial/chapter2/main.qml +++ b/examples/quickcontrols/chattutorial/chapter2/Main.qml @@ -1,6 +1,8 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls @@ -29,14 +31,17 @@ ApplicationWindow { spacing: 20 model: ["Albert Einstein", "Ernest Hemingway", "Hans Gude"] delegate: ItemDelegate { + id: contactDelegate text: modelData width: listView.width - listView.leftMargin - listView.rightMargin height: avatar.implicitHeight leftPadding: avatar.implicitWidth + 32 + required property string modelData + Image { id: avatar - source: "images/" + modelData.replace(" ", "_") + ".png" + source: "images/" + contactDelegate.modelData.replace(" ", "_") + ".png" } } } diff --git a/examples/quickcontrols/chattutorial/chapter2/chapter2.pro b/examples/quickcontrols/chattutorial/chapter2/chapter2.pro index 8679db986a..88f70ce08a 100644 --- a/examples/quickcontrols/chattutorial/chapter2/chapter2.pro +++ b/examples/quickcontrols/chattutorial/chapter2/chapter2.pro @@ -1,7 +1,6 @@ TEMPLATE = app QT += qml quick -CONFIG += c++11 SOURCES += main.cpp @@ -18,8 +17,9 @@ resources.files = \ images/[email protected] \ images/[email protected] \ images/[email protected] \ - main.qml -resources.prefix = qt/qml/chapter2/ + Main.qml \ + qmldir +resources.prefix = qt/qml/chattutorial/ RESOURCES += resources \ qtquickcontrols2.conf diff --git a/examples/quickcontrols/chattutorial/chapter2/main.cpp b/examples/quickcontrols/chattutorial/chapter2/main.cpp index 07f034a827..73d4bf3b63 100644 --- a/examples/quickcontrols/chattutorial/chapter2/main.cpp +++ b/examples/quickcontrols/chattutorial/chapter2/main.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include <QGuiApplication> @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) QGuiApplication app(argc, argv); QQmlApplicationEngine engine; - engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter2/main.qml"))); + engine.loadFromModule("chattutorial", "Main"); return app.exec(); } diff --git a/examples/quickcontrols/chattutorial/chapter2/qmldir b/examples/quickcontrols/chattutorial/chapter2/qmldir new file mode 100644 index 0000000000..1044557225 --- /dev/null +++ b/examples/quickcontrols/chattutorial/chapter2/qmldir @@ -0,0 +1,2 @@ +module chattutorial +Main 1.0 Main.qml diff --git a/examples/quickcontrols/chattutorial/chapter3/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter3/CMakeLists.txt index ad853842f2..de4a16803a 100644 --- a/examples/quickcontrols/chattutorial/chapter3/CMakeLists.txt +++ b/examples/quickcontrols/chattutorial/chapter3/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) @@ -27,11 +27,11 @@ target_link_libraries(chattutorial-chapter3 PRIVATE qt_policy(SET QTP0001 NEW) qt_add_qml_module(chattutorial-chapter3 - URI chapter3 + URI chattutorial QML_FILES "ContactPage.qml" "ConversationPage.qml" - "main.qml" + "Main.qml" RESOURCES "images/Albert_Einstein.png" "images/[email protected]" diff --git a/examples/quickcontrols/chattutorial/chapter3/ContactPage.qml b/examples/quickcontrols/chattutorial/chapter3/ContactPage.qml index 88979a40b2..939d911964 100644 --- a/examples/quickcontrols/chattutorial/chapter3/ContactPage.qml +++ b/examples/quickcontrols/chattutorial/chapter3/ContactPage.qml @@ -1,6 +1,8 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls @@ -25,15 +27,19 @@ Page { spacing: 20 model: ["Albert Einstein", "Ernest Hemingway", "Hans Gude"] delegate: ItemDelegate { + id: contactDelegate text: modelData width: listView.width - listView.leftMargin - listView.rightMargin height: avatar.implicitHeight leftPadding: avatar.implicitWidth + 32 + + required property string modelData + onClicked: root.StackView.view.push("ConversationPage.qml", { inConversationWith: modelData }) Image { id: avatar - source: "images/" + modelData.replace(" ", "_") + ".png" + source: "images/" + contactDelegate.modelData.replace(" ", "_") + ".png" } } } diff --git a/examples/quickcontrols/chattutorial/chapter3/ConversationPage.qml b/examples/quickcontrols/chattutorial/chapter3/ConversationPage.qml index e61d014e6e..8f3a64485c 100644 --- a/examples/quickcontrols/chattutorial/chapter3/ConversationPage.qml +++ b/examples/quickcontrols/chattutorial/chapter3/ConversationPage.qml @@ -1,6 +1,8 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Layouts import QtQuick.Controls @@ -21,7 +23,7 @@ Page { Label { id: pageTitle - text: inConversationWith + text: root.inConversationWith font.pixelSize: 20 anchors.centerIn: parent } @@ -41,28 +43,30 @@ Page { spacing: 12 model: 10 delegate: Row { - readonly property bool sentByMe: index % 2 == 0 - + id: messageDelegate anchors.right: sentByMe ? listView.contentItem.right : undefined spacing: 6 + required property int index + readonly property bool sentByMe: index % 2 == 0 + Rectangle { id: avatar width: height height: parent.height color: "grey" - visible: !sentByMe + visible: !messageDelegate.sentByMe } Rectangle { width: 80 height: 40 - color: sentByMe ? "lightgrey" : "steelblue" + color: messageDelegate.sentByMe ? "lightgrey" : "steelblue" Label { anchors.centerIn: parent - text: index - color: sentByMe ? "black" : "white" + text: messageDelegate.index + color: messageDelegate.sentByMe ? "black" : "white" } } } diff --git a/examples/quickcontrols/chattutorial/chapter5/main.qml b/examples/quickcontrols/chattutorial/chapter3/Main.qml index da080e3a45..bb968e9f7a 100644 --- a/examples/quickcontrols/chattutorial/chapter5/main.qml +++ b/examples/quickcontrols/chattutorial/chapter3/Main.qml @@ -1,7 +1,6 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick import QtQuick.Controls ApplicationWindow { diff --git a/examples/quickcontrols/chattutorial/chapter3/chapter3.pro b/examples/quickcontrols/chattutorial/chapter3/chapter3.pro index 5314e2dd36..b0eebae94a 100644 --- a/examples/quickcontrols/chattutorial/chapter3/chapter3.pro +++ b/examples/quickcontrols/chattutorial/chapter3/chapter3.pro @@ -1,7 +1,6 @@ TEMPLATE = app QT += qml quick -CONFIG += c++11 SOURCES += main.cpp @@ -20,8 +19,9 @@ resources.files = \ images/[email protected] \ images/[email protected] \ images/[email protected] \ - main.qml -resources.prefix = qt/qml/chapter3/ + Main.qml \ + qmldir +resources.prefix = qt/qml/chattutorial/ RESOURCES += resources \ qtquickcontrols2.conf diff --git a/examples/quickcontrols/chattutorial/chapter3/main.cpp b/examples/quickcontrols/chattutorial/chapter3/main.cpp index 71e250bed5..73d4bf3b63 100644 --- a/examples/quickcontrols/chattutorial/chapter3/main.cpp +++ b/examples/quickcontrols/chattutorial/chapter3/main.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include <QGuiApplication> @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) QGuiApplication app(argc, argv); QQmlApplicationEngine engine; - engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter3/main.qml"))); + engine.loadFromModule("chattutorial", "Main"); return app.exec(); } diff --git a/examples/quickcontrols/chattutorial/chapter3/qmldir b/examples/quickcontrols/chattutorial/chapter3/qmldir new file mode 100644 index 0000000000..16e455a37b --- /dev/null +++ b/examples/quickcontrols/chattutorial/chapter3/qmldir @@ -0,0 +1,4 @@ +module chattutorial +ContactPage 1.0 ContactPage.qml +ConversationPage 1.0 ConversationPage.qml +Main 1.0 Main.qml diff --git a/examples/quickcontrols/chattutorial/chapter4/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter4/CMakeLists.txt index a962c10a53..0181c2dee8 100644 --- a/examples/quickcontrols/chattutorial/chapter4/CMakeLists.txt +++ b/examples/quickcontrols/chattutorial/chapter4/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) @@ -30,11 +30,11 @@ target_link_libraries(chattutorial-chapter4 PRIVATE qt_policy(SET QTP0001 NEW) qt_add_qml_module(chattutorial-chapter4 - URI chapter4 + URI chattutorial QML_FILES "ContactPage.qml" "ConversationPage.qml" - "main.qml" + "Main.qml" RESOURCES "images/Albert_Einstein.png" "images/[email protected]" diff --git a/examples/quickcontrols/chattutorial/chapter4/ContactPage.qml b/examples/quickcontrols/chattutorial/chapter4/ContactPage.qml index 739d0b5402..2ed2243289 100644 --- a/examples/quickcontrols/chattutorial/chapter4/ContactPage.qml +++ b/examples/quickcontrols/chattutorial/chapter4/ContactPage.qml @@ -1,10 +1,12 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls -import io.qt.examples.chattutorial +import chattutorial Page { id: root @@ -27,15 +29,21 @@ Page { spacing: 20 model: SqlContactModel {} delegate: ItemDelegate { + id: contactDelegate text: model.display width: listView.width - listView.leftMargin - listView.rightMargin height: avatar.implicitHeight leftPadding: avatar.implicitWidth + 32 + + // Use "model" rather than the specific "display" role, because it + // would conflict with the display property of ItemDelegate. + required property var model + onClicked: root.StackView.view.push("ConversationPage.qml", { inConversationWith: model.display }) Image { id: avatar - source: "images/" + model.display.replace(" ", "_") + ".png" + source: "images/" + contactDelegate.model.display.replace(" ", "_") + ".png" } } } diff --git a/examples/quickcontrols/chattutorial/chapter4/ConversationPage.qml b/examples/quickcontrols/chattutorial/chapter4/ConversationPage.qml index ef5b959aec..44c39b40bd 100644 --- a/examples/quickcontrols/chattutorial/chapter4/ConversationPage.qml +++ b/examples/quickcontrols/chattutorial/chapter4/ConversationPage.qml @@ -1,11 +1,13 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Layouts import QtQuick.Controls -import io.qt.examples.chattutorial +import chattutorial Page { id: root @@ -23,7 +25,7 @@ Page { Label { id: pageTitle - text: inConversationWith + text: root.inConversationWith font.pixelSize: 20 anchors.centerIn: parent } @@ -42,34 +44,40 @@ Page { verticalLayoutDirection: ListView.BottomToTop spacing: 12 model: SqlConversationModel { - recipient: inConversationWith + recipient: root.inConversationWith } delegate: Column { + id: conversationDelegate anchors.right: sentByMe ? listView.contentItem.right : undefined spacing: 6 - readonly property bool sentByMe: model.recipient !== "Me" + required property string author + required property string recipient + required property date timestamp + required property string message + readonly property bool sentByMe: recipient !== "Me" Row { id: messageRow spacing: 6 - anchors.right: sentByMe ? parent.right : undefined + anchors.right: conversationDelegate.sentByMe ? parent.right : undefined Image { id: avatar - source: !sentByMe ? "images/" + model.author.replace(" ", "_") + ".png" : "" + source: !conversationDelegate.sentByMe + ? "images/" + conversationDelegate.author.replace(" ", "_") + ".png" : "" } Rectangle { width: Math.min(messageText.implicitWidth + 24, - listView.width - (!sentByMe ? avatar.width + messageRow.spacing : 0)) + listView.width - (!conversationDelegate.sentByMe ? avatar.width + messageRow.spacing : 0)) height: messageText.implicitHeight + 24 - color: sentByMe ? "lightgrey" : "steelblue" + color: conversationDelegate.sentByMe ? "lightgrey" : "steelblue" Label { id: messageText - text: model.message - color: sentByMe ? "black" : "white" + text: conversationDelegate.message + color: conversationDelegate.sentByMe ? "black" : "white" anchors.fill: parent anchors.margins: 12 wrapMode: Label.Wrap @@ -79,9 +87,9 @@ Page { Label { id: timestampText - text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm") + text: Qt.formatDateTime(conversationDelegate.timestamp, "d MMM hh:mm") color: "lightgrey" - anchors.right: sentByMe ? parent.right : undefined + anchors.right: conversationDelegate.sentByMe ? parent.right : undefined } } @@ -107,8 +115,8 @@ Page { text: qsTr("Send") enabled: messageField.length > 0 onClicked: { - listView.model.sendMessage(inConversationWith, messageField.text); - messageField.text = ""; + listView.model.sendMessage(root.inConversationWith, messageField.text) + messageField.text = "" } } } diff --git a/examples/quickcontrols/chattutorial/chapter3/main.qml b/examples/quickcontrols/chattutorial/chapter4/Main.qml index da080e3a45..bb968e9f7a 100644 --- a/examples/quickcontrols/chattutorial/chapter3/main.qml +++ b/examples/quickcontrols/chattutorial/chapter4/Main.qml @@ -1,7 +1,6 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick import QtQuick.Controls ApplicationWindow { diff --git a/examples/quickcontrols/chattutorial/chapter4/chapter4.pro b/examples/quickcontrols/chattutorial/chapter4/chapter4.pro index 399b3506c2..4f40fd726b 100644 --- a/examples/quickcontrols/chattutorial/chapter4/chapter4.pro +++ b/examples/quickcontrols/chattutorial/chapter4/chapter4.pro @@ -1,7 +1,11 @@ TEMPLATE = app QT += qml quick sql -CONFIG += c++11 +CONFIG += c++11 qmltypes + +QML_IMPORT_PATH = $$pwd/. +QML_IMPORT_NAME = chattutorial +QML_IMPORT_MAJOR_VERSION = 1 HEADERS += sqlcontactmodel.h \ sqlconversationmodel.h @@ -25,8 +29,9 @@ resources.files = \ images/[email protected] \ images/[email protected] \ images/[email protected] \ - main.qml -resources.prefix = qt/qml/chapter4/ + Main.qml \ + qmldir +resources.prefix = qt/qml/chattutorial/ RESOURCES += resources \ qtquickcontrols2.conf diff --git a/examples/quickcontrols/chattutorial/chapter4/main.cpp b/examples/quickcontrols/chattutorial/chapter4/main.cpp index 4c7289deb3..bcb6c1e923 100644 --- a/examples/quickcontrols/chattutorial/chapter4/main.cpp +++ b/examples/quickcontrols/chattutorial/chapter4/main.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include <QtCore> @@ -7,9 +7,6 @@ #include <QSqlError> #include <QtQml> -#include "sqlcontactmodel.h" -#include "sqlconversationmodel.h" - static void connectToDatabase() { QSqlDatabase database = QSqlDatabase::database(); @@ -37,13 +34,10 @@ int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); - qmlRegisterType<SqlContactModel>("io.qt.examples.chattutorial", 1, 0, "SqlContactModel"); - qmlRegisterType<SqlConversationModel>("io.qt.examples.chattutorial", 1, 0, "SqlConversationModel"); - connectToDatabase(); QQmlApplicationEngine engine; - engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter4/main.qml"))); + engine.loadFromModule("chattutorial", "Main"); if (engine.rootObjects().isEmpty()) return -1; diff --git a/examples/quickcontrols/chattutorial/chapter4/qmldir b/examples/quickcontrols/chattutorial/chapter4/qmldir new file mode 100644 index 0000000000..27f99a4777 --- /dev/null +++ b/examples/quickcontrols/chattutorial/chapter4/qmldir @@ -0,0 +1,5 @@ +module chattutorial +ContactPage 1.0 ContactPage.qml +ConversationPage 1.0 ConversationPage.qml +Main 1.0 Main.qml +typeinfo chapter4.qmltypes diff --git a/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.cpp b/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.cpp index cce8cffbb8..189924deec 100644 --- a/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.cpp +++ b/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "sqlcontactmodel.h" diff --git a/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.h b/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.h index a6d24a4774..c7f9a154eb 100644 --- a/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.h +++ b/examples/quickcontrols/chattutorial/chapter4/sqlcontactmodel.h @@ -1,13 +1,17 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef SQLCONTACTMODEL_H #define SQLCONTACTMODEL_H +#include <QQmlEngine> #include <QSqlQueryModel> class SqlContactModel : public QSqlQueryModel { + Q_OBJECT + QML_ELEMENT + public: SqlContactModel(QObject *parent = nullptr); }; diff --git a/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.cpp b/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.cpp index 1a1594b094..5be01de52c 100644 --- a/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.cpp +++ b/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "sqlconversationmodel.h" diff --git a/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.h b/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.h index 8a3cdd4c43..b4917c0eff 100644 --- a/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.h +++ b/examples/quickcontrols/chattutorial/chapter4/sqlconversationmodel.h @@ -1,14 +1,16 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef SQLCONVERSATIONMODEL_H #define SQLCONVERSATIONMODEL_H +#include <QQmlEngine> #include <QSqlTableModel> class SqlConversationModel : public QSqlTableModel { Q_OBJECT + QML_ELEMENT Q_PROPERTY(QString recipient READ recipient WRITE setRecipient NOTIFY recipientChanged) public: diff --git a/examples/quickcontrols/chattutorial/chapter5/+Material/ChatToolBar.qml b/examples/quickcontrols/chattutorial/chapter5/+Material/ChatToolBar.qml index 3fa10c7f13..1b46cfec4e 100644 --- a/examples/quickcontrols/chattutorial/chapter5/+Material/ChatToolBar.qml +++ b/examples/quickcontrols/chattutorial/chapter5/+Material/ChatToolBar.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick.Controls diff --git a/examples/quickcontrols/chattutorial/chapter5/CMakeLists.txt b/examples/quickcontrols/chattutorial/chapter5/CMakeLists.txt index 72afb7888b..fdeab2eeb4 100644 --- a/examples/quickcontrols/chattutorial/chapter5/CMakeLists.txt +++ b/examples/quickcontrols/chattutorial/chapter5/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) @@ -30,13 +30,13 @@ target_link_libraries(chattutorial-chapter5 PRIVATE qt_policy(SET QTP0001 NEW) qt_add_qml_module(chattutorial-chapter5 - URI chapter5 + URI chattutorial QML_FILES "+Material/ChatToolBar.qml" "ChatToolBar.qml" "ContactPage.qml" "ConversationPage.qml" - "main.qml" + "Main.qml" RESOURCES "images/Albert_Einstein.png" "images/[email protected]" diff --git a/examples/quickcontrols/chattutorial/chapter5/ChatToolBar.qml b/examples/quickcontrols/chattutorial/chapter5/ChatToolBar.qml index 73f2c655a9..af3a7a3f8c 100644 --- a/examples/quickcontrols/chattutorial/chapter5/ChatToolBar.qml +++ b/examples/quickcontrols/chattutorial/chapter5/ChatToolBar.qml @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick.Controls diff --git a/examples/quickcontrols/chattutorial/chapter5/ContactPage.qml b/examples/quickcontrols/chattutorial/chapter5/ContactPage.qml index 920c824090..d7254a52b3 100644 --- a/examples/quickcontrols/chattutorial/chapter5/ContactPage.qml +++ b/examples/quickcontrols/chattutorial/chapter5/ContactPage.qml @@ -1,10 +1,12 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Controls -import io.qt.examples.chattutorial +import chattutorial Page { id: root @@ -27,15 +29,21 @@ Page { spacing: 20 model: SqlContactModel {} delegate: ItemDelegate { + id: contactDelegate text: model.display width: listView.width - listView.leftMargin - listView.rightMargin height: avatar.implicitHeight leftPadding: avatar.implicitWidth + 32 + + // Use "model" rather than the specific "display" role, because it + // would conflict with the display property of ItemDelegate. + required property var model + onClicked: root.StackView.view.push("ConversationPage.qml", { inConversationWith: model.display }) Image { id: avatar - source: "images/" + model.display.replace(" ", "_") + ".png" + source: "images/" + contactDelegate.model.display.replace(" ", "_") + ".png" } } } diff --git a/examples/quickcontrols/chattutorial/chapter5/ConversationPage.qml b/examples/quickcontrols/chattutorial/chapter5/ConversationPage.qml index 6e209b7a85..75129b43d8 100644 --- a/examples/quickcontrols/chattutorial/chapter5/ConversationPage.qml +++ b/examples/quickcontrols/chattutorial/chapter5/ConversationPage.qml @@ -1,11 +1,13 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +pragma ComponentBehavior: Bound + import QtQuick import QtQuick.Layouts import QtQuick.Controls -import io.qt.examples.chattutorial +import chattutorial Page { id: root @@ -23,7 +25,7 @@ Page { Label { id: pageTitle - text: inConversationWith + text: root.inConversationWith font.pixelSize: 20 anchors.centerIn: parent } @@ -42,33 +44,39 @@ Page { verticalLayoutDirection: ListView.BottomToTop spacing: 12 model: SqlConversationModel { - recipient: inConversationWith + recipient: root.inConversationWith } delegate: Column { + id: conversationDelegate anchors.right: sentByMe ? listView.contentItem.right : undefined spacing: 6 - readonly property bool sentByMe: model.recipient !== "Me" + required property string author + required property string recipient + required property date timestamp + required property string message + readonly property bool sentByMe: recipient !== "Me" Row { id: messageRow spacing: 6 - anchors.right: sentByMe ? parent.right : undefined + anchors.right: conversationDelegate.sentByMe ? parent.right : undefined Image { id: avatar - source: !sentByMe ? "images/" + model.author.replace(" ", "_") + ".png" : "" + source: !conversationDelegate.sentByMe + ? "images/" + conversationDelegate.author.replace(" ", "_") + ".png" : "" } Rectangle { width: Math.min(messageText.implicitWidth + 24, listView.width - avatar.width - messageRow.spacing) height: messageText.implicitHeight + 24 - color: sentByMe ? "lightgrey" : "steelblue" + color: conversationDelegate.sentByMe ? "lightgrey" : "steelblue" Label { id: messageText - text: model.message - color: sentByMe ? "black" : "white" + text: conversationDelegate.message + color: conversationDelegate.sentByMe ? "black" : "white" anchors.fill: parent anchors.margins: 12 wrapMode: Label.Wrap @@ -78,9 +86,9 @@ Page { Label { id: timestampText - text: Qt.formatDateTime(model.timestamp, "d MMM hh:mm") + text: Qt.formatDateTime(conversationDelegate.timestamp, "d MMM hh:mm") color: "lightgrey" - anchors.right: sentByMe ? parent.right : undefined + anchors.right: conversationDelegate.sentByMe ? parent.right : undefined } } @@ -106,8 +114,8 @@ Page { text: qsTr("Send") enabled: messageField.length > 0 onClicked: { - listView.model.sendMessage(inConversationWith, messageField.text); - messageField.text = ""; + listView.model.sendMessage(root.inConversationWith, messageField.text) + messageField.text = "" } } } diff --git a/examples/quickcontrols/chattutorial/chapter4/main.qml b/examples/quickcontrols/chattutorial/chapter5/Main.qml index da080e3a45..bb968e9f7a 100644 --- a/examples/quickcontrols/chattutorial/chapter4/main.qml +++ b/examples/quickcontrols/chattutorial/chapter5/Main.qml @@ -1,7 +1,6 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -import QtQuick import QtQuick.Controls ApplicationWindow { diff --git a/examples/quickcontrols/chattutorial/chapter5/chapter5.pro b/examples/quickcontrols/chattutorial/chapter5/chapter5.pro index 9f44cb0459..aba1e33cdb 100644 --- a/examples/quickcontrols/chattutorial/chapter5/chapter5.pro +++ b/examples/quickcontrols/chattutorial/chapter5/chapter5.pro @@ -1,7 +1,11 @@ TEMPLATE = app QT += qml quick sql -CONFIG += c++11 +CONFIG += c++11 qmltypes + +QML_IMPORT_PATH = $$pwd/. +QML_IMPORT_NAME = chattutorial +QML_IMPORT_MAJOR_VERSION = 1 HEADERS += sqlcontactmodel.h \ sqlconversationmodel.h @@ -27,8 +31,9 @@ resources.files = \ images/[email protected] \ images/[email protected] \ images/[email protected] \ - main.qml -resources.prefix = qt/qml/chapter5/ + Main.qml \ + qmldir +resources.prefix = qt/qml/chattutorial/ RESOURCES += resources \ qtquickcontrols2.conf diff --git a/examples/quickcontrols/chattutorial/chapter5/main.cpp b/examples/quickcontrols/chattutorial/chapter5/main.cpp index a084655830..bcb6c1e923 100644 --- a/examples/quickcontrols/chattutorial/chapter5/main.cpp +++ b/examples/quickcontrols/chattutorial/chapter5/main.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include <QtCore> @@ -7,9 +7,6 @@ #include <QSqlError> #include <QtQml> -#include "sqlcontactmodel.h" -#include "sqlconversationmodel.h" - static void connectToDatabase() { QSqlDatabase database = QSqlDatabase::database(); @@ -37,13 +34,10 @@ int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); - qmlRegisterType<SqlContactModel>("io.qt.examples.chattutorial", 1, 0, "SqlContactModel"); - qmlRegisterType<SqlConversationModel>("io.qt.examples.chattutorial", 1, 0, "SqlConversationModel"); - connectToDatabase(); QQmlApplicationEngine engine; - engine.load(QUrl(QStringLiteral("qrc:/qt/qml/chapter5/main.qml"))); + engine.loadFromModule("chattutorial", "Main"); if (engine.rootObjects().isEmpty()) return -1; diff --git a/examples/quickcontrols/chattutorial/chapter5/qmldir b/examples/quickcontrols/chattutorial/chapter5/qmldir new file mode 100644 index 0000000000..4ae7752b55 --- /dev/null +++ b/examples/quickcontrols/chattutorial/chapter5/qmldir @@ -0,0 +1,6 @@ +module chattutorial +ChatToolBar 1.0 ChatToolBar.qml +ContactPage 1.0 ContactPage.qml +ConversationPage 1.0 ConversationPage.qml +Main 1.0 Main.qml +typeinfo chapter5.qmltypes diff --git a/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.cpp b/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.cpp index cce8cffbb8..189924deec 100644 --- a/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.cpp +++ b/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "sqlcontactmodel.h" diff --git a/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.h b/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.h index a6d24a4774..c7f9a154eb 100644 --- a/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.h +++ b/examples/quickcontrols/chattutorial/chapter5/sqlcontactmodel.h @@ -1,13 +1,17 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef SQLCONTACTMODEL_H #define SQLCONTACTMODEL_H +#include <QQmlEngine> #include <QSqlQueryModel> class SqlContactModel : public QSqlQueryModel { + Q_OBJECT + QML_ELEMENT + public: SqlContactModel(QObject *parent = nullptr); }; diff --git a/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.cpp b/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.cpp index 1a1594b094..5be01de52c 100644 --- a/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.cpp +++ b/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "sqlconversationmodel.h" diff --git a/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.h b/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.h index 8a3cdd4c43..b4917c0eff 100644 --- a/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.h +++ b/examples/quickcontrols/chattutorial/chapter5/sqlconversationmodel.h @@ -1,14 +1,16 @@ -// Copyright (C) 2017 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef SQLCONVERSATIONMODEL_H #define SQLCONVERSATIONMODEL_H +#include <QQmlEngine> #include <QSqlTableModel> class SqlConversationModel : public QSqlTableModel { Q_OBJECT + QML_ELEMENT Q_PROPERTY(QString recipient READ recipient WRITE setRecipient NOTIFY recipientChanged) public: diff --git a/examples/quickcontrols/chattutorial/doc/src/qtquickcontrols-chattutorial.qdoc b/examples/quickcontrols/chattutorial/doc/src/qtquickcontrols-chattutorial.qdoc index 94d05717f2..a42ef81575 100644 --- a/examples/quickcontrols/chattutorial/doc/src/qtquickcontrols-chattutorial.qdoc +++ b/examples/quickcontrols/chattutorial/doc/src/qtquickcontrols-chattutorial.qdoc @@ -21,16 +21,12 @@ When setting up a new project, it's easiest to use basic "Hello World" application with the following files: \list -\li \c MainForm.ui.qml - Defines the default UI -\li \c main.qml - Embeds the default UI in a Window -\li \c qml.qrc - Lists the \c .qml files that are built into the binary +\li \c CMakeLists.txt - Instructs CMake how our project should be built +\li \c Main.qml - Provides a default UI containing an empty Window \li \c main.cpp - Loads \c main.qml -\li \c chattutorial.pro - Provides the qmake configuration +\li \c qtquickcontrols2.conf - Tells the application which style it should use \endlist -\note Delete the \c MainForm.ui.qml and \c qml.qrc files from the project, as -we will not use them in this tutorial. - \section2 main.cpp The default code in \c main.cpp has two includes: @@ -46,42 +42,35 @@ does. QCoreApplication is sufficient for non-graphical applications. QGuiApplication is sufficient for graphical applications that do not use \l {Qt Widgets}, while QApplication is required for those that do. -The second include makes QQmlApplicationEngine available, along with -some useful functions required for making C++ types accessible from QML. +The second include makes QQmlApplicationEngine available, allowing us to +load our QML. Within \c main(), we set up the application object and QML engine: \skipto main \printuntil } -It begins with enabling \l {High DPI}{high DPI scaling}, which is not -part of the default code. It is necessary to do so before the application -object is constructed. - -After that's done, we construct the application object, passing any application -arguments provided by the user. - -Next, the QML engine is created. \l QQmlApplicationEngine is a convenient -wrapper over QQmlEngine, providing the \l {QQmlApplicationEngine::load}{load()} -function to easily load QML for an application. It also adds some convenience -for using \l {Using File Selectors with Qt Quick Controls}{file selectors}. +\l QQmlApplicationEngine is a convenient wrapper over QQmlEngine, providing the +\l {QQmlApplicationEngine::}{loadFromModule} function to easily load QML for an +application. It also adds some convenience for using \l {Using File Selectors +with Qt Quick Controls}{file selectors}. Once we've set up things in C++, we can move on to the user interface in QML. -\section2 main.qml +\section2 Main.qml Let's modify the default QML code to suit our needs. -\quotefromfile chattutorial/chapter1/main.qml +\quotefromfile chattutorial/chapter1/Main.qml \skipto import \printuntil import QtQuick.Controls -First, import the \l {Qt Quick} module. This gives us -access to graphical primitives such as \l Item, \l Rectangle, \l Text, and so -on. -For the full list of types, see the \l {Qt Quick QML Types} documentation. +You'll notice that the \l {Qt Quick} module has already been imported. This +gives us access to graphical primitives such as \l Item, \l Rectangle, \l Text, +and so on. For the full list of types, see the \l {Qt Quick QML Types} +documentation. -Next, import the Qt Quick Controls module. Amongst other things, this +Add an import of the Qt Quick Controls module. Amongst other things, this provides access to \l ApplicationWindow, which will replace the existing root type, \c Window: @@ -90,6 +79,7 @@ root type, \c Window: \dots \skipto } \skipuntil } +\skipuntil } \printuntil } ApplicationWindow is a \l Window with some added convenience for creating a @@ -121,14 +111,13 @@ which screen the user is viewing, it is much easier to use \l Page. For now, we'll just add one page, but in the next chapter, we'll demonstrate how to navigate between several pages. -\quotefromfile chattutorial/chapter1/main.qml +\quotefromfile chattutorial/chapter1/Main.qml \skipto Page \printuntil } \printuntil } -We replace the default \c{MainForm {...}} code block with a Page, which is -sized to occupy all the space on the window using the \l {Item::}{anchors.fill} -property. +First, we add a Page, which is sized to occupy all the space on the window +using the \l {Item::}{anchors.fill} property. Then, we assign a \l Label to its \l {Page::}{header} property. Label extends the primitive \l Text item from the Qt Quick module by adding @@ -155,43 +144,12 @@ bounds, both horizontally and vertically. \section2 The Project File -The \c .pro or \l {Creating Project Files}{project} file contains all of the -information needed by \l {qmake Manual}{qmake} to generate a Makefile, which is -then used to compile and link the application. - -\quotefromfile chattutorial/chapter1/chapter1.pro -\printline TEMPLATE - -The first line tells \c qmake which kind of project this is. We're building an -application, so we use the \c app template. - -\printline QT - -The next line declares the Qt libraries that we want to use from C++. - -\printline CONFIG - -This line states that a C++11 compatible compiler is required to build the -project. - -\printline SOURCES - -The \c SOURCES variable lists all of the source files that should be compiled. -A similar variable, \c HEADERS, is available for header files. - -\printuntil qtquickcontrols2 - -The next line tells \c qmake that we have a collection of -\l {The Qt Resource System}{resources} that should be built into the -executable. - -\skipto target -\printline +The \c CMakeLists.txt file contains all of the information needed by \l {Build +with CMake}{CMake} to build our project into an executable that we can run. -This line replaces deployment settings that come with the default project file. -It determines where the example is copied, on running "\c{make install}". +For an in-depth explanation of this file, see \l {Building a QML application}. -Now we can build and run the application: +Here is what our application currently looks like when run: \borderedimage qtquickcontrols-chattutorial-chapter1.png @@ -208,7 +166,7 @@ can display text, be checked on and off, and react to mouse clicks. Here is our ListView: -\quotefromfile chattutorial/chapter2/main.qml +\quotefromfile chattutorial/chapter2/Main.qml \dots 8 \codeline \skipto ListView @@ -275,7 +233,7 @@ vertical space. In this chapter, you'll learn how to use \l StackView to navigate between pages in an application. Here's the revised \c main.qml: -\quotefromfile chattutorial/chapter3/main.qml +\quotefromfile chattutorial/chapter3/Main.qml \skipto import \printuntil } \printuntil } @@ -637,8 +595,8 @@ changes to be done to \c ContactPage.qml. To be able to use the types, we must first import them using the URI we set in \c main.cpp: \quotefromfile chattutorial/chapter4/ContactPage.qml -\skipto import io.qt.examples.chattutorial -\printline import io.qt.examples.chattutorial +\skipto import chattutorial +\printline import chattutorial We then replace the dummy model with the proper one: |