This commit is contained in:
朱子楚\zhuzi 2023-09-15 01:28:03 +08:00
parent c47fa5ebc7
commit e6d9de34ea
15 changed files with 295 additions and 890 deletions

View File

@ -28,7 +28,6 @@ Window {
FluApp.init(app) FluApp.init(app)
FluTheme.darkMode = FluThemeType.System FluTheme.darkMode = FluThemeType.System
FluTheme.enableAnimation = true FluTheme.enableAnimation = true
FluTheme.nativeText = true
FluApp.routes = { FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml", "/":"qrc:/example/qml/window/MainWindow.qml",
"/about":"qrc:/example/qml/window/AboutWindow.qml", "/about":"qrc:/example/qml/window/AboutWindow.qml",

View File

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

View File

@ -9,49 +9,10 @@ FluScrollablePage {
title:"TreeView" title:"TreeView"
function randomName() {
var names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十"]
return names[Math.floor(Math.random() * names.length)]
}
function randomCompany() {
var companies = ["阿里巴巴", "腾讯", "百度", "京东", "华为", "小米", "字节跳动", "美团", "滴滴"]
return companies[Math.floor(Math.random() * companies.length)]
}
function randomDepartment() {
var departments = ["技术部", "销售部", "市场部", "人事部", "财务部", "客服部", "产品部", "设计部", "运营部"]
return departments[Math.floor(Math.random() * departments.length)]
}
function createEmployee() {
var name = randomName()
return tree_view.createItem(name, false)
}
function createSubtree(numEmployees) {
var employees = []
for (var i = 0; i < numEmployees; i++) {
employees.push(createEmployee())
}
return tree_view.createItem(randomDepartment(), true, employees)
}
function createOrg(numLevels, numSubtrees, numEmployees) {
if (numLevels === 0) {
return []
}
var subtrees = []
for (var i = 0; i < numSubtrees; i++) {
subtrees.push(createSubtree(numEmployees))
}
return [tree_view.createItem(randomCompany(), true, subtrees)].concat(createOrg(numLevels - 1, numSubtrees, numEmployees))
}
function treeData(){ function treeData(){
const dig = (path = '0', level = 4) => { const dig = (path = '0', level = 4) => {
const list = []; const list = [];
for (let i = 0; i < 10; i += 1) { for (let i = 0; i < 6; i += 1) {
const key = `${path}-${i}`; const key = `${path}-${i}`;
const treeNode = { const treeNode = {
title: key, title: key,
@ -68,65 +29,16 @@ FluScrollablePage {
} }
FluArea{ FluArea{
id:layout_actions
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 10
height: 50
paddings: 10 paddings: 10
RowLayout{ height: 60
spacing: 14 FluText{
FluDropDownButton{ text:"共计:%1条数据".arg(tree_view.count())
id:btn_selection_model anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: 140
text:"None"
FluMenuItem{
text:"None"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.Equal
}
}
FluMenuItem{
text:"Single"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.SizeToContent
}
}
FluMenuItem{
text:"Muiltple"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.Compact
}
}
}
FluFilledButton{
text:"获取选中的数据"
onClicked: {
if(tree_view.selectionMode === FluTreeViewType.None){
showError("当前非选择模式,没有选中的数据")
}
if(tree_view.selectionMode === FluTreeViewType.Single){
if(!tree_view.signleData()){
showError("没有选中数据")
return
}
showSuccess(tree_view.signleData().text)
}
if(tree_view.selectionMode === FluTreeViewType.Multiple){
if(tree_view.multipData().length===0){
showError("没有选中数据")
return
}
var info = []
tree_view.multipData().map((value)=>info.push(value.text))
showSuccess(info.join(","))
}
}
}
} }
} }
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 10 Layout.topMargin: 10
@ -140,25 +52,6 @@ FluScrollablePage {
left:parent.left left:parent.left
bottom:parent.bottom bottom:parent.bottom
} }
onItemClicked:
(model)=>{
showSuccess(model.text)
}
Component.onCompleted: {
var org = createOrg(3, 3, 3)
createItem()
updateData(org)
}
}
FluTreeView2{
id:tree_view_2
width:240
anchors{
top:parent.top
bottom:parent.bottom
}
x:260
Component.onCompleted: { Component.onCompleted: {
var data = treeData() var data = treeData()
dataSource = data dataSource = data
@ -174,15 +67,10 @@ FluScrollablePage {
width:240 width:240
height:600 height:600
Component.onCompleted: { Component.onCompleted: {
var datas = [] var data = treeData()
datas.push(createItem("Node1",false)) dataSource = data
datas.push(createItem("Node2",false))
datas.push(createItem("Node2",true,[createItem("Node2-1",false),createItem("Node2-2",false)]))
updateData(datas)
} }
} }
' '
} }
} }

View File

@ -28,7 +28,6 @@ Window {
FluApp.init(app) FluApp.init(app)
FluTheme.darkMode = FluThemeType.System FluTheme.darkMode = FluThemeType.System
FluTheme.enableAnimation = true FluTheme.enableAnimation = true
FluTheme.nativeText = true
FluApp.routes = { FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml", "/":"qrc:/example/qml/window/MainWindow.qml",
"/about":"qrc:/example/qml/window/AboutWindow.qml", "/about":"qrc:/example/qml/window/AboutWindow.qml",

View File

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

View File

@ -10,105 +10,36 @@ FluScrollablePage {
title:"TreeView" title:"TreeView"
function randomName() { function treeData(){
var names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十"] const dig = (path = '0', level = 4) => {
return names[Math.floor(Math.random() * names.length)] const list = [];
} for (let i = 0; i < 6; i += 1) {
const key = `${path}-${i}`;
function randomCompany() { const treeNode = {
var companies = ["阿里巴巴", "腾讯", "百度", "京东", "华为", "小米", "字节跳动", "美团", "滴滴"] title: key,
return companies[Math.floor(Math.random() * companies.length)] key,
} };
if (level > 0) {
function randomDepartment() { treeNode.children = dig(key, level - 1);
var departments = ["技术部", "销售部", "市场部", "人事部", "财务部", "客服部", "产品部", "设计部", "运营部"] }
return departments[Math.floor(Math.random() * departments.length)] list.push(treeNode);
} }
return list;
function createEmployee() { };
var name = randomName() return dig();
return tree_view.createItem(name, false)
}
function createSubtree(numEmployees) {
var employees = []
for (var i = 0; i < numEmployees; i++) {
employees.push(createEmployee())
}
return tree_view.createItem(randomDepartment(), true, employees)
}
function createOrg(numLevels, numSubtrees, numEmployees) {
if (numLevels === 0) {
return []
}
var subtrees = []
for (var i = 0; i < numSubtrees; i++) {
subtrees.push(createSubtree(numEmployees))
}
return [tree_view.createItem(randomCompany(), true, subtrees)].concat(createOrg(numLevels - 1, numSubtrees, numEmployees))
} }
FluArea{ FluArea{
id:layout_actions
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 10
height: 50
paddings: 10 paddings: 10
RowLayout{ height: 60
spacing: 14 FluText{
FluDropDownButton{ text:"共计:%1条数据".arg(tree_view.count())
id:btn_selection_model anchors.verticalCenter: parent.verticalCenter
Layout.preferredWidth: 140
text:"None"
FluMenuItem{
text:"None"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.Equal
}
}
FluMenuItem{
text:"Single"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.SizeToContent
}
}
FluMenuItem{
text:"Muiltple"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.Compact
}
}
}
FluFilledButton{
text:"获取选中的数据"
onClicked: {
if(tree_view.selectionMode === FluTreeViewType.None){
showError("当前非选择模式,没有选中的数据")
}
if(tree_view.selectionMode === FluTreeViewType.Single){
if(!tree_view.signleData()){
showError("没有选中数据")
return
}
showSuccess(tree_view.signleData().text)
}
if(tree_view.selectionMode === FluTreeViewType.Multiple){
if(tree_view.multipData().length===0){
showError("没有选中数据")
return
}
var info = []
tree_view.multipData().map((value)=>info.push(value.text))
showSuccess(info.join(","))
}
}
}
} }
} }
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 10 Layout.topMargin: 10
@ -122,15 +53,9 @@ FluScrollablePage {
left:parent.left left:parent.left
bottom:parent.bottom bottom:parent.bottom
} }
onItemClicked:
(model)=>{
showSuccess(model.text)
}
Component.onCompleted: { Component.onCompleted: {
var org = createOrg(3, 3, 3) var data = treeData()
createItem() dataSource = data
updateData(org)
} }
} }
} }
@ -143,15 +68,10 @@ FluScrollablePage {
width:240 width:240
height:600 height:600
Component.onCompleted: { Component.onCompleted: {
var datas = [] var data = treeData()
datas.push(createItem("Node1",false)) dataSource = data
datas.push(createItem("Node2",false))
datas.push(createItem("Node2",true,[createItem("Node2-1",false),createItem("Node2-2",false)]))
updateData(datas)
} }
} }
' '
} }
} }

View File

@ -148,6 +148,7 @@ void FluentUI::registerTypes(const char *uri){
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluStaggeredView.qml"),uri,major,minor,"FluStaggeredView"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluStaggeredView.qml"),uri,major,minor,"FluStaggeredView");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluProgressButton.qml"),uri,major,minor,"FluProgressButton"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluProgressButton.qml"),uri,major,minor,"FluProgressButton");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluLoadingButton.qml"),uri,major,minor,"FluLoadingButton"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluLoadingButton.qml"),uri,major,minor,"FluLoadingButton");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTreeItem.qml"),uri,major,minor,"FluTreeItem");
qmlRegisterUncreatableMetaObject(Fluent_Awesome::staticMetaObject, uri,major,minor,"FluentIcons", "Access to enums & flags only"); qmlRegisterUncreatableMetaObject(Fluent_Awesome::staticMetaObject, uri,major,minor,"FluentIcons", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluHttpType::staticMetaObject, uri,major,minor,"FluHttpType", "Access to enums & flags only"); qmlRegisterUncreatableMetaObject(FluHttpType::staticMetaObject, uri,major,minor,"FluHttpType", "Access to enums & flags only");

View File

@ -0,0 +1,21 @@
import QtQuick 2.15
QtObject {
property string key
property string title
property var children: []
property int depth: 0
property bool isExpanded: true
property var __parent
property bool __expanded:{
var p = __parent;
while (p) {
if(!p.isExpanded){
return false
}
p = p.__parent;
}
return true
}
property int index: 0
}

View File

@ -2,291 +2,143 @@ import QtQuick 2.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import Qt.labs.qmlmodels 1.0
import FluentUI 1.0 import FluentUI 1.0
Item { Item {
property int selectionMode: FluTreeViewType.None property var dataSource
property var currentElement id:control
property var currentParentElement QtObject {
property var rootModel: tree_model.get(0).items id:d
signal itemClicked(var item) signal refreshLayout()
id:root onRefreshLayout: {
ListModel{ table_view.forceLayout()
id:tree_model
ListElement{
text: "根节点"
expanded:true
items:[]
key:"123456"
multipSelected:false
multipIndex:0
multipParentKey:""
} }
} function handleTree(treeData) {
Component{ var comItem = Qt.createComponent("FluTreeItem.qml");
id: delegate_root if (comItem.status !== Component.Ready) {
Column{ return
width: calculateWidth()
property var itemModel: model
Repeater{
id: repeater_first_level
model: items
delegate: delegate_items
} }
function calculateWidth(){ var stack = []
var w = 0; var rawData = []
for(var i = 0; i < repeater_first_level.count; i++) { for (var item of treeData) {
var child = repeater_first_level.itemAt(i) stack.push({node:item,depth:0,isExpanded:true,__parent:undefined})
if(w < child.width_hint){ }
w = child.width_hint; stack = stack.reverse()
var index =0
while (stack.length > 0) {
const { node, depth,isExpanded,__parent} = stack.pop();
node.depth = depth;
node.isExpanded = isExpanded;
node.__parent = __parent;
var objItem = comItem.createObject(table_view);
objItem.title = node.title
objItem.key = node.key
objItem.depth = node.depth
objItem.isExpanded = node.isExpanded
objItem.__parent = node.__parent
objItem.children = node.children
objItem.index = index
index = index + 1;
rawData.push({display:objItem})
if (node.children && node.children.length > 0) {
const children = node.children.reverse();
for (const child of children) {
stack.push({ node: child, depth: depth + 1,isExpanded:true,__parent:objItem});
} }
} }
return w;
} }
return rawData
} }
} }
Component{ onDataSourceChanged: {
id:delegate_items table_model.clear()
Column{ var data = d.handleTree(dataSource)
id:item_layout table_model.rows = data
property real level: (mapToItem(list_root,0,0).x+list_root.contentX)/0.001 table_view.forceLayout()
property var text: model.text??"Item" console.debug("共计:%1条数据".arg(table_model.rowCount))
property bool hasChild : (model.items !== undefined) && (model.items.count !== 0) }
property var items: model.items??[] TableModel {
property var expanded: model.expanded??true id:table_model
property int width_hint: calculateWidth() TableModelColumn { display: "display" }
property bool singleSelected: currentElement === model }
property var itemModel: model TableView{
function calculateWidth(){ id:table_view
var w = Math.max(list_root.width, item_layout_row.implicitWidth + 10); ScrollBar.horizontal: FluScrollBar{}
if(expanded){ ScrollBar.vertical: FluScrollBar{}
for(var i = 0; i < repeater_items.count; i++) { boundsBehavior: Flickable.StopAtBounds
var child = repeater_items.itemAt(i) model: table_model
if(w < child.width_hint){ clip: true
w = child.width_hint; anchors.fill: parent
} rowHeightProvider: function(row) {
} if(table_model.getRow(row).display.__expanded){
} return 38
return w;
} }
Item{ return 0
id:item_layout_rect }
width: list_root.contentWidth delegate: Item {
height: item_layout_row.implicitHeight implicitWidth: 46 + item_layout_text.width + 30*display.depth
Rectangle{ RowLayout{
anchors.fill: parent anchors.verticalCenter: parent.verticalCenter
anchors.margins: 2 anchors.left: parent.left
color:{ anchors.leftMargin: 14 + 30*display.depth
if(FluTheme.dark){ FluIcon{
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){ rotation: display.isExpanded?0:-90
return Qt.rgba(62/255,62/255,62/255,1) iconSource:FluentIcons.ChevronDown
} iconSize: 15
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(62/255,62/255,62/255,1):Qt.rgba(0,0,0,0) Layout.alignment: Qt.AlignVCenter
}else{ opacity: {
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){ if(display.children){
return Qt.rgba(0,0,0,0.06) return true
}
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(0,0,0,0.03):Qt.rgba(0,0,0,0)
}
}
Rectangle{
width: 3
color:FluTheme.primaryColor.dark
visible: item_layout.singleSelected && (selectionMode === FluTreeViewType.Single)
radius: 3
height: 20
anchors{
left: parent.left
verticalCenter: parent.verticalCenter
} }
return false
} }
MouseArea{ MouseArea{
id:item_layout_mouse
anchors.fill: parent anchors.fill: parent
hoverEnabled: true
onClicked: { onClicked: {
item_layout_rect.onClickItem() display.isExpanded = !display.isExpanded
d.refreshLayout()
} }
} }
} }
function onClickItem(){ Rectangle{
if(selectionMode === FluTreeViewType.None){ id:item_layout_text
itemClicked(model) radius: 4
Layout.preferredWidth: item_text.implicitWidth+14
Layout.preferredHeight:item_text.implicitHeight+14
Layout.alignment: Qt.AlignVCenter
HoverHandler{
id:item_hover_text
} }
if(selectionMode === FluTreeViewType.Single){ color: {
currentElement = model if(FluTheme.dark){
if(item_layout.parent.parent.parent.itemModel){ if(item_hover_text.hovered){
currentParentElement = item_layout.parent.parent.parent.itemModel return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
}else{ }else{
if(item_layout.parent.itemModel){ if(item_hover_text.hovered){
currentParentElement = item_layout.parent.itemModel return Qt.rgba(0,0,0,0.03)
}
}
itemClicked(model)
}
if(selectionMode === FluTreeViewType.Multiple){
}
}
RowLayout{
id:item_layout_row
anchors.verticalCenter: item_layout_rect.verticalCenter
Item{
width: 15*level
Layout.alignment: Qt.AlignVCenter
}
FluCheckBox{
id:item_layout_checkbox
text:""
checked: itemModel.multipSelected
visible: selectionMode === FluTreeViewType.Multiple
Layout.leftMargin: 5
function refreshCheckBox(){
const stack = [tree_model.get(0)];
const result = [];
while (stack.length > 0) {
const curr = stack.pop();
result.unshift(curr);
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipIndex",i)
curr.items.setProperty(i,"multipParentKey",curr.key)
stack.push(curr.items.get(i));
}
}
}
for(var j=0 ; j<result.length-1 ; j++){
var item = result[j]
let obj = result.find(function(o) {
return o.key === item.multipParentKey;
});
if((item.items !== undefined) && (item.items.count !== 0)){
var items = item.items
for(var k=0 ; k<items.count ; k++){
if(items.get(k).multipSelected === false){
obj.items.setProperty(item.multipIndex,"multipSelected",false)
break
}
obj.items.setProperty(item.multipIndex,"multipSelected",true)
}
}
}
}
clickListener:function(){
if(hasChild){
const stack = [itemModel];
while (stack.length > 0) {
const curr = stack.pop();
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipSelected",!itemModel.multipSelected)
stack.push(curr.items.get(i));
}
}
}
refreshCheckBox()
}else{
itemModel.multipSelected = !itemModel.multipSelected
refreshCheckBox()
}
}
}
FluIconButton{
id:item_layout_expanded
color:"#00000000"
opacity: item_layout.hasChild
onClicked: {
if(!item_layout.hasChild){
item_layout_rect.onClickItem()
return
}
model.expanded = !model.expanded
}
contentItem: FluIcon{
rotation: item_layout.expanded?0:-90
iconSource:FluentIcons.ChevronDown
iconSize: 15
Behavior on rotation {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
} }
return Qt.rgba(0,0,0,0)
} }
} }
FluText { FluText {
text: item_layout.text id:item_text
Layout.alignment: Qt.AlignVCenter text: display.title
topPadding: 7 anchors.centerIn: parent
bottomPadding: 7
} }
} MouseArea{
} anchors.fill: parent
Item{ onClicked: {
id:item_sub d.refreshLayout()
visible: { }
if(!hasChild){
return false
}
return item_layout.expanded??false
}
width: item_sub_layout.implicitWidth
height: item_sub_layout.implicitHeight
x:0.001
Column{
id: item_sub_layout
Repeater{
id:repeater_items
model: item_layout.items
delegate: delegate_items
} }
} }
} }
} }
} }
ListView { function count(){
id: list_root return table_model.rowCount
anchors.fill: parent
delegate: delegate_root
contentWidth: contentItem.childrenRect.width
model: tree_model
flickableDirection: Flickable.HorizontalAndVerticalFlick
clip: true
boundsBehavior: ListView.StopAtBounds
ScrollBar.vertical: FluScrollBar {}
ScrollBar.horizontal: FluScrollBar { }
} }
function updateData(items){
rootModel.clear()
rootModel.append(items)
}
function signleData(){
return currentElement
}
function multipData(){
const stack = [tree_model.get(0)];
const result = [];
while (stack.length > 0) {
const curr = stack.pop();
if(curr.multipSelected){
result.push(curr)
}
for(var i=0 ; i<curr.items.count ; i++){
stack.push(curr.items.get(i));
}
}
return result
}
function createItem(text="",expanded=true,items=[],data={}){
return {text:text,expanded:expanded,items:items,key:uniqueRandom(),multipSelected:false,multipIndex:0,multipParentKey:"",data:data};
}
function uniqueRandom() {
var timestamp = Date.now();
var random = Math.floor(Math.random() * 1000000);
return timestamp.toString() + random.toString();
}
} }

View File

@ -96,4 +96,5 @@ FluRangeSlider 1.0 Controls/FluRangeSlider.qml
FluStaggeredView 1.0 Controls/FluStaggeredView.qml FluStaggeredView 1.0 Controls/FluStaggeredView.qml
FluProgressButton 1.0 Controls/FluProgressButton.qml FluProgressButton 1.0 Controls/FluProgressButton.qml
FluLoadingButton 1.0 Controls/FluLoadingButton.qml FluLoadingButton 1.0 Controls/FluLoadingButton.qml
FluTreeItem 1.0 Controls/FluTreeItem.qml
plugin fluentuiplugin plugin fluentuiplugin

View File

@ -96,5 +96,6 @@
<file>FluentUI/Controls/ColorPicker/Content/PanelBorder.qml</file> <file>FluentUI/Controls/ColorPicker/Content/PanelBorder.qml</file>
<file>FluentUI/Controls/ColorPicker/Content/SBPicker.qml</file> <file>FluentUI/Controls/ColorPicker/Content/SBPicker.qml</file>
<file>FluentUI/Controls/FluLoadingButton.qml</file> <file>FluentUI/Controls/FluLoadingButton.qml</file>
<file>FluentUI/Controls/FluTreeItem.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -21,6 +21,7 @@ T.ScrollBar {
property int minLine : 2 property int minLine : 2
property int maxLine : 6 property int maxLine : 6
} }
z: horizontal? 10 : 20
verticalPadding : vertical ? 15 : 3 verticalPadding : vertical ? 15 : 3
horizontalPadding : horizontal ? 15 : 3 horizontalPadding : horizontal ? 15 : 3
background: Rectangle{ background: Rectangle{

View File

@ -7,4 +7,15 @@ QtObject {
property int depth: 0 property int depth: 0
property bool isExpanded: true property bool isExpanded: true
property var __parent property var __parent
property bool __expanded:{
var p = __parent;
while (p) {
if(!p.isExpanded){
return false
}
p = p.__parent;
}
return true
}
property int index: 0
} }

View File

@ -2,291 +2,146 @@ import QtQuick
import QtQuick.Window import QtQuick.Window
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Qt.labs.qmlmodels
import FluentUI import FluentUI
Item { Item {
property int selectionMode: FluTreeViewType.None property var dataSource
property var currentElement id:control
property var currentParentElement QtObject {
property var rootModel: tree_model.get(0).items id:d
signal itemClicked(var item) signal refreshLayout()
id:root onRefreshLayout: {
ListModel{ table_view.forceLayout()
id:tree_model
ListElement{
text: "根节点"
expanded:true
items:[]
key:"123456"
multipSelected:false
multipIndex:0
multipParentKey:""
} }
} function handleTree(treeData) {
Component{ var comItem = Qt.createComponent("FluTreeItem.qml");
id: delegate_root if (comItem.status !== Component.Ready) {
Column{ return
width: calculateWidth()
property var itemModel: model
Repeater{
id: repeater_first_level
model: items
delegate: delegate_items
} }
function calculateWidth(){ var stack = []
var w = 0; var rawData = []
for(var i = 0; i < repeater_first_level.count; i++) { for (var item of treeData) {
var child = repeater_first_level.itemAt(i) stack.push({node:item,depth:0,isExpanded:true,__parent:undefined})
if(w < child.width_hint){ }
w = child.width_hint; stack = stack.reverse()
var index =0
while (stack.length > 0) {
const { node, depth,isExpanded,__parent} = stack.pop();
node.depth = depth;
node.isExpanded = isExpanded;
node.__parent = __parent;
var objItem = comItem.createObject(table_view);
objItem.title = node.title
objItem.key = node.key
objItem.depth = node.depth
objItem.isExpanded = node.isExpanded
objItem.__parent = node.__parent
objItem.children = node.children
objItem.index = index
index = index + 1;
rawData.push({display:objItem})
if (node.children && node.children.length > 0) {
const children = node.children.reverse();
for (const child of children) {
stack.push({ node: child, depth: depth + 1,isExpanded:true,__parent:objItem});
} }
} }
return w;
} }
return rawData
} }
} }
Component{ onDataSourceChanged: {
id:delegate_items table_model.clear()
Column{ var data = d.handleTree(dataSource)
id:item_layout table_model.rows = data
property real level: (mapToItem(list_root,0,0).x+list_root.contentX)/0.001 table_view.forceLayout()
property var text: model.text??"Item" console.debug("共计:%1条数据".arg(table_model.rowCount))
property bool hasChild : (model.items !== undefined) && (model.items.count !== 0) }
property var items: model.items??[] TableModel {
property var expanded: model.expanded??true id:table_model
property int width_hint: calculateWidth() TableModelColumn { display: "display" }
property bool singleSelected: currentElement === model }
property var itemModel: model ListView{
function calculateWidth(){ anchors.fill: parent
var w = Math.max(list_root.width, item_layout_row.implicitWidth + 10); TableView{
if(expanded){ id:table_view
for(var i = 0; i < repeater_items.count; i++) { ScrollBar.horizontal: FluScrollBar{}
var child = repeater_items.itemAt(i) ScrollBar.vertical: FluScrollBar{}
if(w < child.width_hint){ boundsBehavior: Flickable.StopAtBounds
w = child.width_hint; model: table_model
clip: true
anchors.fill: parent
rowHeightProvider: function(row) {
if(table_model.getRow(row).display.__expanded){
return 38
}
return 0
}
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
iconSource:FluentIcons.ChevronDown
iconSize: 15
Layout.alignment: Qt.AlignVCenter
opacity: {
if(display.children){
return true
}
return false
} }
} MouseArea{
} anchors.fill: parent
return w; onClicked: {
} display.isExpanded = !display.isExpanded
Item{ d.refreshLayout()
id:item_layout_rect
width: list_root.contentWidth
height: item_layout_row.implicitHeight
Rectangle{
anchors.fill: parent
anchors.margins: 2
color:{
if(FluTheme.dark){
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){
return Qt.rgba(62/255,62/255,62/255,1)
} }
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(62/255,62/255,62/255,1):Qt.rgba(0,0,0,0)
}else{
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){
return Qt.rgba(0,0,0,0.06)
}
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(0,0,0,0.03):Qt.rgba(0,0,0,0)
} }
} }
Rectangle{ Rectangle{
width: 3 id:item_layout_text
color:FluTheme.primaryColor.dark radius: 4
visible: item_layout.singleSelected && (selectionMode === FluTreeViewType.Single) Layout.preferredWidth: item_text.implicitWidth+14
radius: 3 Layout.preferredHeight:item_text.implicitHeight+14
height: 20
anchors{
left: parent.left
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_layout_mouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
item_layout_rect.onClickItem()
}
}
}
function onClickItem(){
if(selectionMode === FluTreeViewType.None){
itemClicked(model)
}
if(selectionMode === FluTreeViewType.Single){
currentElement = model
if(item_layout.parent.parent.parent.itemModel){
currentParentElement = item_layout.parent.parent.parent.itemModel
}else{
if(item_layout.parent.itemModel){
currentParentElement = item_layout.parent.itemModel
}
}
itemClicked(model)
}
if(selectionMode === FluTreeViewType.Multiple){
}
}
RowLayout{
id:item_layout_row
anchors.verticalCenter: item_layout_rect.verticalCenter
Item{
width: 15*level
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
} HoverHandler{
FluCheckBox{ id:item_hover_text
id:item_layout_checkbox
text:""
checked: itemModel.multipSelected
visible: selectionMode === FluTreeViewType.Multiple
Layout.leftMargin: 5
function refreshCheckBox(){
const stack = [tree_model.get(0)];
const result = [];
while (stack.length > 0) {
const curr = stack.pop();
result.unshift(curr);
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipIndex",i)
curr.items.setProperty(i,"multipParentKey",curr.key)
stack.push(curr.items.get(i));
}
}
}
for(var j=0 ; j<result.length-1 ; j++){
var item = result[j]
let obj = result.find(function(o) {
return o.key === item.multipParentKey;
});
if((item.items !== undefined) && (item.items.count !== 0)){
var items = item.items
for(var k=0 ; k<items.count ; k++){
if(items.get(k).multipSelected === false){
obj.items.setProperty(item.multipIndex,"multipSelected",false)
break
}
obj.items.setProperty(item.multipIndex,"multipSelected",true)
}
}
}
} }
clickListener:function(){ color: {
if(hasChild){ if(FluTheme.dark){
const stack = [itemModel]; if(item_hover_text.hovered){
while (stack.length > 0) { return Qt.rgba(1,1,1,0.03)
const curr = stack.pop();
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipSelected",!itemModel.multipSelected)
stack.push(curr.items.get(i));
}
}
} }
refreshCheckBox() return Qt.rgba(0,0,0,0)
}else{ }else{
itemModel.multipSelected = !itemModel.multipSelected if(item_hover_text.hovered){
refreshCheckBox() return Qt.rgba(0,0,0,0.03)
}
}
}
FluIconButton{
id:item_layout_expanded
color:"#00000000"
opacity: item_layout.hasChild
onClicked: {
if(!item_layout.hasChild){
item_layout_rect.onClickItem()
return
}
model.expanded = !model.expanded
}
contentItem: FluIcon{
rotation: item_layout.expanded?0:-90
iconSource:FluentIcons.ChevronDown
iconSize: 15
Behavior on rotation {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
} }
return Qt.rgba(0,0,0,0)
}
}
FluText {
id:item_text
text: display.title
anchors.centerIn: parent
}
MouseArea{
anchors.fill: parent
onClicked: {
d.refreshLayout()
} }
} }
} }
FluText {
text: item_layout.text
Layout.alignment: Qt.AlignVCenter
topPadding: 7
bottomPadding: 7
}
}
}
Item{
id:item_sub
visible: {
if(!hasChild){
return false
}
return item_layout.expanded??false
}
width: item_sub_layout.implicitWidth
height: item_sub_layout.implicitHeight
x:0.001
Column{
id: item_sub_layout
Repeater{
id:repeater_items
model: item_layout.items
delegate: delegate_items
}
} }
} }
} }
} }
ListView { function count(){
id: list_root return table_model.rowCount
anchors.fill: parent
delegate: delegate_root
contentWidth: contentItem.childrenRect.width
model: tree_model
flickableDirection: Flickable.HorizontalAndVerticalFlick
clip: true
boundsBehavior: ListView.StopAtBounds
ScrollBar.vertical: FluScrollBar {}
ScrollBar.horizontal: FluScrollBar { }
} }
function updateData(items){
rootModel.clear()
rootModel.append(items)
}
function signleData(){
return currentElement
}
function multipData(){
const stack = [tree_model.get(0)];
const result = [];
while (stack.length > 0) {
const curr = stack.pop();
if(curr.multipSelected){
result.push(curr)
}
for(var i=0 ; i<curr.items.count ; i++){
stack.push(curr.items.get(i));
}
}
return result
}
function createItem(text="",expanded=true,items=[],data={}){
return {text:text,expanded:expanded,items:items,key:uniqueRandom(),multipSelected:false,multipIndex:0,multipParentKey:"",data:data};
}
function uniqueRandom() {
var timestamp = Date.now();
var random = Math.floor(Math.random() * 1000000);
return timestamp.toString() + random.toString();
}
} }

View File

@ -1,144 +0,0 @@
import QtQuick
import QtQuick.Window
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.qmlmodels
import FluentUI
Rectangle {
color:"#33000000"
property var dataSource
clip: true
function iterateTree(treeData) {
var comItem = Qt.createComponent("FluTreeItem.qml");
if (comItem.status !== Component.Ready) {
return
}
const stack = [{ node: treeData[0], depth: 0 , isExpanded: true, __parent: undefined}];
while (stack.length > 0) {
const { node, depth,isExpanded,__parent} = stack.pop();
node.depth = depth;
node.isExpanded = isExpanded;
node.__parent = __parent;
var objItem = comItem.createObject(table_view);
objItem.title = node.title
objItem.key = node.key
objItem.depth = node.depth
objItem.isExpanded = node.isExpanded
objItem.__parent = node.__parent
objItem.children = node.children
table_model.appendRow({itemData:objItem})
if (node.children && node.children.length > 0) {
const children = node.children.reverse();
for (const child of children) {
stack.push({ node: child, depth: depth + 1,isExpanded:true,__parent:objItem});
}
}
}
console.debug("共计:%1条数据".arg(table_model.rowCount))
}
onDataSourceChanged: {
table_model.clear()
iterateTree(dataSource)
}
TableModel {
id:table_model
TableModelColumn { display: "itemData" }
}
Component{
id:com_item
Rectangle {
implicitWidth: 120 + 50*itemData.depth
implicitHeight: 38
RowLayout{
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin:14+50*itemData.depth
FluIcon{
rotation: itemData.isExpanded?0:-90
iconSource:FluentIcons.ChevronDown
iconSize: 15
Layout.alignment: Qt.AlignVCenter
Behavior on rotation {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
opacity: {
if(itemData.children){
return true
}
return false
}
MouseArea{
anchors.fill: parent
onClicked: {
itemData.isExpanded = !itemData.isExpanded
}
}
}
Rectangle{
radius: 4
Layout.preferredWidth: item_text.width+14
Layout.preferredHeight:item_text.height+14
Layout.alignment: Qt.AlignVCenter
HoverHandler{
id:item_hover_text
}
color: {
if(FluTheme.dark){
if(item_hover_text.hovered){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
}else{
if(item_hover_text.hovered){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
}
}
Text {
id:item_text
text: itemData.title
anchors.centerIn: parent
}
}
}
}
}
TableView{
id:table_view
anchors.fill: parent
ScrollBar.horizontal: FluScrollBar{}
ScrollBar.vertical: FluScrollBar{}
boundsBehavior: Flickable.StopAtBounds
model: table_model
delegate: Loader{
property var itemData: display
sourceComponent: {
if(!itemData.__parent){
return com_item
}
if(itemData.__parent.isExpanded){
return com_item
}
return undefined
}
}
}
}