2023-09-15 19:11:55 +08:00
|
|
|
#include "FluTreeModel.h"
|
|
|
|
|
|
|
|
#include <QMetaEnum>
|
|
|
|
|
2023-09-22 00:11:58 +08:00
|
|
|
Node::Node(QObject *parent): QObject{parent}{
|
2023-09-17 20:36:33 +08:00
|
|
|
}
|
|
|
|
|
2023-09-22 00:11:58 +08:00
|
|
|
FluTreeModel::FluTreeModel(QObject *parent): QAbstractItemModel{parent}{
|
2023-09-17 20:36:33 +08:00
|
|
|
dataSourceSize(0);
|
2023-09-15 19:11:55 +08:00
|
|
|
}
|
|
|
|
|
2023-09-19 23:41:56 +08:00
|
|
|
QModelIndex FluTreeModel::parent(const QModelIndex &child) const{
|
|
|
|
return QModelIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
QModelIndex FluTreeModel::index(int row, int column,const QModelIndex &parent) const{
|
|
|
|
if (!hasIndex(row, column, parent) || parent.isValid())
|
|
|
|
return QModelIndex();
|
|
|
|
return createIndex(row, column, _rows.at(row));
|
|
|
|
}
|
|
|
|
|
2023-09-15 19:11:55 +08:00
|
|
|
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<int, QByteArray> FluTreeModel::roleNames() const {
|
2023-10-15 16:29:50 +08:00
|
|
|
return { {Qt::DisplayRole, "dataModel"} };
|
2023-09-15 19:11:55 +08:00
|
|
|
};
|
|
|
|
|
2023-09-17 20:36:33 +08:00
|
|
|
void FluTreeModel::setData(QList<Node*> data){
|
2023-09-15 19:11:55 +08:00
|
|
|
beginResetModel();
|
|
|
|
_rows = data;
|
|
|
|
endResetModel();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FluTreeModel::removeRows(int row,int count){
|
2023-09-17 20:36:33 +08:00
|
|
|
if (row < 0 || row + count > _rows.size() || count==0)
|
2023-09-15 19:11:55 +08:00
|
|
|
return;
|
2023-09-18 00:12:39 +08:00
|
|
|
beginRemoveRows(QModelIndex(),row, row + count - 1);
|
2023-09-21 18:29:09 +08:00
|
|
|
QList<Node*> firstPart = _rows.mid(0,row);
|
|
|
|
QList<Node*> secondPart = _rows.mid(row + count);
|
|
|
|
_rows.clear();
|
|
|
|
_rows.append(firstPart);
|
|
|
|
_rows.append(secondPart);
|
2023-09-18 00:12:39 +08:00
|
|
|
endRemoveRows();
|
2023-09-15 19:11:55 +08:00
|
|
|
}
|
|
|
|
|
2023-09-17 20:36:33 +08:00
|
|
|
void FluTreeModel::insertRows(int row,QList<Node*> data){
|
|
|
|
if (row < 0 || row > _rows.size() || data.size() == 0)
|
|
|
|
return;;
|
2023-09-18 00:12:39 +08:00
|
|
|
beginInsertRows(QModelIndex(), row, row + data.size() - 1);
|
2023-09-21 18:29:09 +08:00
|
|
|
QList<Node*> firstPart = _rows.mid(0, row);
|
|
|
|
QList<Node*> secondPart = _rows.mid(row);
|
|
|
|
_rows.clear();
|
|
|
|
_rows.append(firstPart);
|
|
|
|
_rows.append(data);
|
|
|
|
_rows.append(secondPart);
|
2023-09-18 00:12:39 +08:00
|
|
|
endInsertRows();
|
2023-09-15 19:11:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
QObject* FluTreeModel::getRow(int row){
|
|
|
|
return _rows.at(row);
|
|
|
|
}
|
2023-09-17 20:36:33 +08:00
|
|
|
|
2023-09-22 17:35:02 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-09-17 20:36:33 +08:00
|
|
|
void FluTreeModel::setDataSource(QList<QMap<QString,QVariant>> data){
|
|
|
|
_dataSource.clear();
|
|
|
|
if(_root){
|
|
|
|
delete _root;
|
|
|
|
_root = nullptr;
|
|
|
|
}
|
|
|
|
_root = new Node(this);
|
|
|
|
std::reverse(data.begin(), data.end());
|
|
|
|
while (data.count() > 0) {
|
|
|
|
auto item = data.at(data.count()-1);
|
|
|
|
data.pop_back();
|
|
|
|
Node* node = new Node(this);
|
|
|
|
node->_title = item.value("title").toString();
|
|
|
|
node->_key = item.value("key").toString();
|
|
|
|
node->_depth = item.value("__depth").toInt();
|
|
|
|
node->_parent = item.value("__parent").value<Node*>();
|
|
|
|
node->_isExpanded = true;
|
|
|
|
if(node->_parent){
|
|
|
|
node->_parent->_children.append(node);
|
|
|
|
}else{
|
|
|
|
node->_parent = _root;
|
|
|
|
_root->_children.append(node);
|
|
|
|
}
|
|
|
|
_dataSource.append(node);
|
|
|
|
if (item.contains("children")) {
|
|
|
|
QList<QVariant> children = item.value("children").toList();
|
|
|
|
if(!children.isEmpty()){
|
|
|
|
std::reverse(children.begin(), children.end());
|
2023-09-21 18:29:09 +08:00
|
|
|
for (int i = 0; i < children.count(); ++i) {
|
|
|
|
auto child = children.at(i).toMap();
|
2023-09-17 20:36:33 +08:00
|
|
|
child.insert("__depth",item.value("__depth").toInt(0)+1);
|
|
|
|
child.insert("__parent",QVariant::fromValue(node));
|
|
|
|
data.append(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
beginResetModel();
|
|
|
|
_rows = _dataSource;
|
|
|
|
endResetModel();
|
|
|
|
dataSourceSize(_dataSource.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void FluTreeModel::collapse(int row){
|
|
|
|
if(!_rows.at(row)->_isExpanded){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_rows.at(row)->_isExpanded = false;
|
|
|
|
Q_EMIT dataChanged(index(row,0),index(row,0));
|
|
|
|
auto modelData = _rows.at(row);
|
|
|
|
int removeCount = 0;
|
|
|
|
for(int i=row+1;i<_rows.count();i++){
|
|
|
|
auto obj = _rows[i];
|
|
|
|
if(obj->_depth<=modelData->_depth){
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
removeCount = removeCount + 1;
|
|
|
|
}
|
|
|
|
removeRows(row+1,removeCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FluTreeModel::expand(int row){
|
|
|
|
if(_rows.at(row)->_isExpanded){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_rows.at(row)->_isExpanded = true;
|
|
|
|
Q_EMIT dataChanged(index(row,0),index(row,0));
|
|
|
|
auto modelData = _rows.at(row);
|
|
|
|
QList<Node*> insertData;
|
|
|
|
QList<Node*> stack = modelData->_children;
|
|
|
|
std::reverse(stack.begin(), stack.end());
|
|
|
|
while (stack.count() > 0) {
|
|
|
|
auto item = stack.at(stack.count()-1);
|
|
|
|
stack.pop_back();
|
|
|
|
if(item->isShown()){
|
|
|
|
insertData.append(item);
|
|
|
|
}
|
|
|
|
QList<Node*> children = item->_children;
|
|
|
|
if(!children.isEmpty()){
|
|
|
|
std::reverse(children.begin(), children.end());
|
|
|
|
foreach (auto c, children) {
|
|
|
|
stack.append(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
insertRows(row+1,insertData);
|
|
|
|
}
|
|
|
|
|
2023-09-19 00:31:49 +08:00
|
|
|
void FluTreeModel::dragAnddrop(int dragIndex,int dropIndex,bool isDropTopArea){
|
|
|
|
if(dropIndex>_rows.count() || dropIndex<0){
|
2023-09-17 20:36:33 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto dragItem = _rows[dragIndex];
|
|
|
|
auto dropItem = _rows[dropIndex];
|
2023-09-21 18:29:09 +08:00
|
|
|
int targetIndex;
|
|
|
|
if(dropIndex > dragIndex){
|
|
|
|
if(isDropTopArea){
|
|
|
|
targetIndex = dropIndex;
|
|
|
|
}else{
|
|
|
|
targetIndex = dropIndex+1;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if(isDropTopArea){
|
|
|
|
targetIndex = dropIndex;
|
|
|
|
}else{
|
|
|
|
targetIndex = dropIndex+1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!beginMoveRows(QModelIndex(), dragIndex, dragIndex, QModelIndex(), targetIndex)) {
|
2023-09-17 20:36:33 +08:00
|
|
|
return;
|
|
|
|
}
|
2023-09-21 18:29:09 +08:00
|
|
|
if(dropIndex > dragIndex){
|
|
|
|
if(isDropTopArea){
|
|
|
|
targetIndex = dropIndex-1;
|
|
|
|
}else{
|
|
|
|
targetIndex = dropIndex;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if(isDropTopArea){
|
|
|
|
targetIndex = dropIndex;
|
|
|
|
}else{
|
|
|
|
targetIndex = dropIndex+1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_rows.move(dragIndex,targetIndex);
|
|
|
|
endMoveRows();
|
2023-09-22 17:35:02 +08:00
|
|
|
|
2023-09-21 18:29:09 +08:00
|
|
|
Q_EMIT layoutAboutToBeChanged();
|
2023-09-17 20:36:33 +08:00
|
|
|
if(dragItem->_parent == dropItem->_parent){
|
2023-09-18 18:10:27 +08:00
|
|
|
QList<Node*>* children = &(dragItem->_parent->_children);
|
|
|
|
int srcIndex = children->indexOf(dragItem);
|
|
|
|
int destIndex = children->indexOf(dropItem);
|
2023-09-21 18:29:09 +08:00
|
|
|
if(dropIndex > dragIndex){
|
|
|
|
if(isDropTopArea){
|
|
|
|
targetIndex = destIndex-1;
|
|
|
|
}else{
|
|
|
|
targetIndex = destIndex;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if(isDropTopArea){
|
|
|
|
targetIndex = destIndex;
|
|
|
|
}else{
|
|
|
|
targetIndex = destIndex+1;
|
|
|
|
}
|
2023-09-19 00:31:49 +08:00
|
|
|
}
|
2023-09-21 18:29:09 +08:00
|
|
|
children->move(srcIndex,targetIndex);
|
2023-09-17 20:36:33 +08:00
|
|
|
}else{
|
2023-09-18 18:10:27 +08:00
|
|
|
QList<Node*>* srcChildren = &(dragItem->_parent->_children);
|
|
|
|
QList<Node*>* destChildren = &(dropItem->_parent->_children);
|
|
|
|
int srcIndex = srcChildren->indexOf(dragItem);
|
|
|
|
int destIndex = destChildren->indexOf(dropItem);
|
|
|
|
dragItem->_depth = dropItem->_depth;
|
|
|
|
dragItem->_parent = dropItem->_parent;
|
2023-09-17 20:36:33 +08:00
|
|
|
if(dragItem->hasChildren()){
|
2023-09-18 18:10:27 +08:00
|
|
|
QList<Node*> stack = dragItem->_children;
|
|
|
|
foreach (auto node, stack) {
|
|
|
|
node->_depth = dragItem->_depth+1;
|
|
|
|
}
|
|
|
|
std::reverse(stack.begin(), stack.end());
|
|
|
|
while (stack.count() > 0) {
|
|
|
|
auto item = stack.at(stack.count()-1);
|
|
|
|
stack.pop_back();
|
|
|
|
QList<Node*> children = item->_children;
|
|
|
|
if(!children.isEmpty()){
|
|
|
|
std::reverse(children.begin(), children.end());
|
|
|
|
foreach (auto c, children) {
|
|
|
|
c->_depth = item->_depth+1;
|
|
|
|
stack.append(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-09-17 20:36:33 +08:00
|
|
|
}
|
2023-09-18 18:10:27 +08:00
|
|
|
srcChildren->removeAt(srcIndex);
|
2023-09-21 18:29:09 +08:00
|
|
|
if(dropIndex > dragIndex){
|
|
|
|
if(isDropTopArea){
|
|
|
|
targetIndex = destIndex;
|
|
|
|
}else{
|
|
|
|
targetIndex = destIndex + 1;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if(isDropTopArea){
|
|
|
|
targetIndex = destIndex;
|
|
|
|
}else{
|
|
|
|
targetIndex = destIndex + 1;
|
|
|
|
}
|
2023-09-19 18:31:29 +08:00
|
|
|
}
|
2023-09-21 18:29:09 +08:00
|
|
|
destChildren->insert(targetIndex,dragItem);
|
2023-09-17 20:36:33 +08:00
|
|
|
}
|
2023-09-21 18:29:09 +08:00
|
|
|
changePersistentIndex(index(qMin(dragIndex,dropIndex),0),index(qMax(dragIndex,dropIndex),0));
|
|
|
|
Q_EMIT layoutChanged(QList<QPersistentModelIndex>(),QAbstractItemModel::VerticalSortHint);
|
|
|
|
|
2023-09-17 20:36:33 +08:00
|
|
|
}
|
2023-09-18 18:10:27 +08:00
|
|
|
|
2023-09-19 00:31:49 +08:00
|
|
|
bool FluTreeModel::hitHasChildrenExpanded(int row){
|
2023-09-22 00:11:58 +08:00
|
|
|
auto itemData = _rows.at(row);
|
|
|
|
if(itemData->hasChildren() && itemData->_isExpanded){
|
|
|
|
return true;
|
|
|
|
}
|
2023-09-19 00:31:49 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-09-18 18:10:27 +08:00
|
|
|
void FluTreeModel::refreshNode(int row){
|
|
|
|
Q_EMIT dataChanged(index(row,0),index(row,0));
|
|
|
|
};
|
|
|
|
|
|
|
|
Node* FluTreeModel::getNode(int row){
|
|
|
|
return _rows.at(row);
|
|
|
|
}
|
2023-09-19 00:31:49 +08:00
|
|
|
|
|
|
|
void FluTreeModel::allExpand(){
|
|
|
|
beginResetModel();
|
|
|
|
QList<Node*> data;
|
|
|
|
QList<Node*> stack = _root->_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->_isExpanded = true;
|
|
|
|
}
|
|
|
|
data.append(item);
|
|
|
|
QList<Node*> children = item->_children;
|
|
|
|
if(!children.isEmpty()){
|
|
|
|
std::reverse(children.begin(), children.end());
|
|
|
|
foreach (auto c, children) {
|
|
|
|
stack.append(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_rows = data;
|
|
|
|
endResetModel();
|
|
|
|
}
|
|
|
|
void FluTreeModel::allCollapse(){
|
|
|
|
beginResetModel();
|
|
|
|
QList<Node*> stack = _root->_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->_isExpanded = false;
|
|
|
|
}
|
|
|
|
QList<Node*> children = item->_children;
|
|
|
|
if(!children.isEmpty()){
|
|
|
|
std::reverse(children.begin(), children.end());
|
|
|
|
foreach (auto c, children) {
|
|
|
|
stack.append(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_rows = _root->_children;
|
|
|
|
endResetModel();
|
|
|
|
}
|