From be194e762494a0bf0e2824870b19cf3453c676e2 Mon Sep 17 00:00:00 2001 From: zhuzichu Date: Fri, 15 Sep 2023 19:11:55 +0800 Subject: [PATCH] update --- example/qml-Qt6/page/T_InfoBar.qml | 10 +- example/qml-Qt6/page/T_TreeView.qml | 52 +++- example/qml/page/T_InfoBar.qml | 10 +- example/qml/page/T_TreeView.qml | 52 +++- src/FluTreeModel.cpp | 61 ++++ src/FluTreeModel.h | 27 ++ src/FluentUI.cpp | 2 + .../imports/FluentUI/Controls/FluInfoBar.qml | 47 +++- .../imports/FluentUI/Controls/FluTreeItem.qml | 1 + .../imports/FluentUI/Controls/FluTreeView.qml | 201 ++++++++++--- .../imports/FluentUI/Controls/FluWindow.qml | 4 +- .../imports/FluentUI/Controls/FluInfoBar.qml | 47 +++- src/Qt6/imports/FluentUI/Controls/FluPage.qml | 9 + .../imports/FluentUI/Controls/FluTreeItem.qml | 1 + .../imports/FluentUI/Controls/FluTreeView.qml | 264 ++++++++++++------ .../imports/FluentUI/Controls/FluWindow.qml | 4 +- 16 files changed, 637 insertions(+), 155 deletions(-) create mode 100644 src/FluTreeModel.cpp create mode 100644 src/FluTreeModel.h diff --git a/example/qml-Qt6/page/T_InfoBar.qml b/example/qml-Qt6/page/T_InfoBar.qml index 24e3d907..328a2d79 100644 --- a/example/qml-Qt6/page/T_InfoBar.qml +++ b/example/qml-Qt6/page/T_InfoBar.qml @@ -12,7 +12,7 @@ FluScrollablePage{ FluArea{ Layout.fillWidth: true Layout.topMargin: 20 - height: 240 + height: 270 paddings: 10 ColumnLayout{ spacing: 14 @@ -23,7 +23,7 @@ FluScrollablePage{ FluButton{ text:"Info" onClicked: { - showInfo("这是一个Info样式的InfoBar") + showInfo("这是一个Info样式的InfoBar",0,"123") } } FluButton{ @@ -44,6 +44,12 @@ FluScrollablePage{ showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar") } } + FluButton{ + text:"手动关闭的InfoBar" + onClicked: { + showInfo("这是一个Info样式的InfoBar",0,"支持手动关闭") + } + } FluButton{ text:"Loading" onClicked: { diff --git a/example/qml-Qt6/page/T_TreeView.qml b/example/qml-Qt6/page/T_TreeView.qml index bf940b19..f4c027e2 100644 --- a/example/qml-Qt6/page/T_TreeView.qml +++ b/example/qml-Qt6/page/T_TreeView.qml @@ -12,7 +12,7 @@ FluScrollablePage { function treeData(){ const dig = (path = '0', level = 4) => { const list = []; - for (let i = 0; i < 6; i += 1) { + for (let i = 0; i < 10; i += 1) { const key = `${path}-${i}`; const treeNode = { title: key, @@ -32,33 +32,71 @@ FluScrollablePage { Layout.fillWidth: true Layout.topMargin: 10 paddings: 10 - height: 60 - FluText{ - text:"共计:%1条数据".arg(tree_view.count()) + height: 80 + Column{ anchors.verticalCenter: parent.verticalCenter + spacing: 10 + FluText{ + text:"高性能树控件,新的TreeView用TableView实现!!" + } + FluText{ + text:"共计:%1条数据,当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount()) + } } } - FluArea{ Layout.fillWidth: true Layout.topMargin: 10 paddings: 10 height: 400 + Item{ + anchors.fill: tree_view + FluShadow{} + } FluTreeView{ id:tree_view - width:240 + width:slider_width.value anchors{ top:parent.top left:parent.left bottom:parent.bottom } + showLine: switch_showline.checked Component.onCompleted: { var data = treeData() dataSource = data } } - } + Column{ + anchors{ + top:parent.top + topMargin: 10 + bottomMargin: 10 + rightMargin: 10 + bottom:parent.bottom + right: parent.right + } + RowLayout{ + spacing: 10 + FluText{ + text:"width:" + Layout.alignment: Qt.AlignVCenter + } + FluSlider{ + id:slider_width + value: 200 + from: 160 + to:320 + } + } + FluToggleSwitch{ + id:switch_showline + text:"showLine" + checked: true + } + } + } CodeExpander{ Layout.fillWidth: true Layout.topMargin: -1 diff --git a/example/qml/page/T_InfoBar.qml b/example/qml/page/T_InfoBar.qml index 55dd065a..86329e09 100644 --- a/example/qml/page/T_InfoBar.qml +++ b/example/qml/page/T_InfoBar.qml @@ -13,7 +13,7 @@ FluScrollablePage{ FluArea{ Layout.fillWidth: true Layout.topMargin: 20 - height: 240 + height: 270 paddings: 10 ColumnLayout{ spacing: 14 @@ -24,7 +24,7 @@ FluScrollablePage{ FluButton{ text:"Info" onClicked: { - showInfo("这是一个Info样式的InfoBar") + showInfo("这是一个Info样式的InfoBar",0,"123") } } FluButton{ @@ -45,6 +45,12 @@ FluScrollablePage{ showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar") } } + FluButton{ + text:"手动关闭的InfoBar" + onClicked: { + showInfo("这是一个Info样式的InfoBar",0,"支持手动关闭") + } + } FluButton{ text:"Loading" onClicked: { diff --git a/example/qml/page/T_TreeView.qml b/example/qml/page/T_TreeView.qml index 2db8f164..08e28698 100644 --- a/example/qml/page/T_TreeView.qml +++ b/example/qml/page/T_TreeView.qml @@ -13,7 +13,7 @@ FluScrollablePage { function treeData(){ const dig = (path = '0', level = 4) => { const list = []; - for (let i = 0; i < 6; i += 1) { + for (let i = 0; i < 10; i += 1) { const key = `${path}-${i}`; const treeNode = { title: key, @@ -33,33 +33,71 @@ FluScrollablePage { Layout.fillWidth: true Layout.topMargin: 10 paddings: 10 - height: 60 - FluText{ - text:"共计:%1条数据".arg(tree_view.count()) + height: 80 + Column{ anchors.verticalCenter: parent.verticalCenter + spacing: 10 + FluText{ + text:"高性能树控件,新的TreeView用TableView实现!!" + } + FluText{ + text:"共计:%1条数据,当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount()) + } } } - FluArea{ Layout.fillWidth: true Layout.topMargin: 10 paddings: 10 height: 400 + Item{ + anchors.fill: tree_view + FluShadow{} + } FluTreeView{ id:tree_view - width:240 + width:slider_width.value anchors{ top:parent.top left:parent.left bottom:parent.bottom } + showLine: switch_showline.checked Component.onCompleted: { var data = treeData() dataSource = data } } - } + Column{ + anchors{ + top:parent.top + topMargin: 10 + bottomMargin: 10 + rightMargin: 10 + bottom:parent.bottom + right: parent.right + } + RowLayout{ + spacing: 10 + FluText{ + text:"width:" + Layout.alignment: Qt.AlignVCenter + } + FluSlider{ + id:slider_width + value: 200 + from: 160 + to:320 + } + } + FluToggleSwitch{ + id:switch_showline + text:"showLine" + checked: true + } + } + } CodeExpander{ Layout.fillWidth: true Layout.topMargin: -1 diff --git a/src/FluTreeModel.cpp b/src/FluTreeModel.cpp new file mode 100644 index 00000000..933f97ec --- /dev/null +++ b/src/FluTreeModel.cpp @@ -0,0 +1,61 @@ +#include "FluTreeModel.h" + +#include + +FluTreeModel::FluTreeModel(QObject *parent) + : QAbstractTableModel{parent} +{ + +} + +int FluTreeModel::rowCount(const QModelIndex &parent) const { + return _rows.count(); +}; + +int FluTreeModel::columnCount(const QModelIndex &parent) const { + return 1;; +}; + +QVariant FluTreeModel::data(const QModelIndex &index, int role) const { + switch (role) { + case Qt::DisplayRole: + return QVariant::fromValue(_rows.at(index.row())); + default: + break; + } + return QVariant(); +}; + +QHash FluTreeModel::roleNames() const { + return { {Qt::DisplayRole, "display"} }; +}; + +void FluTreeModel::setData(QList data){ + beginResetModel(); + _rows = data; + endResetModel(); +} + +void FluTreeModel::removeRows(int row,int count){ + if (row < 0 || row + count > _rows.size()) + return; + beginRemoveRows(QModelIndex(),row, row + count - 1); + for (int i = 0; i < count; ++i) { + _rows.removeAt(row); + } + endRemoveRows(); +} + +void FluTreeModel::insertRows(int row,QList data){ + if (row < 0 || row > _rows.size()) + return; + beginInsertRows(QModelIndex(), row, row + data.size() - 1); + for (const auto& item : data) { + _rows.insert(row++, item); + } + endInsertRows(); +} + +QObject* FluTreeModel::getRow(int row){ + return _rows.at(row); +} diff --git a/src/FluTreeModel.h b/src/FluTreeModel.h new file mode 100644 index 00000000..2d1c307e --- /dev/null +++ b/src/FluTreeModel.h @@ -0,0 +1,27 @@ +#ifndef FLUTREEMODEL_H +#define FLUTREEMODEL_H + +#include +#include +#include + +class FluTreeModel : public QAbstractTableModel +{ + Q_OBJECT + QML_NAMED_ELEMENT(FluTreeModel) + QML_ADDED_IN_MINOR_VERSION(1) +public: + explicit FluTreeModel(QObject *parent = nullptr); + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + Q_INVOKABLE void removeRows(int row,int count); + Q_INVOKABLE void insertRows(int row,QList data); + Q_INVOKABLE QObject* getRow(int row); + Q_INVOKABLE void setData(QList data); +private: + QList _rows; +}; + +#endif // FLUTREEMODEL_H diff --git a/src/FluentUI.cpp b/src/FluentUI.cpp index 58969b75..0efeaf75 100644 --- a/src/FluentUI.cpp +++ b/src/FluentUI.cpp @@ -13,6 +13,7 @@ #include "FluWatermark.h" #include "FluCaptcha.h" #include "FluEventBus.h" +#include "FluTreeModel.h" #include "FluViewModel.h" #include "Screenshot.h" #include "QRCode.h" @@ -53,6 +54,7 @@ void FluentUI::registerTypes(const char *uri){ qmlRegisterType(uri,major,minor,"HttpRequest"); qmlRegisterType(uri,major,minor,"FluEvent"); qmlRegisterType(uri,major,minor,"FluViewModel"); + qmlRegisterType(uri,major,minor,"FluTreeModel"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/ColorPicker.qml"),uri,major,minor,"ColorPicker"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/Content/Checkerboard.qml"),uri,major,minor,"Checkerboard"); diff --git a/src/Qt5/imports/FluentUI/Controls/FluInfoBar.qml b/src/Qt5/imports/FluentUI/Controls/FluInfoBar.qml index f24f627d..53bdc85c 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluInfoBar.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluInfoBar.qml @@ -83,7 +83,7 @@ FluObject { } Timer { id:delayTimer - interval: duration; running: true; repeat: true + interval: duration; running: duration > 0; repeat: duration > 0 onTriggered: content.close(); } Loader{ @@ -184,10 +184,47 @@ FluObject { } } - FluText{ - text:_super.text - wrapMode: Text.WrapAnywhere - width: Math.min(implicitWidth,mcontrol.maxWidth) + Column{ + spacing: 5 + FluText{ + text:_super.text + wrapMode: Text.WrapAnywhere + width: Math.min(implicitWidth,mcontrol.maxWidth) + } + FluText{ + text: _super.moremsg + visible: _super.moremsg + wrapMode : Text.WordWrap + textColor: FluColors.Grey120 + } + } + + FluIconButton{ + iconSource: FluentIcons.ChromeClose + iconSize: 10 + y:5 + x:parent.width-35 + visible: _super.duration<=0 + iconColor: { + if(FluTheme.dark){ + switch(_super.type){ + case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1); + case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1); + case mcontrol.const_info: return FluTheme.primaryColor.lighter; + case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1); + } + return "#FFFFFF" + }else{ + switch(_super.type){ + case mcontrol.const_success: return "#0f7b0f"; + case mcontrol.const_warning: return "#9d5d00"; + case mcontrol.const_info: return "#0066b4"; + case mcontrol.const_error: return "#c42b1c"; + } + return "#FFFFFF" + } + } + onClicked: _super.close() } } } diff --git a/src/Qt5/imports/FluentUI/Controls/FluTreeItem.qml b/src/Qt5/imports/FluentUI/Controls/FluTreeItem.qml index 48b8b10f..ca6f5f86 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTreeItem.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTreeItem.qml @@ -7,6 +7,7 @@ QtObject { property int depth: 0 property bool isExpanded: true property var __parent + property int __childIndex: 0 property bool __expanded:{ var p = __parent; while (p) { diff --git a/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml b/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml index e0bd081e..a8eb4304 100644 --- a/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml +++ b/src/Qt5/imports/FluentUI/Controls/FluTreeView.qml @@ -6,14 +6,14 @@ import Qt.labs.qmlmodels 1.0 import FluentUI 1.0 Item { + property int currentIndex : -1 property var dataSource + property bool showLine: true + property color lineColor: FluTheme.dark ? Qt.rgba(111/255,111/255,111/255,1) : Qt.rgba(217/255,217/255,217/255,1) id:control QtObject { id:d - signal refreshLayout() - onRefreshLayout: { - table_view.forceLayout() - } + property var rowData: [] function handleTree(treeData) { var comItem = Qt.createComponent("FluTreeItem.qml"); if (comItem.status !== Component.Ready) { @@ -22,15 +22,16 @@ Item { var stack = [] var rawData = [] for (var item of treeData) { - stack.push({node:item,depth:0,isExpanded:true,__parent:undefined}) + stack.push({node:item,depth:0,isExpanded:true,__parent:undefined,__childIndex:0}) } stack = stack.reverse() var index =0 while (stack.length > 0) { - const { node, depth,isExpanded,__parent} = stack.pop(); + const { node, depth,isExpanded,__parent,__childIndex} = stack.pop(); node.depth = depth; node.isExpanded = isExpanded; node.__parent = __parent; + node.__childIndex = __childIndex; var objItem = comItem.createObject(table_view); objItem.title = node.title objItem.key = node.key @@ -38,13 +39,16 @@ Item { objItem.isExpanded = node.isExpanded objItem.__parent = node.__parent objItem.children = node.children + objItem.__childIndex = node.__childIndex objItem.index = index index = index + 1; - rawData.push({display:objItem}) + rawData.push(objItem) if (node.children && node.children.length > 0) { const children = node.children.reverse(); + var childIndex = children.length-1 for (const child of children) { - stack.push({ node: child, depth: depth + 1,isExpanded:true,__parent:objItem}); + stack.push({ node: child, depth: depth + 1,isExpanded:true,__parent:objItem,__childIndex:childIndex}); + childIndex=childIndex-1; } } } @@ -52,52 +56,139 @@ Item { } } onDataSourceChanged: { - table_model.clear() - var data = d.handleTree(dataSource) - table_model.rows = data - table_view.forceLayout() - console.debug("共计:%1条数据".arg(table_model.rowCount)) + d.rowData = d.handleTree(dataSource) + tree_model.setData(d.rowData) } - TableModel { - id:table_model - TableModelColumn { display: "display" } + FluTreeModel{ + id:tree_model + } + Timer{ + id:timer_refresh + interval: 10 + onTriggered: { + table_view.forceLayout() + } } TableView{ id:table_view ScrollBar.horizontal: FluScrollBar{} ScrollBar.vertical: FluScrollBar{} boundsBehavior: Flickable.StopAtBounds - model: table_model + model: tree_model clip: true anchors.fill: parent - rowHeightProvider: function(row) { - if(table_model.getRow(row).display.__expanded){ - return 38 - } - return 0 + onContentYChanged:{ + timer_refresh.restart() } + reuseItems: false delegate: Item { + property bool hasChildren: { + if(display.children){ + return true + } + return false + } + property var itemData: display + property bool vlineVisible: display.depth !== 0 && control.showLine + property bool hlineVisible: display.depth !== 0 && control.showLine && !hasChildren + property bool isLastIndex : { + if(display.__parent && display.__parent.children){ + return display.__childIndex === display.__parent.children.length-1 + } + return false + } + property bool isCurrent: control.currentIndex === row implicitWidth: 46 + item_layout_text.width + 30*display.depth + implicitHeight: 30 + Rectangle{ + width: 1 + color: control.lineColor + visible: hlineVisible + height: isLastIndex ? parent.height/2 : parent.height + anchors{ + top: parent.top + right: layout_row.left + } + } + Rectangle{ + height: 1 + color: control.lineColor + visible: hlineVisible + width: 18 + anchors{ + right: layout_row.left + rightMargin: -18 + verticalCenter: parent.verticalCenter + } + } + Repeater{ + model: Math.max(display.depth-1,0) + delegate: Rectangle{ + required property int index + width: 1 + color: control.lineColor + visible: vlineVisible + anchors{ + top:parent.top + bottom: parent.bottom + left: parent.left + leftMargin: 30*(index+2) - 8 + } + } + } RowLayout{ + id:layout_row anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 14 + 30*display.depth - FluIcon{ - rotation: display.isExpanded?0:-90 - iconSource:FluentIcons.ChevronDown - iconSize: 15 - Layout.alignment: Qt.AlignVCenter - opacity: { - if(display.children){ - return true + FluIconButton{ + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + enabled: opacity + opacity: hasChildren + contentItem: FluIcon{ + rotation: itemData.isExpanded?0:-90 + iconSource:FluentIcons.ChevronDown + iconSize: 16 + Behavior on rotation{ + NumberAnimation{ + duration: FluTheme.enableAnimation ? 167 : 0 + easing.type: Easing.OutCubic + } } - return false } - MouseArea{ - anchors.fill: parent - onClicked: { - display.isExpanded = !display.isExpanded - d.refreshLayout() + onClicked: { + var isExpanded = !itemData.isExpanded + itemData.isExpanded = isExpanded + var i,obj + if(isExpanded){ + for( i=0;i 0; repeat: duration > 0 onTriggered: content.close(); } Loader{ @@ -184,10 +184,47 @@ FluObject { } } - FluText{ - text:_super.text - wrapMode: Text.WrapAnywhere - width: Math.min(implicitWidth,mcontrol.maxWidth) + Column{ + spacing: 5 + FluText{ + text:_super.text + wrapMode: Text.WrapAnywhere + width: Math.min(implicitWidth,mcontrol.maxWidth) + } + FluText{ + text: _super.moremsg + visible: _super.moremsg + wrapMode : Text.WordWrap + textColor: FluColors.Grey120 + } + } + + FluIconButton{ + iconSource: FluentIcons.ChromeClose + iconSize: 10 + y:5 + x:parent.width-35 + visible: _super.duration<=0 + iconColor: { + if(FluTheme.dark){ + switch(_super.type){ + case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1); + case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1); + case mcontrol.const_info: return FluTheme.primaryColor.lighter; + case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1); + } + return "#FFFFFF" + }else{ + switch(_super.type){ + case mcontrol.const_success: return "#0f7b0f"; + case mcontrol.const_warning: return "#9d5d00"; + case mcontrol.const_info: return "#0066b4"; + case mcontrol.const_error: return "#c42b1c"; + } + return "#FFFFFF" + } + } + onClicked: _super.close() } } } diff --git a/src/Qt6/imports/FluentUI/Controls/FluPage.qml b/src/Qt6/imports/FluentUI/Controls/FluPage.qml index 74fd01e2..87420acd 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluPage.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluPage.qml @@ -8,6 +8,7 @@ Item { property int launchMode: FluPageType.SingleTop property bool animDisabled: false property string url : "" + signal animationEnd() id: control opacity: visible visible: false @@ -30,5 +31,13 @@ Item { } Component.onCompleted: { visible = true + timer.restart() + } + Timer{ + id:timer + interval: !animDisabled && FluTheme.enableAnimation ? 200 : 0 + onTriggered: { + control.animationEnd() + } } } diff --git a/src/Qt6/imports/FluentUI/Controls/FluTreeItem.qml b/src/Qt6/imports/FluentUI/Controls/FluTreeItem.qml index 2a95022a..2539f11a 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTreeItem.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTreeItem.qml @@ -7,6 +7,7 @@ QtObject { property int depth: 0 property bool isExpanded: true property var __parent + property int __childIndex: 0 property bool __expanded:{ var p = __parent; while (p) { diff --git a/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml b/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml index 2a9220a5..88df62b3 100644 --- a/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml +++ b/src/Qt6/imports/FluentUI/Controls/FluTreeView.qml @@ -6,14 +6,14 @@ import Qt.labs.qmlmodels import FluentUI Item { + property int currentIndex : -1 property var dataSource + property bool showLine: true + property color lineColor: FluTheme.dark ? Qt.rgba(111/255,111/255,111/255,1) : Qt.rgba(217/255,217/255,217/255,1) id:control QtObject { id:d - signal refreshLayout() - onRefreshLayout: { - table_view.forceLayout() - } + property var rowData: [] function handleTree(treeData) { var comItem = Qt.createComponent("FluTreeItem.qml"); if (comItem.status !== Component.Ready) { @@ -22,15 +22,16 @@ Item { var stack = [] var rawData = [] for (var item of treeData) { - stack.push({node:item,depth:0,isExpanded:true,__parent:undefined}) + stack.push({node:item,depth:0,isExpanded:true,__parent:undefined,__childIndex:0}) } stack = stack.reverse() var index =0 while (stack.length > 0) { - const { node, depth,isExpanded,__parent} = stack.pop(); + const { node, depth,isExpanded,__parent,__childIndex} = stack.pop(); node.depth = depth; node.isExpanded = isExpanded; node.__parent = __parent; + node.__childIndex = __childIndex; var objItem = comItem.createObject(table_view); objItem.title = node.title objItem.key = node.key @@ -38,13 +39,16 @@ Item { objItem.isExpanded = node.isExpanded objItem.__parent = node.__parent objItem.children = node.children + objItem.__childIndex = node.__childIndex objItem.index = index index = index + 1; - rawData.push({display:objItem}) + rawData.push(objItem) if (node.children && node.children.length > 0) { const children = node.children.reverse(); + var childIndex = children.length-1 for (const child of children) { - stack.push({ node: child, depth: depth + 1,isExpanded:true,__parent:objItem}); + stack.push({ node: child, depth: depth + 1,isExpanded:true,__parent:objItem,__childIndex:childIndex}); + childIndex=childIndex-1; } } } @@ -52,89 +56,188 @@ Item { } } onDataSourceChanged: { - table_model.clear() - var data = d.handleTree(dataSource) - table_model.rows = data - table_view.forceLayout() - console.debug("共计:%1条数据".arg(table_model.rowCount)) + d.rowData = d.handleTree(dataSource) + tree_model.setData(d.rowData) } - TableModel { - id:table_model - TableModelColumn { display: "display" } + FluTreeModel{ + id:tree_model } - ListView{ + Timer{ + id:timer_refresh + interval: 10 + onTriggered: { + table_view.forceLayout() + } + } + TableView{ + id:table_view + ScrollBar.horizontal: FluScrollBar{} + ScrollBar.vertical: FluScrollBar{} + boundsBehavior: Flickable.StopAtBounds + model: tree_model + clip: true anchors.fill: parent - TableView{ - id:table_view - ScrollBar.horizontal: FluScrollBar{} - ScrollBar.vertical: FluScrollBar{} - boundsBehavior: Flickable.StopAtBounds - model: table_model - clip: true - anchors.fill: parent - rowHeightProvider: function(row) { - if(table_model.getRow(row).display.__expanded){ - return 38 + onContentYChanged:{ + timer_refresh.restart() + } + reuseItems: false + delegate: Item { + property bool hasChildren: { + if(display.children){ + return true } - return 0 + return false } - delegate: Item { - implicitWidth: 46 + item_layout_text.width + 30*display.depth - RowLayout{ - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 14 + 30*display.depth - FluIcon{ - rotation: display.isExpanded?0:-90 + property var itemData: display + property bool vlineVisible: display.depth !== 0 && control.showLine + property bool hlineVisible: display.depth !== 0 && control.showLine && !hasChildren + property bool isLastIndex : { + if(display.__parent && display.__parent.children){ + return display.__childIndex === display.__parent.children.length-1 + } + return false + } + property bool isCurrent: control.currentIndex === row + implicitWidth: 46 + item_layout_text.width + 30*display.depth + implicitHeight: 30 + Rectangle{ + width: 1 + color: control.lineColor + visible: hlineVisible + height: isLastIndex ? parent.height/2 : parent.height + anchors{ + top: parent.top + right: layout_row.left + } + } + Rectangle{ + height: 1 + color: control.lineColor + visible: hlineVisible + width: 18 + anchors{ + right: layout_row.left + rightMargin: -18 + verticalCenter: parent.verticalCenter + } + } + Repeater{ + model: Math.max(display.depth-1,0) + delegate: Rectangle{ + required property int index + width: 1 + color: control.lineColor + visible: vlineVisible + anchors{ + top:parent.top + bottom: parent.bottom + left: parent.left + leftMargin: 30*(index+2) - 8 + } + } + } + RowLayout{ + id:layout_row + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 14 + 30*display.depth + FluIconButton{ + Layout.preferredWidth: 20 + Layout.preferredHeight: 20 + enabled: opacity + opacity: hasChildren + contentItem: FluIcon{ + rotation: itemData.isExpanded?0:-90 iconSource:FluentIcons.ChevronDown - iconSize: 15 - Layout.alignment: Qt.AlignVCenter - opacity: { - if(display.children){ - return true - } - return false - } - MouseArea{ - anchors.fill: parent - onClicked: { - display.isExpanded = !display.isExpanded - d.refreshLayout() + iconSize: 16 + Behavior on rotation{ + NumberAnimation{ + duration: FluTheme.enableAnimation ? 167 : 0 + easing.type: Easing.OutCubic } } } + onClicked: { + var isExpanded = !itemData.isExpanded + itemData.isExpanded = isExpanded + var i,obj + if(isExpanded){ + for( i=0;i