Довольно часто на форуме возникает один и тот же вопрос, касающийся проблемы отображения контента, встроенного в какие-либо QML-объекты.
Наиболее частым проявлением этой проблемы является прокрутка содержимого ListView за пределы его области. В любом случае, это один из тех частых случаев, когда с этой проблемой сталкиваются новички, приступая к изучению QML. Ну или еще вариант, когда столкнулся с такой проблемой при использовании объектов Rectangle.
На следующем изображении ниже вы можете увидеть два окна приложения, которые иллюстрируют эту проблему.
- В левом окне показано, как изначально выглядит макет
- Правое окно показывает, что если вы начнете прокручивать содержимое ListView, оно уйдет под текст заголовка, хотя должно было быть обрезано.
Пример исходного кода
Вот так выглядит исходный код программы.
import QtQuick 2.12 import QtQuick.Window 2.12 Window { visible: true width: 640 height: 480 title: qsTr("Hello World") Text { id: topText text: qsTr("Title") anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top } ListView { width: 180; height: 200 model: contactModel delegate: Text { text: name + ": " + number } anchors.horizontalCenter: parent.horizontalCenter anchors.top: topText.bottom } ListModel { id: contactModel ListElement { name: "Bill Smith" number: "555 3264" } ListElement { name: "John Brown" number: "555 8426" } ListElement { name: "Sam Wise" number: "555 0473" } } }
Чтобы решить эту проблему, просто установите свойство clip: true в ListView .
ListView { clip: true /* код */ }
И теперь содержимое ListView не будет выходить за пределы ListView
Использование прямоугольных объектов
Точно так же clip работает для объектов Rectangle. Вот код программы, а также пример до и после включения клипа.
import QtQuick 2.12 import QtQuick.Window 2.12 Window { visible: true width: 640 height: 480 title: qsTr("Hello World") Rectangle { x: 27 y: 31 width: 100 height: 100 color: "blue" clip: true Rectangle { x: -20 y: -20 width: 100 height: 100 color: "green" } } }
Округление
Но что не работает при использовании клипа, так это скругление, даже если вы настраиваете радиус скругления у родительского объекта Rectangle.
Например вот у нас такой код не даст ожидаемого результата
import QtQuick 2.12 import QtQuick.Window 2.12 Window { visible: true width: 640 height: 480 title: qsTr("Hello World") Rectangle { x: 27 y: 31 width: 100 height: 100 color: "blue" radius: 30 // set rounding clip: true // enable cropping Rectangle { x: -20 y: -20 width: 100 height: 100 color: "green" } } }
The result
Такое поведение клипа является стандартным и было сделано из соображений производительности, поскольку почти все графические элементы QML наследуются от типа Item. Это означает, что дополнительная обработка масок во всем приложении избыточна.
Но ожидаемого результата можно добиться следующим программным кодом с использованием эффекта прививки OpacityMask
import QtQuick 2.12 import QtQuick.Window 2.12 import QtGraphicalEffects 1.13 Window { visible: true width: 640 height: 480 title: qsTr("Hello World") Rectangle { id: rect x: 27 y: 31 width: 100 height: 100 color: "blue" layer.enabled: true layer.effect: OpacityMask { maskSource: Item { width: rect.width height: rect.height Rectangle { anchors.centerIn: parent width: rect.adapt ? rect.width : Math.min(rect.width, rect.height) height: rect.adapt ? rect.height : width radius: 30 } } } Rectangle { x: -20 y: -20 width: 100 height: 100 color: "green" } } }
И результат будет следующим
Вывод
В заключение скажу, что clip — очень полезное свойство, которое из соображений производительности по умолчанию установлено в false. При этом это свойство выполняет только простую обрезку в виде прямоугольника, а что-то более сложное нужно обрезать по маске.
Зачем внутри OpacityMask Item, почему сразу Rectangle не вставить?
И что за rect.adapt?
Мое предложение: