This commit is contained in:
朱子楚\zhuzi 2023-09-19 00:31:49 +08:00
parent b27a88d261
commit 66ae37a023
7 changed files with 329 additions and 195 deletions

View File

@ -86,22 +86,32 @@ FluScrollablePage {
} }
FluSlider{ FluSlider{
id:slider_width id:slider_width
value: 200 value: 280
from: 160 from: 160
to:400 to:400
} }
} }
FluToggleSwitch{ FluToggleSwitch{
id:switch_showline id:switch_showline
text:"showLine" text:"showLine"
checked: true checked: true
} }
FluToggleSwitch{ FluToggleSwitch{
id:switch_draggable id:switch_draggable
text:"draggable" text:"draggable"
checked: false checked: true
}
FluButton{
text:"all expand"
onClicked: {
tree_view.allExpand()
}
}
FluButton{
text:"all collapse"
onClicked: {
tree_view.allCollapse()
}
} }
} }
} }
@ -120,3 +130,4 @@ FluScrollablePage {
' '
} }
} }

View File

@ -166,3 +166,7 @@ void FluTools::showFileInFolder(QString path){
bool FluTools::isSoftware(){ bool FluTools::isSoftware(){
return QQuickWindow::sceneGraphBackend() == "software"; return QQuickWindow::sceneGraphBackend() == "software";
} }
QPoint FluTools::cursorPos(){
return QCursor::pos();
}

View File

@ -47,6 +47,7 @@ public:
Q_INVOKABLE bool removeFile(QString filePath); Q_INVOKABLE bool removeFile(QString filePath);
Q_INVOKABLE void showFileInFolder(QString path); Q_INVOKABLE void showFileInFolder(QString path);
Q_INVOKABLE bool isSoftware(); Q_INVOKABLE bool isSoftware();
Q_INVOKABLE QPoint cursorPos();
}; };
#endif // FLUTOOLS_H #endif // FLUTOOLS_H

View File

@ -154,8 +154,8 @@ void FluTreeModel::expand(int row){
insertRows(row+1,insertData); insertRows(row+1,insertData);
} }
void FluTreeModel::dragAnddrop(int dragIndex,int dropIndex){ void FluTreeModel::dragAnddrop(int dragIndex,int dropIndex,bool isDropTopArea){
if(dragIndex == dropIndex+1 || dropIndex>_rows.count() || dropIndex<0){ if(dropIndex>_rows.count() || dropIndex<0){
return; return;
} }
auto dragItem = _rows[dragIndex]; auto dragItem = _rows[dragIndex];
@ -167,8 +167,12 @@ void FluTreeModel::dragAnddrop(int dragIndex,int dropIndex){
QList<Node*>* children = &(dragItem->_parent->_children); QList<Node*>* children = &(dragItem->_parent->_children);
int srcIndex = children->indexOf(dragItem); int srcIndex = children->indexOf(dragItem);
int destIndex = children->indexOf(dropItem); int destIndex = children->indexOf(dropItem);
children->move(srcIndex,destIndex>srcIndex? destIndex : destIndex +1); int offset = 1;
_rows.move(dragIndex,dropIndex>dragIndex? dropIndex : dropIndex+1); if(isDropTopArea){
offset = offset - 1;
}
children->move(srcIndex,destIndex>srcIndex? destIndex-1 + offset : destIndex + offset);
_rows.move(dragIndex,dropIndex>dragIndex? dropIndex-1 + offset : dropIndex + offset);
}else{ }else{
QList<Node*>* srcChildren = &(dragItem->_parent->_children); QList<Node*>* srcChildren = &(dragItem->_parent->_children);
QList<Node*>* destChildren = &(dropItem->_parent->_children); QList<Node*>* destChildren = &(dropItem->_parent->_children);
@ -197,11 +201,23 @@ void FluTreeModel::dragAnddrop(int dragIndex,int dropIndex){
} }
srcChildren->removeAt(srcIndex); srcChildren->removeAt(srcIndex);
destChildren->insert(destIndex+1,dragItem); destChildren->insert(destIndex+1,dragItem);
_rows.move(dragIndex,dropIndex>dragIndex? dropIndex : dropIndex+1); int offset = 1;
if(isDropTopArea){
offset = offset - 1;
}
_rows.move(dragIndex,dropIndex>dragIndex? dropIndex-1+offset : dropIndex+offset);
} }
endMoveRows(); endMoveRows();
} }
bool FluTreeModel::hitHasChildrenExpanded(int row){
auto itemData = _rows.at(row);
if(itemData->hasChildren() && itemData->_isExpanded){
return true;
}
return false;
}
void FluTreeModel::refreshNode(int row){ void FluTreeModel::refreshNode(int row){
Q_EMIT dataChanged(index(row,0),index(row,0)); Q_EMIT dataChanged(index(row,0),index(row,0));
}; };
@ -209,3 +225,48 @@ void FluTreeModel::refreshNode(int row){
Node* FluTreeModel::getNode(int row){ Node* FluTreeModel::getNode(int row){
return _rows.at(row); return _rows.at(row);
} }
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();
}

View File

@ -20,16 +20,6 @@ public:
Q_INVOKABLE int depth(){return _depth;}; Q_INVOKABLE int depth(){return _depth;};
Q_INVOKABLE bool isExpanded(){return _isExpanded;}; Q_INVOKABLE bool isExpanded(){return _isExpanded;};
Q_INVOKABLE bool hasChildren(){ return !_children.isEmpty();}; Q_INVOKABLE bool hasChildren(){ return !_children.isEmpty();};
Q_INVOKABLE bool isHeaderNode(){
if(hasChildren() && _isExpanded){
return true;
}
return false;
}
Q_INVOKABLE bool isFooterNode(){
return this->_parent->_children.indexOf(this) == this->_parent->_children.count()-1;
}
Q_INVOKABLE bool hasNextNodeByIndex(int index){ Q_INVOKABLE bool hasNextNodeByIndex(int index){
Node* p = this; Node* p = this;
for(int i=0;i<(_depth - index -1);i++){ for(int i=0;i<(_depth - index -1);i++){
@ -91,9 +81,12 @@ public:
Q_INVOKABLE void setDataSource(QList<QMap<QString,QVariant>> data); Q_INVOKABLE void setDataSource(QList<QMap<QString,QVariant>> data);
Q_INVOKABLE void collapse(int row); Q_INVOKABLE void collapse(int row);
Q_INVOKABLE void expand(int row); Q_INVOKABLE void expand(int row);
Q_INVOKABLE void dragAnddrop(int dragIndex,int dropIndex); Q_INVOKABLE void dragAnddrop(int dragIndex,int dropIndex,bool isDropTopArea);
Q_INVOKABLE Node* getNode(int row); Q_INVOKABLE Node* getNode(int row);
Q_INVOKABLE void refreshNode(int row); Q_INVOKABLE void refreshNode(int row);
Q_INVOKABLE bool hitHasChildrenExpanded(int row);
Q_INVOKABLE void allExpand();
Q_INVOKABLE void allCollapse();
private: private:
QList<Node*> _rows; QList<Node*> _rows;
QList<Node*> _dataSource; QList<Node*> _dataSource;

View File

@ -16,6 +16,7 @@ Item {
id:d id:d
property var current property var current
property int dropIndex: -1 property int dropIndex: -1
property bool isDropTopArea: false
property int dragIndex: -1 property int dragIndex: -1
property color hitColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark property color hitColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
@ -40,10 +41,8 @@ Item {
signal pooled signal pooled
onReused: { onReused: {
console.debug("----->onReused")
} }
onPooled: { onPooled: {
console.debug("----->onPooled")
} }
property bool isCurrent: d.current === itemModel property bool isCurrent: d.current === itemModel
@ -92,18 +91,18 @@ Item {
loader_container.sourceComponent = com_item_container loader_container.sourceComponent = com_item_container
} }
} }
onPressed: (mouse)=>{ onPressed:
clickPos = Qt.point(mouse.x,mouse.y) (mouse)=>{
console.debug(clickPos) clickPos = Qt.point(mouse.x,mouse.y)
loader_container.itemControl = itemControl console.debug(clickPos)
loader_container.itemModel = itemModel loader_container.itemControl = itemControl
var cellPosition = item_container.mapToItem(table_view, 0, 0) loader_container.itemModel = itemModel
loader_container.width = item_container.width var cellPosition = item_container.mapToItem(table_view, 0, 0)
loader_container.height = item_container.height loader_container.width = item_container.width
loader_container.x = table_view.contentX + cellPosition.x loader_container.height = item_container.height
loader_container.y = table_view.contentY + cellPosition.y loader_container.x = table_view.contentX + cellPosition.x
loader_container.y = table_view.contentY + cellPosition.y
} }
onClicked: { onClicked: {
d.current = itemModel d.current = itemModel
} }
@ -126,13 +125,20 @@ Item {
d.dropIndex = -1 d.dropIndex = -1
return return
} }
var y = loader_container.y var pos = FluTools.cursorPos()
var index = Math.round(y/30) var viewPos = table_view.mapToGlobal(0,0)
if(index !== d.dragIndex){ var y = table_view.contentY + pos.y-viewPos.y
d.dropIndex = index var index = Math.floor(y/30)
tree_model.refreshNode(rowIndex) if(tree_model.hitHasChildrenExpanded(index) && y>index*30+15){
d.dropIndex = index + 1
d.isDropTopArea = true
}else{ }else{
d.dropIndex = -1 d.dropIndex = index
if(y>index*30+15){
d.isDropTopArea = false
}else{
d.isDropTopArea = true
}
} }
} }
onCanceled: { onCanceled: {
@ -143,7 +149,7 @@ Item {
onReleased: { onReleased: {
loader_container.sourceComponent = undefined loader_container.sourceComponent = undefined
if(d.dropIndex !== -1){ if(d.dropIndex !== -1){
tree_model.dragAnddrop(d.dragIndex,d.dropIndex) tree_model.dragAnddrop(d.dragIndex,d.dropIndex,d.isDropTopArea)
} }
d.dropIndex = -1 d.dropIndex = -1
d.dragIndex = -1 d.dragIndex = -1
@ -151,6 +157,7 @@ Item {
} }
Drag.active: item_mouse.drag.active Drag.active: item_mouse.drag.active
Rectangle{ Rectangle{
id:item_line_drop_tip
anchors{ anchors{
left: parent.left left: parent.left
leftMargin: { leftMargin: {
@ -163,7 +170,23 @@ Item {
right: parent.right right: parent.right
rightMargin: 10 rightMargin: 10
bottom: parent.bottom bottom: parent.bottom
bottomMargin: -1.5
top: undefined
} }
states: [
State {
when:d.isDropTopArea
AnchorChanges {
target: item_line_drop_tip
anchors.top: item_container.top
anchors.bottom: undefined
}
PropertyChanges {
target: item_line_drop_tip
anchors.topMargin: -1.5
}
}
]
height: 3 height: 3
radius: 1.5 radius: 1.5
color: d.hitColor color: d.hitColor
@ -324,7 +347,6 @@ Item {
} }
} }
} }
Loader{ Loader{
id:loader_container id:loader_container
property var itemControl property var itemControl

View File

@ -16,6 +16,7 @@ Item {
id:d id:d
property var current property var current
property int dropIndex: -1 property int dropIndex: -1
property bool isDropTopArea: false
property int dragIndex: -1 property int dragIndex: -1
property color hitColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark property color hitColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
@ -40,10 +41,8 @@ Item {
signal pooled signal pooled
onReused: { onReused: {
console.debug("----->onReused")
} }
onPooled: { onPooled: {
console.debug("----->onPooled")
} }
property bool isCurrent: d.current === itemModel property bool isCurrent: d.current === itemModel
@ -65,6 +64,185 @@ Item {
tree_model.expand(rowIndex) tree_model.expand(rowIndex)
} }
} }
Rectangle{
width: 3
height: 18
radius: 1.5
color: FluTheme.primaryColor.dark
visible: isCurrent
anchors{
left: parent.left
leftMargin: 6
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_mouse
property point clickPos: Qt.point(0,0)
anchors.fill: parent
drag.target:control.draggable ? loader_container : undefined
hoverEnabled: true
drag.onActiveChanged: {
if(drag.active){
if(itemModel.isExpanded && itemModel.hasChildren()){
tree_model.collapse(rowIndex)
}
d.dragIndex = rowIndex
loader_container.sourceComponent = com_item_container
}
}
onPressed:
(mouse)=>{
clickPos = Qt.point(mouse.x,mouse.y)
console.debug(clickPos)
loader_container.itemControl = itemControl
loader_container.itemModel = itemModel
var cellPosition = item_container.mapToItem(table_view, 0, 0)
loader_container.width = item_container.width
loader_container.height = item_container.height
loader_container.x = table_view.contentX + cellPosition.x
loader_container.y = table_view.contentY + cellPosition.y
}
onClicked: {
d.current = itemModel
}
onDoubleClicked: {
if(itemModel.hasChildren()){
item_container.toggle()
}
}
onPositionChanged:
(mouse)=> {
if(!drag.active){
return
}
var cellPosition = item_container.mapToItem(table_view, 0, 0)
if(mouse.y+cellPosition.y<0 || mouse.y+cellPosition.y>table_view.height){
d.dropIndex = -1
return
}
if((mouse.x-table_view.contentX)>table_view.width || (mouse.x-table_view.contentX)<0){
d.dropIndex = -1
return
}
var pos = FluTools.cursorPos()
var viewPos = table_view.mapToGlobal(0,0)
var y = table_view.contentY + pos.y-viewPos.y
var index = Math.floor(y/30)
if(tree_model.hitHasChildrenExpanded(index) && y>index*30+15){
d.dropIndex = index + 1
d.isDropTopArea = true
}else{
d.dropIndex = index
if(y>index*30+15){
d.isDropTopArea = false
}else{
d.isDropTopArea = true
}
}
}
onCanceled: {
loader_container.sourceComponent = undefined
d.dropIndex = -1
d.dragIndex = -1
}
onReleased: {
loader_container.sourceComponent = undefined
if(d.dropIndex !== -1){
tree_model.dragAnddrop(d.dragIndex,d.dropIndex,d.isDropTopArea)
}
d.dropIndex = -1
d.dragIndex = -1
}
}
Drag.active: item_mouse.drag.active
Rectangle{
id:item_line_drop_tip
anchors{
left: parent.left
leftMargin: {
var count = itemModel.depth+1
if(itemModel.hasChildren()){
return 30*count - 8
}
return 30*count + 18
}
right: parent.right
rightMargin: 10
bottom: parent.bottom
bottomMargin: -1.5
top: undefined
}
states: [
State {
when:d.isDropTopArea
AnchorChanges {
target: item_line_drop_tip
anchors.top: item_container.top
anchors.bottom: undefined
}
PropertyChanges {
target: item_line_drop_tip
anchors.topMargin: -1.5
}
}
]
height: 3
radius: 1.5
color: d.hitColor
visible: d.dropIndex === rowIndex
Rectangle{
width: 10
height: 10
radius: 5
border.width: 3
border.color: d.hitColor
color: FluTheme.dark ? FluColors.Black : FluColors.White
anchors{
top: parent.top
left: parent.left
topMargin: -3
leftMargin: -5
}
}
}
FluRectangle{
width: 1
color: control.lineColor
visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren()
height: itemModel.hideLineFooter() ? parent.height/2 : parent.height
anchors{
top: parent.top
right: layout_row.left
rightMargin: -9
}
}
FluRectangle{
height: 1
color: control.lineColor
visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren()
width: 18
anchors{
right: layout_row.left
rightMargin: -27
verticalCenter: parent.verticalCenter
}
}
Repeater{
model: Math.max(itemModel.depth-1,0)
delegate: FluRectangle{
required property int index
width: 1
color: control.lineColor
visible: control.showLine && isItemLoader && itemModel.depth !== 0 && itemModel.hasNextNodeByIndex(index)
anchors{
top:parent.top
bottom: parent.bottom
left: parent.left
leftMargin: 30*(index+2) - 8
}
}
}
Rectangle{ Rectangle{
anchors.fill: parent anchors.fill: parent
radius: 4 radius: 4
@ -92,156 +270,6 @@ Item {
} }
} }
} }
Rectangle{
width: 3
height: 18
radius: 1.5
color: FluTheme.primaryColor.dark
visible: isCurrent
anchors{
left: parent.left
leftMargin: 6
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_mouse
anchors.fill: parent
drag.target:control.draggable ? loader_container : undefined
hoverEnabled: true
drag.onActiveChanged: {
if(drag.active){
if(itemModel.isExpanded && itemModel.hasChildren()){
tree_model.collapse(rowIndex)
}
d.dragIndex = rowIndex
loader_container.sourceComponent = com_item_container
}
}
onPressed: {
loader_container.itemControl = itemControl
loader_container.itemModel = itemModel
var cellPosition = item_container.mapToItem(table_view, 0, 0)
loader_container.width = item_container.width
loader_container.height = item_container.height
loader_container.x = table_view.contentX + cellPosition.x
loader_container.y = table_view.contentY + cellPosition.y
}
onClicked: {
d.current = itemModel
}
onDoubleClicked: {
if(itemModel.hasChildren()){
item_container.toggle()
}
}
onPositionChanged:
(mouse)=> {
if(!drag.active){
return
}
var cellPosition = item_container.mapToItem(table_view, 0, 0)
if(mouse.y+cellPosition.y<0 || mouse.y+cellPosition.y>table_view.height){
d.dropIndex = -1
return
}
if((mouse.x-table_view.contentX)>table_view.width || (mouse.x-table_view.contentX)<0){
d.dropIndex = -1
return
}
var y = loader_container.y
var index = Math.round(y/30)
if(index !== d.dragIndex){
d.dropIndex = index
}else{
d.dropIndex = -1
}
}
onCanceled: {
loader_container.sourceComponent = undefined
d.dropIndex = -1
d.dragIndex = -1
}
onReleased: {
loader_container.sourceComponent = undefined
if(d.dropIndex !== -1){
tree_model.dragAnddrop(d.dragIndex,d.dropIndex)
}
d.dropIndex = -1
d.dragIndex = -1
}
}
Drag.active: item_mouse.drag.active
Rectangle{
anchors{
left: parent.left
leftMargin: {
if(itemModel.hasChildren()){
return 30*(itemModel.depth+1) - 8
}
return 30*(itemModel.depth+1) + 18
}
right: parent.right
rightMargin: 10
bottom: parent.bottom
}
height: 3
radius: 1.5
color: d.hitColor
visible: d.dropIndex === rowIndex
Rectangle{
width: 10
height: 10
radius: 5
border.width: 3
border.color: d.hitColor
color: FluTheme.dark ? FluColors.Black : FluColors.White
anchors{
top: parent.top
left: parent.left
topMargin: -3
leftMargin: -5
}
}
}
FluRectangle{
width: 1
color: control.lineColor
visible: itemModel.depth !== 0 && control.showLine && !itemModel.hasChildren()
height: itemModel.hideLineFooter() ? parent.height/2 : parent.height
anchors{
top: parent.top
right: layout_row.left
rightMargin: -9
}
}
FluRectangle{
height: 1
color: control.lineColor
visible: itemModel.depth !== 0 && control.showLine && !itemModel.hasChildren()
width: 18
anchors{
right: layout_row.left
rightMargin: -27
verticalCenter: parent.verticalCenter
}
}
Repeater{
model: Math.max(itemModel.depth-1,0)
delegate: FluRectangle{
required property int index
width: 1
color: control.lineColor
visible: itemModel.depth !== 0 && control.showLine
anchors{
top:parent.top
bottom: parent.bottom
left: parent.left
leftMargin: 30*(index+2) - 8
}
}
}
RowLayout{ RowLayout{
id:layout_row id:layout_row
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -313,16 +341,17 @@ Item {
property var itemControl: item_control property var itemControl: item_control
property var itemModel: modelData property var itemModel: modelData
property int rowIndex: row property int rowIndex: row
property bool isItemLoader: true
id:item_loader_container id:item_loader_container
sourceComponent: com_item_container sourceComponent: com_item_container
} }
} }
} }
Loader{ Loader{
id:loader_container id:loader_container
property var itemControl property var itemControl
property var itemModel property var itemModel
property bool isItemLoader: false
} }
} }
function count(){ function count(){
@ -331,4 +360,17 @@ Item {
function visibleCount(){ function visibleCount(){
return table_view.rows return table_view.rows
} }
function collapse(rowIndex){
tree_model.collapse(rowIndex)
}
function expand(rowIndex){
tree_model.expand(rowIndex)
}
function allExpand(){
tree_model.allExpand()
}
function allCollapse(){
tree_model.allCollapse()
}
} }