This commit is contained in:
zhuzichu 2023-09-22 17:35:02 +08:00
parent 67ef7f13aa
commit f88b330f8e
11 changed files with 175 additions and 30 deletions

View File

@ -302,7 +302,7 @@ FluObject{
onTap:{ navigationView.push(url) }
}
FluPaneItem{
title:"TreeView(Todo)"
title:"TreeView"
url:"qrc:/example/qml/page/T_TreeView.qml"
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
onTap:{ navigationView.push(url) }

View File

@ -12,7 +12,7 @@ FluContentPage {
function treeData(){
const dig = (path = '0', level = 4) => {
const list = [];
for (let i = 0; i < 9; i += 1) {
for (let i = 0; i < 6; i += 1) {
const key = `${path}-${i}`;
const treeNode = {
title: key,
@ -45,6 +45,10 @@ FluContentPage {
text:"共计%1条数据当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount())
}
FluText{
text:"共计选中%1条数据".arg(tree_view.selectionModel().length)
}
RowLayout{
spacing: 10
FluText{
@ -74,12 +78,17 @@ FluContentPage {
FluToggleSwitch{
id:switch_showline
text:"showLine"
checked: true
checked: false
}
FluToggleSwitch{
id:switch_draggable
text:"draggable"
checked: true
checked: false
}
FluToggleSwitch{
id:switch_checkable
text:"checkable"
checked: false
}
FluButton{
text:"all expand"
@ -111,6 +120,7 @@ FluContentPage {
cellHeight: slider_cell_height.value
draggable:switch_draggable.checked
showLine: switch_showline.checked
checkable:switch_checkable.checked
depthPadding: slider_depth_padding.value
Component.onCompleted: {
var data = treeData()

View File

@ -302,7 +302,7 @@ FluObject{
onTap:{ navigationView.push(url) }
}
FluPaneItem{
title:"TreeView(Todo)"
title:"TreeView"
url:"qrc:/example/qml/page/T_TreeView.qml"
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
onTap:{ navigationView.push(url) }

View File

@ -13,7 +13,7 @@ FluContentPage {
function treeData(){
const dig = (path = '0', level = 4) => {
const list = [];
for (let i = 0; i < 9; i += 1) {
for (let i = 0; i < 6; i += 1) {
const key = `${path}-${i}`;
const treeNode = {
title: key,
@ -46,6 +46,10 @@ FluContentPage {
text:"共计%1条数据当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount())
}
FluText{
text:"共计选中%1条数据".arg(tree_view.selectionModel().length)
}
RowLayout{
spacing: 10
FluText{
@ -75,12 +79,17 @@ FluContentPage {
FluToggleSwitch{
id:switch_showline
text:"showLine"
checked: true
checked: false
}
FluToggleSwitch{
id:switch_draggable
text:"draggable"
checked: true
checked: false
}
FluToggleSwitch{
id:switch_checkable
text:"checkable"
checked: false
}
FluButton{
text:"all expand"
@ -112,6 +121,7 @@ FluContentPage {
cellHeight: slider_cell_height.value
draggable:switch_draggable.checked
showLine: switch_showline.checked
checkable:switch_checkable.checked
depthPadding: slider_depth_padding.value
Component.onCompleted: {
var data = treeData()

View File

@ -1,8 +1,5 @@
#include "FluRectangle.h"
#include <QPainterPath>
#include <QSGClipNode>
#include <QQuickWindow>
FluRectangle::FluRectangle(QQuickItem* parent) : QQuickPaintedItem(parent){
setFlag(ItemHasContents, true);
@ -24,7 +21,7 @@ void FluRectangle::paint(QPainter* painter){
path.arcTo(QRectF(QPointF(rect.topLeft()), QSize(_radius[0] * 2, _radius[0] * 2)), 90, 90);
path.lineTo(rect.bottomLeft() - QPointF(0, _radius[3]));
path.arcTo(QRectF(QPointF(rect.bottomLeft() - QPointF(0, _radius[3] * 2)), QSize(_radius[3] * 2, _radius[3] * 2)), 180, 90);
path.lineTo(rect.bottomLeft() + QPointF(_radius[2], 0));
path.lineTo(rect.bottomRight() - QPointF(_radius[2], 0));
path.arcTo(QRectF(QPointF(rect.bottomRight() - QPointF(_radius[2] * 2, _radius[2] * 2)), QSize(_radius[2] * 2, _radius[2] * 2)), 270, 90);
painter->fillPath(path,_color);
painter->restore();

View File

@ -76,6 +76,43 @@ QObject* FluTreeModel::getRow(int row){
return _rows.at(row);
}
void FluTreeModel::checkRow(int row,bool chekced){
auto itemData = _rows.at(row);
if(itemData->hasChildren()){
QList<Node*> stack = itemData->_children;
std::reverse(stack.begin(), stack.end());
while (stack.count() > 0) {
auto item = stack.at(stack.count()-1);
stack.pop_back();
if(!item->hasChildren()){
item->_checked = chekced;
}
QList<Node*> children = item->_children;
if(!children.isEmpty()){
std::reverse(children.begin(), children.end());
foreach (auto c, children) {
stack.append(c);
}
}
}
}else{
if(itemData->_checked == chekced){
return;
}
itemData->_checked = chekced;
}
Q_EMIT layoutChanged(QList<QPersistentModelIndex>(),QAbstractItemModel::VerticalSortHint);
QList<Node*> data;
foreach (auto item, _dataSource) {
if(!item->hasChildren()){
if(item->_checked){
data.append(item);
}
}
}
selectionModel(data);
}
void FluTreeModel::setDataSource(QList<QMap<QString,QVariant>> data){
_dataSource.clear();
if(_root){
@ -84,7 +121,6 @@ void FluTreeModel::setDataSource(QList<QMap<QString,QVariant>> data){
}
_root = new Node(this);
std::reverse(data.begin(), data.end());
auto startTime = QDateTime::currentMSecsSinceEpoch();
while (data.count() > 0) {
auto item = data.at(data.count()-1);
data.pop_back();
@ -114,7 +150,6 @@ void FluTreeModel::setDataSource(QList<QMap<QString,QVariant>> data){
}
}
}
auto endTime = QDateTime::currentMSecsSinceEpoch();
beginResetModel();
_rows = _dataSource;
endResetModel();
@ -204,6 +239,7 @@ void FluTreeModel::dragAnddrop(int dragIndex,int dropIndex,bool isDropTopArea){
}
_rows.move(dragIndex,targetIndex);
endMoveRows();
Q_EMIT layoutAboutToBeChanged();
if(dragItem->_parent == dropItem->_parent){
QList<Node*>* children = &(dragItem->_parent->_children);

View File

@ -13,6 +13,7 @@ class Node : public QObject{
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(int depth READ depth CONSTANT)
Q_PROPERTY(bool isExpanded READ isExpanded CONSTANT)
Q_PROPERTY(bool checked READ checked CONSTANT)
public:
explicit Node(QObject *parent = nullptr);
Q_INVOKABLE QString key(){return _key;};
@ -30,6 +31,18 @@ public:
}
return true;
}
Q_INVOKABLE bool checked(){
if(!hasChildren()){
return _checked;
}
foreach (auto item, _children) {
if(!item->checked()){
return false;
}
}
return true;
};
Q_INVOKABLE bool hideLineFooter(){
if(_parent){
auto childIndex = _parent->_children.indexOf(this);
@ -57,6 +70,7 @@ public:
QString _key="";
QString _title="";
int _depth=0;
bool _checked = false;
bool _isExpanded=true;
QList<Node*> _children;
Node* _parent = nullptr;
@ -66,6 +80,7 @@ class FluTreeModel : public QAbstractItemModel
{
Q_OBJECT
Q_PROPERTY_AUTO(int,dataSourceSize)
Q_PROPERTY_AUTO(QList<Node*>,selectionModel)
QML_NAMED_ELEMENT(FluTreeModel)
QML_ADDED_IN_MINOR_VERSION(1)
public:
@ -87,6 +102,7 @@ public:
Q_INVOKABLE void dragAnddrop(int dragIndex,int dropIndex,bool isDropTopArea);
Q_INVOKABLE Node* getNode(int row);
Q_INVOKABLE void refreshNode(int row);
Q_INVOKABLE void checkRow(int row,bool chekced);
Q_INVOKABLE bool hitHasChildrenExpanded(int row);
Q_INVOKABLE void allExpand();
Q_INVOKABLE void allCollapse();

View File

@ -22,6 +22,7 @@ Button {
property alias textColor: btn_text.textColor
property bool textRight: true
property real textSpacing: 6
property bool enableAnimation: FluTheme.enableAnimation
property var clickListener : function(){
checked = !checked
}
@ -39,8 +40,9 @@ Button {
visible: control.activeFocus
}
}
horizontalPadding:2
verticalPadding: 2
horizontalPadding:0
verticalPadding: 0
padding: 0
Accessible.role: Accessible.Button
Accessible.name: control.text
Accessible.description: contentDescription
@ -91,7 +93,7 @@ Button {
return normalColor
}
Behavior on color {
enabled: FluTheme.enableAnimation
enabled: control.enableAnimation
ColorAnimation{
duration: 83
}
@ -103,7 +105,7 @@ Button {
visible: checked
iconColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
Behavior on visible {
enabled: FluTheme.enableAnimation
enabled: control.enableAnimation
NumberAnimation{
duration: 83
}

View File

@ -12,6 +12,7 @@ Item {
property bool draggable: false
property int cellHeight: 30
property int depthPadding: 30
property bool checkable: false
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 {
@ -50,7 +51,7 @@ Item {
}
NumberAnimation {
properties: "opacity"
duration: 300
duration: 88
from: 0
to: 1
}
@ -69,7 +70,7 @@ Item {
}
NumberAnimation {
properties: "opacity"
duration: 300
duration: 88
from: 0
to: 1
}
@ -197,6 +198,11 @@ Item {
var viewPos = table_view.mapToGlobal(0,0)
var y = table_view.contentY + pos.y-viewPos.y
var index = Math.floor(y/control.cellHeight)
if(index<0 || index>table_view.count-1){
d.dropIndex = -1
return
}
console.debug(index)
if(tree_model.hitHasChildrenExpanded(index) && y>index*control.cellHeight+control.cellHeight/2){
d.dropIndex = index + 1
d.isDropTopArea = true
@ -321,7 +327,10 @@ Item {
if(isCurrent){
return Qt.rgba(1,1,1,0.06)
}
if(item_mouse.containsMouse){
if(item_mouse.containsMouse || item_check_box.hovered){
return Qt.rgba(1,1,1,0.03)
}
if(item_loader_expand.item && item_loader_expand.item.hovered){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
@ -329,7 +338,10 @@ Item {
if(isCurrent){
return Qt.rgba(0,0,0,0.06)
}
if(item_mouse.containsMouse){
if(item_mouse.containsMouse || item_check_box.hovered){
return Qt.rgba(0,0,0,0.03)
}
if(item_loader_expand.item && item_loader_expand.item.hovered){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
@ -341,6 +353,7 @@ Item {
height: parent.height
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
spacing: 0
anchors.leftMargin: 14 + control.depthPadding*itemModel.depth
Component{
id:com_icon_btn
@ -357,16 +370,36 @@ Item {
}
}
}
Loader{
id:item_loader_expand
Layout.preferredWidth: 20
Layout.preferredHeight: 20
sourceComponent: itemModel.hasChildren() ? com_icon_btn : undefined
Layout.alignment: Qt.AlignVCenter
}
FluCheckBox{
id:item_check_box
Layout.preferredWidth: 18
Layout.preferredHeight: 18
Layout.leftMargin: 5
horizontalPadding:0
verticalPadding: 0
checked: itemModel.checked
enableAnimation:false
visible: control.checkable
padding: 0
clickListener: function(){
tree_model.checkRow(rowIndex,!itemModel.checked)
}
Layout.alignment: Qt.AlignVCenter
}
Loader{
property var modelData: itemModel
property var itemMouse: item_mouse
id:item_loader_cell
Layout.leftMargin: 10
Layout.preferredWidth: {
if(item){
return item.width
@ -397,6 +430,9 @@ Item {
}
}
}
function selectionModel(){
return tree_model.selectionModel
}
function count(){
return tree_model.dataSourceSize
}

View File

@ -23,6 +23,7 @@ Button {
property alias textColor: btn_text.textColor
property bool textRight: true
property real textSpacing: 6
property bool enableAnimation: FluTheme.enableAnimation
property var clickListener : function(){
checked = !checked
}
@ -35,8 +36,9 @@ Button {
visible: control.activeFocus
}
}
horizontalPadding:2
verticalPadding: 2
horizontalPadding:0
verticalPadding: 0
padding: 0
Accessible.role: Accessible.Button
Accessible.name: control.text
Accessible.description: contentDescription
@ -87,7 +89,7 @@ Button {
return normalColor
}
Behavior on color {
enabled: FluTheme.enableAnimation
enabled: control.enableAnimation
ColorAnimation{
duration: 83
}
@ -99,7 +101,7 @@ Button {
visible: checked
iconColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
Behavior on visible {
enabled: FluTheme.enableAnimation
enabled: control.enableAnimation
NumberAnimation{
duration: 83
}

View File

@ -12,6 +12,7 @@ Item {
property bool draggable: false
property int cellHeight: 30
property int depthPadding: 30
property bool checkable: false
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 {
@ -50,7 +51,7 @@ Item {
}
NumberAnimation {
properties: "opacity"
duration: 300
duration: 88
from: 0
to: 1
}
@ -69,7 +70,7 @@ Item {
}
NumberAnimation {
properties: "opacity"
duration: 300
duration: 88
from: 0
to: 1
}
@ -197,6 +198,11 @@ Item {
var viewPos = table_view.mapToGlobal(0,0)
var y = table_view.contentY + pos.y-viewPos.y
var index = Math.floor(y/control.cellHeight)
if(index<0 || index>table_view.count-1){
d.dropIndex = -1
return
}
console.debug(index)
if(tree_model.hitHasChildrenExpanded(index) && y>index*control.cellHeight+control.cellHeight/2){
d.dropIndex = index + 1
d.isDropTopArea = true
@ -321,7 +327,10 @@ Item {
if(isCurrent){
return Qt.rgba(1,1,1,0.06)
}
if(item_mouse.containsMouse){
if(item_mouse.containsMouse || item_check_box.hovered){
return Qt.rgba(1,1,1,0.03)
}
if(item_loader_expand.item && item_loader_expand.item.hovered){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
@ -329,7 +338,10 @@ Item {
if(isCurrent){
return Qt.rgba(0,0,0,0.06)
}
if(item_mouse.containsMouse){
if(item_mouse.containsMouse || item_check_box.hovered){
return Qt.rgba(0,0,0,0.03)
}
if(item_loader_expand.item && item_loader_expand.item.hovered){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
@ -341,6 +353,7 @@ Item {
height: parent.height
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
spacing: 0
anchors.leftMargin: 14 + control.depthPadding*itemModel.depth
Component{
id:com_icon_btn
@ -357,16 +370,36 @@ Item {
}
}
}
Loader{
id:item_loader_expand
Layout.preferredWidth: 20
Layout.preferredHeight: 20
sourceComponent: itemModel.hasChildren() ? com_icon_btn : undefined
Layout.alignment: Qt.AlignVCenter
}
FluCheckBox{
id:item_check_box
Layout.preferredWidth: 18
Layout.preferredHeight: 18
Layout.leftMargin: 5
horizontalPadding:0
verticalPadding: 0
checked: itemModel.checked
enableAnimation:false
visible: control.checkable
padding: 0
clickListener: function(){
tree_model.checkRow(rowIndex,!itemModel.checked)
}
Layout.alignment: Qt.AlignVCenter
}
Loader{
property var modelData: itemModel
property var itemMouse: item_mouse
id:item_loader_cell
Layout.leftMargin: 10
Layout.preferredWidth: {
if(item){
return item.width
@ -397,6 +430,9 @@ Item {
}
}
}
function selectionModel(){
return tree_model.selectionModel
}
function count(){
return tree_model.dataSourceSize
}