diff --git a/qml.html.markdown b/qml.html.markdown new file mode 100644 index 00000000..78e94d66 --- /dev/null +++ b/qml.html.markdown @@ -0,0 +1,368 @@ +--- +language: QML +contributors: + - ["Furkan Uzumcu", "https://zmc.space/"] +filename: learnqml.qml +lang: en-us +--- + +```qml +// This is a completely valid QML file that you can run using `qmlscene` if you copy the contents +// into a *.qml file. +// Comments start with double forward slashes. +/* Or you + can have + multi line + comments + */ + +// Import statement syntax is +// import ${MODULE_NAME} [${VERSION_NUMBER}] [as ${QUALIFIER}] +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 as QQC +import QtQuick.Layouts 1.15 +import Qt.labs.platform 1.1 + +// Each QML document can contain only one top level type +Window { + // Each object has a special and optional `id` attribute that can be used to refer to the + // declared objects. An `id` has to be unique in the same document. + id: root + width: 400 + height: 600 + title: "Learn QML in Y Minutes" + + Item { + // Every object that can be declared inherits from QObject and contains at + // least one property, which is `objectName`. All the other properties are + // added by extending `QObject` type. This is an `Item` type and it contains + // the additional `width` and `height` properties and more. + objectName: "My Item" + // `id`s in the same document can be used anywhere in the same file. + // You cannot access an `id` from a different file. + width: root.width + } + + // Signals are used to communicate that a certain event happened. + // Some types have built-in signals + Timer { + id: timer + interval: 500 + onTriggered: { + console.log("Timer triggered!") + } + } + + QtObject { + id: objSignals + // You can also declare your own signals. + signal clicked() + // Signals can also have arguments. + signal mousePositionChanged(int x, int y) + // The way to react to a signal emission is by adding signal handlers to + // the immediate object that the signal belongs to. + onClicked: () => { + // Do stuff here. + console.log("objSignals.clicked() signal is emitted.") + } + // Signal handlers must explicitly declare the arguments. + onMousePositionChanged: (x, y) => { + // Do stuff here. + console.log("objSignals.mousePositionChanged() signal is emitted. x=", x, "y=", y) + } + } + + // If you want to declare signal handlers for other objects, you can use + // `Connections`. + Connections { + target: objSignals + + // You can then declare functions with the same name as the signal + // handler. + function onClicked() { + console.log("objSignals.clicked() signal is handled from Connections.") + } + } + + Item { + visible: false + + // An object can support having child objects. You can add child objects + // by declaring types as follows: + Rectangle { + width: 16 + height: 16 + color: "red" + } + } + + Item { + id: objProperties + // You can also declare your own properties. + // Syntax for declaring is + // [default] [required] [readonly] property ${TYPE} ${NAME} + property color nextColor + // Read only properties have to be initialized when declared. + readonly property color defaultColor: "red" + // Required properties have to be initialized where the reusable type is + // used. + required property color initialColor + + // NOTE: Although the initial assignment can be done in the same file, + // it is not often the use case. + initialColor: "green" + + // Properties are type safe and a property can only be assigned a value + // that matches the property type. + // property int volume: "four" // ERROR! + + Item { + // You can create alias properties that hold a reference to another + // property. + + property alias parentNextColor: objProperties.nextColor + + // Assignments to alias properties alter the property that it holds + // a reference to. + parentNextColor: "blue" // Changes objProperties.nextColor + // Since `parentNextColor` is an alias to `nextColor`, any changes + // to `nextColor` will also be reflected to `parentNextColor`. + } + } + + Item { + // Property assignment values can either be static or binding + // expressions. + // Static value + property int radius: 32 + // Binding expressions describe a property's relationship to other + // properties. When the value of `radius` changes, the expression here + // will be re-evaluated. + property int diameter: radius * 2 + + onDiameterChanged: { + console.log("onDiameterChanged:", diameter) + } + } + + ListView { + // Attached properties and signal handlers provide a way to extend an + // existing object and provide more information that is otherwise not + // immediately available. + width: 100 + height: 30 + model: 3 + delegate: Rectangle { + // ListView provides an attached property for its children that can + // be used to access more information. + color: ListView.isCurrentItem ? "green" : "red" + } + // Attached types can also have signal handlers. + // `Component` is attached to every type that's available in QML. + Component.onCompleted: { + console.log("This signal handler is called after object is created.") + } + } + + Rectangle { + // Since this rectangle is not created by the ListView, the attached + // type is not avaiable. + color: ListView.isCurrentItem ? "green" : "red" + } + + QtObject { + id: calculator + + // Objects can also declare methods. Function declarations can annotate + // the arguments, or have no arguments at all. + function add(a: int, b: int): int { + // Semicolon at the end of a line is optional. + return a + b + } + + function multiply(a: real, b: real): real { + return a * b; + } + } + + MouseArea { + anchors.fill: parent + onClicked: (mouse) => { + console.log("2 + 2 =", calculator.add(2, 2)) + } + } + + Item { + width: 100 + // Methods can also be used as binding expressions. When `width` + // changes, the binding expression will evaluate and call `multiply`. + height: calculator.multiply(width, 0.5) + opacity: calculateOpacity() + + function calculateOpacity() { + // If the function declaration contains references to other + // properties, changes to those properties also trigger a binding + // evaluation. + return height < 50 ? 0.5 : 1 + } + } + + // Each QML file that starts with an upper case name declares a re-usable + // component, e.g "RedRectangle.qml". + // In addition, reusable components can be declared in-line. + component RedRectangle: Rectangle { + color: "red" + } + + // This inline component can then be used in the same file, or in other + // files by prefixing the type name with the file name that it belongs to. + // + // ${FILE_NAME}.RedRectangle { } + // or + RedRectangle { + } + + // QML also supports enumeration declarations. + component MyText: Text { + enum TextType { + Normal, + Heading + } + + // Enum types are assigned to integer properties. + property int textType: MyText.TextType.Normal + + font.bold: textType == MyText.TextType.Heading + font.pixelSize: textType == MyText.TextType.Heading ? 24 : 12 + } + + // ----- Interactive Area + + QQC.ScrollView { + anchors.fill: parent + contentWidth: container.implicitWidth + contentHeight: container.implicitHeight + + Column { + id: container + spacing: 6 + + Row { + spacing: 2 + + QQC.Label { + width: 200 + anchors.verticalCenter: parent.verticalCenter + text: "Click to start the timer.\nCheck the logs!" + wrapMode: QQC.Label.WordWrap + } + + QQC.Button { + text: timer.running ? "Timer Running" : "Start Timer" + onClicked: { + timer.start() + } + } + } + + Row { + spacing: 2 + + QQC.Label { + width: 200 + anchors.verticalCenter: parent.verticalCenter + text: "Click to emit objSignals.clicked() signal" + wrapMode: QQC.Label.WordWrap + } + + QQC.Button { + property int emissionCount: 0 + + text: "Emitted " + emissionCount + " times." + onClicked: { + objSignals.clicked() + emissionCount++ + } + } + } + + Row { + spacing: 2 + + QQC.Label { + width: 200 + anchors.verticalCenter: parent.verticalCenter + text: "Click to emit objSignals.mousePositionChanged() signal" + wrapMode: QQC.Label.WordWrap + } + + QQC.Button { + property int emissionCount: 0 + + text: "Emitted " + emissionCount + " times." + onClicked: { + objSignals.mousePositionChanged(32, 32) + emissionCount++ + } + } + } + + Rectangle { + width: 200 + height: 80 + color: objProperties.nextColor + + QQC.Label { + width: 200 + anchors.verticalCenter: parent.verticalCenter + text: "Click to change nextColor property." + wrapMode: QQC.Label.WordWrap + } + + TapHandler { + onTapped: { + colorDialog.open() + } + } + + ColorDialog { + id: colorDialog + currentColor: objProperties.initialColor + onColorChanged: { + objProperties.nextColor = color + + } + } + } + + Row { + spacing: 2 + + Rectangle { + width: 200 + height: 80 + color: "red" + radius: radiusSlider.value + + QQC.Label { + width: parent.width + anchors.centerIn: parent + text: "Use slider to change radius" + wrapMode: QQC.Label.WordWrap + horizontalAlignment: Qt.AlignHCenter + } + } + + QQC.Slider { + id: radiusSlider + width: 100 + anchors.verticalCenter: parent.verticalCenter + from: 0 + to: 80 + } + } + } + } +} +```