yxdy 9debdc84b8
Some checks are pending
Gallery App Build / macOS (push) Waiting to run
Gallery App Build / Windows (push) Waiting to run
Gallery App Build / Ubuntu (push) Waiting to run
树点击选不中问题
2025-03-07 18:04:16 +08:00

492 lines
22 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import QtQuick
import QtQuick.Controls
import FluentUI.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
/**
树控件
作者surfsky.cnblogs.com 2014-10
协议MIT 请保留本文档说明
功能
/递归树显示
/左侧一个箭头,点击可展开显示子树
/选中节点变色
/节点点击事件
/tag属性携带类似id的数据
异步方式点击箭头后请求子数据。异步模式的话节点要加上isLeaf属性点击箭头后动态加载数据
使用
TreeView {
anchors.fill: parent
id: tree
model: modelTree2
onSelectedItemChanged: console.log(item.text)
}
ListModel {
id: modelTree2
Component.onCompleted: {
modelTree2.append([
{title: "Node 1"},
{title: "Node 2", items: [
{title: "Node 21", items:[
{title: "Node 211"},
{title: "Node 212"}
]},
{title: "Node 22"}
]
},
{title: "Node 3"}
]);
}
}
参考 http://qt-project.org/forums/viewthread/30521/
*/
ScrollView {
id: view
// frameVisible: true
clip: true
// 输入属性
property var model
property int rowHeight: 30
property int columnIndent: 15
property int lineHeight: 1
property string expanderImage : "qrc:/qt/qml/Gallery/res/image/components/expand.png"
// 私有属性
property var currentNode // 当前节点数据
property var currentItem // 当前节点UI
property var start_drag: false
// 信号
signal selectedItemChanged(var item)
Timer {
id: timer
// Start the timer and execute the provided callback on every X milliseconds
function startTimer(callback, milliseconds) {
timer.interval = milliseconds;
timer.repeat = false;
timer.triggered.connect(callback);
timer.start();
}
// Stop the timer and unregister the callback
function stopTimer(callback) {
timer.stop();
timer.triggered.disconnect(callback);
}
}
// 节点数据展示的UI
property Component delegate: Label {
id: label
text: model.title ? model.title : 0
color: currentItem === model ? "#2a2a2a" : "white"
elide: Text.ElideRight
property var tag : model.tag
}
//
Loader {
id: content
onLoaded: item.isRoot = true
sourceComponent: treeBranch
property var items: model
// 背景条纹
// Column {
// anchors.fill: parent
// Repeater {
// model: 1 + Math.max(view.contentItem.height, view.height) / rowHeight
// Rectangle {
// objectName: "Faen"
// color: index % 2 ? "#eee" : "white"
// width: view.width ; height: rowHeight
// }
// }
// }
// 树节点组件
Component {
id: treeBranch
Item {
id: root
property bool isRoot: false
implicitHeight: column.implicitHeight
implicitWidth: column.implicitWidth
// Rectangle{
// width: {
// console.log(column.implicitHeight + "," + column.implicitWidth)
// return 0
// }
// height: column.implicitHeight
// color: "cyan"
// }
Column {
id: column
x: 2
Item {
height: isRoot ? 0 : rowHeight; width: 1
}
Repeater {
model: items
Item {
id: filler
width: Math.max(loader.width + columnIndent, row.width)
height: Math.max(row.height, loader.height)
property var _model: model
// 当前行背景色块--可拖拽!
Rectangle {
id: rowfill
x: view.mapToItem(rowfill, 0, 0).x
width: view.width
height: filler.height // rowHeight
visible: currentItem === model
color: Qt.rgba(15/255,147/255,83/255, 0.1)
border.color: Qt.rgba(34/255,197/255,94/255, 0.3)
border.width: 1
}
Rectangle {
id: rowfill_cur
x: view.mapToItem(rowfill_cur, 0, 0).x
width: view.width
height: rowHeight
visible: currentItem === model
color: Qt.rgba(15/255,147/255,83/255, 0.1)
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: "#77ED8B" }
GradientStop { position: 1.0; color: "#22C55E" }
}
radius: 5
}
Rectangle{
id: dragRect
x: view.mapToItem(rowfill, 0, 0).x
width: view.width
height: filler.height
color: "transparent"
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: dragRect
onPressed: {
// timer.startTimer(() => {
view.start_drag = true
dragRect.Drag.active = true
dragRect.Drag.source = dragRect
// }, 100)
}
onReleased: {
timer.stopTimer(()=> {
})
if (view.start_drag === true) {
dragRect.Drag.drop()
dragRect.Drag.active = false
dragRect.x = 0
dragRect.y = 0
}
}
onClicked: {
currentItem = model
console.log("click modelId =" + model.id)
}
// hoverEnabled: true
// onEntered: {
// // console.log(rowfill_cur.mapToItem(view, 0, 0).x)
// }
// onExited: {
// }
}
Drag.active: mouseArea.drag.active
Drag.dragType: Drag.Automatic
Drag.hotSpot.x: dragRect.width / 2
Drag.hotSpot.y: dragRect.height / 2
Drag.mimeData: { "uuid": model.id }
}
// 行数据UI
Row {
id: row
// 行图标
Item {
width: rowHeight
height: rowHeight
opacity: (!!model.items && !!model.items.count > 0) ? 1 : 0
Image {
id: expander
source: view.expanderImage
height: view.rowHeight * 0.6
fillMode: Image.PreserveAspectFit
opacity: mouse.containsMouse ? 1 : 0.7
anchors.centerIn: parent
rotation: loader.expanded ? 90 : 0
Behavior on rotation {NumberAnimation { duration: 120}}
ColorOverlay{
anchors.fill: parent
color: "#2a2a2a"
source: parent
visible: currentItem === model
}
}
MouseArea {
id: mouse
anchors.fill: parent
hoverEnabled: true
onClicked: loader.expanded = !loader.expanded
}
}
Item{
width: 19
height: rowHeight
Image{
width: 14
height: 12
anchors.centerIn: parent
fillMode: Image.PreserveAspectFit
source: "qrc:/qt/qml/Gallery/res/image/components/" + model.ico
ColorOverlay{
anchors.fill: parent
color: "#2a2a2a"
source: parent
visible: currentItem === model
}
}
}
// 行文本
Loader {
width: {
// console.log(view.width + "," + rowfill_cur.x)
return view.width + rowfill_cur.x - rowHeight - 20 - 60
}
property var model: _model
sourceComponent: delegate
anchors.verticalCenter: parent.verticalCenter
}
RowLayout{
width: 60
spacing: 5
Layout.rightMargin: 5
Rectangle{
Layout.preferredWidth: 25
Layout.preferredHeight: 25
color: "transparent"
radius: 5
Image{
source: "qrc:/qt/qml/Gallery/res/image/components/tree/ico_unlock.png"
width: 14
height: 12
// icon.color: currentItem === model ? "#2a2a2a" : "#ffffff"
ColorOverlay{
anchors.fill: parent
color: "#2a2a2a"
source: parent
visible: currentItem === model
}
anchors.centerIn: parent
}
MouseArea{
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = Qt.rgba(255/255,255/255,255/255, 0.05)
onExited: parent.color = "transparent"
}
}
Rectangle{
Layout.preferredWidth: 25
Layout.preferredHeight: 25
color: "transparent"
radius: 5
Image{
source: "qrc:/qt/qml/Gallery/res/image/components/tree/ico_eye.png"
width: 14
height: 12
ColorOverlay{
anchors.fill: parent
color: "#2a2a2a"
source: parent
visible: currentItem === model
}
anchors.centerIn: parent
}
MouseArea{
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = Qt.rgba(255/255,255/255,255/255, 0.05)
onExited: parent.color = "transparent"
}
}
}
}
DropArea {
id: mid_area
width: view.width
height: rowHeight - 10
anchors.left: parent.left
anchors.top: parent.top
Rectangle{
id: top_area_rect
width: parent.width
height: rowHeight
color: "transparent"
opacity: 0.2
}
onEntered: {
top_area_rect.color = "white"
}
onExited: {
top_area_rect.color = "transparent"
}
onDropped: {
top_area_rect.color = "transparent"
var sourceId = drop.getDataAsString("uuid")
var targetId = model.id
if (sourceId === targetId) {
console.log("same id!")
return
}
var sinfo = view.getInfoById(sourceId, view.model)
var einfo = view.getInfoById(targetId, view.model)
if (sinfo.mm == einfo.mm) {
// 先添加,后删除。
einfo.mm.get(einfo.index).items.append(sinfo.mm.get(sinfo.index))
sinfo.mm.remove(sinfo.index)
} else {
var tmp = view.getInfoById(targetId, sinfo.mm.get(sinfo.index).items)
if (!tmp) {
console.log('no same parent...OK')
// 先添加,后删除。
einfo.mm.get(einfo.index).items.append(sinfo.mm.get(sinfo.index))
sinfo.mm.remove(sinfo.index)
} else {
console.log('error luoji.')
}
}
console.log("sin=" + sinfo.index + ",ein=" + einfo.index)
}
}
DropArea {
width: view.width
height: rowHeight * 0.2
anchors.left: parent.left
anchors.bottom: parent.bottom
Rectangle{
id: bot_area_rect
width: parent.width
height: lineHeight
anchors.left: parent.left
anchors.bottom: parent.bottom
color: "transparent"
}
onEntered: {
bot_area_rect.color = "white"
}
onExited: {
bot_area_rect.color = "transparent"
}
onDropped: {
bot_area_rect.color = "transparent"
var sourceId = drop.getDataAsString("uuid")
var targetId = model.id
if (sourceId === targetId) {
console.log("same id!")
return
}
var sinfo = view.getInfoById(sourceId, view.model)
var einfo = view.getInfoById(targetId, view.model)
if (sinfo.mm !== einfo.mm) {
var tmp = view.getInfoById(targetId, sinfo.mm.get(sinfo.index).items)
if (!tmp) {
console.log("OK...")
var ryAdd = sinfo.mm.get(sinfo.index)
var addTmp = {id:ryAdd.id, title: ryAdd.title, items: ryAdd.items}
console.log(einfo.index + "------")
einfo.mm.insert(einfo.index + 1, ryAdd)
sinfo.mm.remove(sinfo.index)
}
} else {
if (sinfo.index < einfo.index) {
sinfo.mm.move(sinfo.index, einfo.index, 1)
} else if (sinfo.index > einfo.index) {
sinfo.mm.move(sinfo.index, einfo.index + 1, 1)
}
}
}
}
// 子树(递归自身)
Loader {
id: loader
x: columnIndent
height: expanded ? implicitHeight : 0
property var node: model
property bool expanded: false
property var items: model.items
property var text: model.title
sourceComponent: (expanded && !!model.items) ? treeBranch : undefined
}
}
}
}
}
}
}
function getInfoById(id, paModel) {
var index = -1
var mm = null
if (paModel) {
for (var i = 0;i<paModel.count;i++) {
if (paModel.get(i).items !== undefined && paModel.get(i).items.count > 0) {
var info = getInfoById(id, paModel.get(i).items)
if (info) {
index = info.index
mm = info.mm
break
}
}
if (paModel.get(i).id == id) {
index = i
mm = paModel
break
}
}
}
if (mm) {
return {index, mm}
}
return null
}
}