Любой элемент в QML может быть источником событий перетаскивания (Drag and Drop). У каждого элемента типа Item (который является базовым типом для всех стандартных элементов интерфейса) имеется прикрепленное свойство Drag, которое представляет одноименный тип Drag и с помощью которого можно установить настройки перетаскивания. Для этого данный тип предоставляет ряд свойств, из которых отмечу следующие:
active: определяет, активна ли в данный момент последовательность событий перетаскивания.
Установка для свойства значения true
сгенерирует событие QDragEnter
, то есть индикатор начало перемещения элемента.
Установка значения false
приведет к отправке события QDragLeave
. Пока это свойство равно true
,
любое изменение положения элемента будет генерировать событие QDragMove
- индикатор перемещения элемента с новой позицией элемента.
hotSpot: хранит позицию перетаскивания относительно верхнего левого угла элемента. По умолчанию это (0, 0). Изменения в hotSpot вызывают новое перетаскивание с обновленной позицией.
imageSource: хранит адрес изображения (объект QUrl), которое отображается во время перетаскивания
mimeData: тип перетаскиваемых данных
source: содержит объект-источник событий перетаскивания. По умолчанию это элемент, к которому прикреплено свойство Drag и который перетаскивается.
target: объект, на который выполняется перетаскивание.
Рассмотрим простейший пример:
import QtQuick Window { width: 250 height: 150 visible: true title: "METANIT.COM" Rectangle { x: 10; y: 10 // начальная позиция width: 50; height: 50 color: "red" Drag.active: dragArea.drag.active Drag.hotSpot.x: 10 Drag.hotSpot.y: 10 MouseArea { id: dragArea anchors.fill: parent drag.target: parent } } }
Для теста перетаскивания определен элемент Rectangle, а чтобы можно было взаимодействовать мышью, внутри Rectangle определена область MouseArea,
которая занимает все пространство прямоугольника. Привязка свойства active к свойству MouseArea::drag.active
приведет к вызову startDrag, когда
пользователь начнет перетаскивание. У объекта MouseArea, в свою очередь, устанавливается, свойство drag.target
, которому передается идентифкатор перетаскиваемого элемента - в данном случае - идентифкатор
родительского объекта Rectangle.
И при захвате мышью прямоугольника мы сможем переместить его в любое место окна.
Для создания области перетаскивания применяется тип DropArea, который представляет невидимый элемент и который может получать события
перетаскивания элемента. Когда перетаскивание элемента активно (свойство drag.active
равно true
), любое изменение позиции элемента будет генерировать
событие перетаскивания, которое будет получено объектом DropArea.
Например, немного усложним предыдущий пример. Допустим, нам надо, чтобы прямоугольник можно было перетащить не в любое место окна, а только в какую-то определенную область. В этом случае нам надо отселживать перемещение перетаскиваемого элемента:
import QtQuick Window { width: 250 height: 150 visible: true title: "METANIT.COM" Rectangle { id: dragItem x: 10; y: 10 property point beginDrag // для фиксации начальной позиции width: 50; height: 50 color: "red" Drag.active: dragArea.drag.active MouseArea { id: dragArea anchors.fill: parent drag.target: parent // по нажатию устанавливаем начальную позицию прямоугольника onPressed: dragItem.beginDrag = Qt.point(dragItem.x, dragItem.y) onReleased: { // возвращаемся на начальную позицию dragItem.x = dragItem.beginDrag.x dragItem.y = dragItem.beginDrag.y } } } }
Здесь в элементе Rectangle добавлено новое свойство для хранения начальной позиции элемента:
property point beginDrag
Это свойство представляет тип point - точку, у которой есть свойства x и y для хранения координат точки. И по нажатию на область MouseArea в обработчике onPressed устанавливаем
эту точку с помощью функции Qt.point
:
onPressed: dragItem.beginDrag = Qt.point(dragItem.x, dragItem.y)
то есть beginDrag представляет начальные координаты прямоугольника (x=10, y=10).
В обработчике onReleased выполняет код при отпускании мыши, когда перетаскивание завершено:
onReleased: { dragItem.x = dragItem.beginDrag.x dragItem.y = dragItem.beginDrag.y }
То есть свойствам x и y прямоугольника присваиваются начальные значения, сохраненные в beginDrag.
Таким образом, куда бы мы не перетащили прямоугольник, он всегда будет возвращаться на предыдущую позицию. Теперь пойдем дальше и добавим в окне область для перетаскивания:
import QtQuick Window { width: 250 height: 150 visible: true title: "METANIT.COM" Rectangle { id: dragItem x: 10; y: 10 width: 50; height: 50 color: "red" Drag.active: dragArea.drag.active property point beginDrag // для фиксации начальной позиции property bool caught: false // элемент в DropArea z: dragArea.drag.active || dragArea.pressed ? 2 : 1 MouseArea { id: dragArea anchors.fill: parent drag.target: parent // по нажатию устанавливаем начальную позицию прямоугольника onPressed: dragItem.beginDrag = Qt.point(dragItem.x, dragItem.y) onReleased: { // возвращаемся на начальную позицию if(!dragItem.caught){ dragItem.x = dragItem.beginDrag.x dragItem.y = dragItem.beginDrag.y } } } } Rectangle { // обработчик вхождения элемента в DropArea function onEntered(drag){ drag.source.caught = true } x: parent.width/2 width: parent.width/2 ; height:parent.height color: "silver" DropArea { id: dropArea anchors.fill: parent // обработчик ухода элемента из DropArea onExited: drag.source.caught = false } Component.onCompleted: { // связь сигнала entered с onEntered dropArea.entered.connect(onEntered) } } }
Прежде всего в перетаскиваемый элемент Rectangle добавлено новое свойство
property bool caught: false // элемент в DropArea
Если это свойство равно true, то элемент находится в пределах DropArea.
И теперь обработчик опускания мыши в MouseArea возвращает прямоугольник на начальную позицию, если это свойство равно false:
onReleased: { // возвращаемся на начальную позицию if(!dragItem.caught){ dragItem.x = dragItem.beginDrag.x dragItem.y = dragItem.beginDrag.y } }
В конце кода окна добавлен второй прямоугольник Rectangle, который будет представлять область перетаскивания. Этот прямоугольник занимает правую половину окна. А все пространство этого прямоугольника занимает DropArea.
Для DropArea определены два обработчика сигналов. Прежде всего, когда перетаскиваемый элемент входит в границы DropArea, то генерируется сигнал entered, который обрабатывается функцией onEntered:
function onEntered(drag){ drag.source.caught = true }
В качестве параметра в функцию передается информация о событии в виде объекта DragEvent. Его свойство source
указывает на перетаскиваемый элемент.
В данном случае просто переключаем у прямоугольника свойство caught в true, тем самым указывая, что перетаскиваемый элемент надо DropArea.
Другой сигнал - exited срабатывает, когда элемент покидает границы DropArea. Он обрабатывается функцией onExited, которая устанавливает для свойства caught значение false:
onExited: drag.source.caught = false
И чтобы при перетаскивании перемещаемый прямоугольник отображался поверх DropArea, у него устанавливаем свойство z:
z: dragArea.drag.active || dragArea.pressed ? 2 : 1
Таким образом, прямоугольник можно будет перетащить только на DropArea: