У той час, як у Qt 6.0 очікуються великі зміни, QML вже отримав деякі нові мовні можливості у 5.15. Читайте далі, щоб дізнатися про обов'язкові властивості, вбудовані компоненти та nullish coalescing.
Обов'язкові властивості (Required Properties)
Іноді у вас є компоненти, які вимагають встановлення будь-якої властивості, і насправді не мають хороших значень за промовчанням. Наприклад, ви можете подбати про доступність ваших кнопок, тому ви створюєте AccessibleButton, у якого завжди має бути опис.
// AccessibleButton.qml Button { property string description Accessible.description: description }
Однак той факт, що кнопка має властивість опису, не означає, що її встановлюватиме хтось інший. Таким чином, ви або ваш колега могли б у якийсь момент створити екземпляр компонента за допомогою:
AccessibleButton { onClicked: (mouse) => { /* fancy business logic here */ } }
Опис тепер просто порожній рядок! Звичайно, ви могли б дати їй значення за промовчанням, але яке саме? "Button" ("Кнопка")? Навряд це корисно. Чи не повинні ви повідомити про це? Принаймні тестування може це виявити. Але чи не буде кориснішим, якщо механізм QML знатиме, що цю властивість потрібно встановити?
До Qt 5.15, на жаль, не було можливості забезпечити, щоб опис було встановлено. Але починаючи з Qt 5.15 це стає можливим:
Button { required property string description Accessible.description: description }
Тепер, якщо AccessibleButton створено, а опис не задано, вся програма не запуститься. Це, однак, не може бути зроблено, якщо компонент створюється динамічно, наприклад через Loader. У цьому випадку буде лише попередження під час виконання.
Також планується додати додаткову підтримку інструментів у qmllint та QtCreator, щоб показувати попередження, якщо необхідні властивості не встановлені.
Обов'язкові властивості та делегати (Delegates)
Крім того, обов'язкові властивості відіграють особливу роль у делегатах. Як ви, можливо, знаєте, делегати можуть звертатися до ролей моделі даної моделі безпосередньо на ім'я, а також до деяких інших властивостей, таких як модель та індекс.
ListView { model: root.myModel delegate: Text { id: delegate color: index % 2 ? "gray" : "black" text: description } }
І якщо ваші делегати не містять обов'язкових властивостей, тут нічого не змінюється. Однак, якщо вони містять хоч одну обов'язкову властивість, ці імена більше недоступні. Натомість ви повинні точно вибрати, вказавши їх, як обов'язкові властивості.
ListView { model: root.myModel delegate: Text { id: delegate required property int index required property string description color: index % 2 ? "gray" : "black" text: description } }
Потім механізм QML встановить обов'язкові властивості. Зверніть увагу, що між новим і старим підходом є одна істотна відмінність у тому випадку, якщо ваша модель була редагованою: при старому підході ви могли б написати:
Text { id: delegate Component.onCompleted: description = "My fancy new text" }
і модель, відповідно, було б оновлено. Але якщо ви робите,
Text { id: delegate required property string description Component.onCompleted: delegate.description = "My fancy new text" }
тоді прив'язка до опису буде порушена (і механізм QML виведе попередження) і модель не буде оновлена. Така поведінка була обрана, щоб гарантувати, що компоненти не будуть поводитися надто по-різному при використанні в делегатах і при їх використанні поза ними. Крім того, розробники не хотіли нікого заохочувати робити обов'язкові надання властивостям (оскільки це порушує прив'язки в цілому).
Якщо ви дійсно хочете оновити значення моделі, звичайно, є спосіб досягти цього: зробити модель обов'язковою властивістю і написати:
Component.onCompleted: model.description= "My fancy new text"
Розробники рекомендують завжди використовувати обов'язкові властивості делегатів. Це дозволяє уникнути некваліфікованих пошуків, які є проблемними для інструментів і, як правило, повільнішими.
Вбудовані компоненти (Inline Components)
Ще одна нова функція в 5.15 – вбудовані компоненти. Як випливає з назви, вони дозволяють визначити новий компонент всередині файлу. Основний синтаксис це:
component <component name> : BaseType { // declare properties and bindings here }
Всередині файлу ви можете посилатися на новий компонент на його ім'я, якби він був визначений у його власному файлі. Давайте розглянемо компонент LabeledImage як приклад, щоб показати, як це працює:
// Images.qml import QtQuick 2.15 Item { component LabeledImage: Column { property alias source: image.source property alias caption: text.text Image { id: image width: 50 height: 50 } Text { id: text font.bold: true } } Row { LabeledImage { id: before source: "before.png" caption: "Before" } LabeledImage { id: after source: "after.png" caption: "After" } } property LabeledImage selectedImage: before }
Також можна посилатися на компонент в інших файлах. У цьому випадку вам потрібно додати префікс його імені до імені компонента, що містить:
// LabeledImageBox.qml import QtQuick 2.15 Rectangle { property alias caption: image.caption property alias source: image.source border.width: 2 border.color: "black" Images.LabeledImage { id: image } }
Ви можете поцікавитись, навіщо потрібні вбудовані компоненти, коли QML вже має тип Component. Дивлячись на попередні приклади, можна бачити, що вбудовані компоненти дозволяють робити такі речі, які Component не робить:
• Ви можете створити екземпляр компонента без додаткових витрат на використання Loader.
• Ви можете використовувати тип компонента в об'явах властивостей.
• Ви можете посилатися на компонент інших файлів, відмінних від того, в якому він визначений.
Нульове об'єднання
Остання нова мовна функція була реалізована стажистом компанії Qt Максиміліаном Гольдштейном. Хоча QML зазвичай підтримує лише EcmaScript 6, Макс додав підтримку для нової мовної функції, яка в даний час знаходиться в процесі додавання до останнього стандарту EcmaScript: nullish coalescing. Цитуючи MDN:
nullish coalescing operator - це логічний оператор, який повертає свій правий операнд, коли його лівий операнд дорівнює нулю або не визначений, або, інакше, повертає свій лівий операнд.
Ось приклад того, як його можна використовувати в QML для встановлення властивостей із JSON та надання розумних значень за умовчанням, якщо вони не були надані.
Item { property var settings property int brightness: settings.brightness ?? 100 property color color: settings.color ?? "blue" Component.onCompleted: settings = JSON.parse(settingsString) }
Зверніть увагу, що не можна було використати || замість ?? для brightness (яскравості), так як settings.brightness міг би бути 0, у цьому випадку набули б значення за замовчуванням.
Чекайте!
З Qt 6 на горизонті багато інших змін обов'язково з'являться в QML. Крім модернізації внутрішніх компонентів механізму QML, розробники хочуть використовувати статичну типізацію як для генерування більш швидкого коду (включаючи компіляцію в C++), так і для поліпшення інструментів. Більш того, хоча розробники концентруються на цих великих темах для початкового випуску 6.0, вони зберігають відкритість щодо невеликих покращень якості життя: необов'язкове створення ланцюжка або додавання підтримки для API вибірки - це лише два приклади запитів функцій від спільноти, розглядається більша часова шкала 6 .x (але не початкова версія 6.0).