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}`;
const treeNode = {
title: key,
key,
};
if (level > 0) {
treeNode.children = dig(key, level - 1);
} }
list.push(treeNode);
function randomCompany() {
var companies = ["阿里巴巴", "腾讯", "百度", "京东", "华为", "小米", "字节跳动", "美团", "滴滴"]
return companies[Math.floor(Math.random() * companies.length)]
} }
return list;
function randomDepartment() { };
var departments = ["技术部", "销售部", "市场部", "人事部", "财务部", "客服部", "产品部", "设计部", "运营部"] return dig();
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))
} }
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{
width: calculateWidth()
property var itemModel: model
Repeater{
id: repeater_first_level
model: items
delegate: delegate_items
}
function calculateWidth(){
var w = 0;
for(var i = 0; i < repeater_first_level.count; i++) {
var child = repeater_first_level.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
return w;
}
}
}
Component{
id:delegate_items
Column{
id:item_layout
property real level: (mapToItem(list_root,0,0).x+list_root.contentX)/0.001
property var text: model.text??"Item"
property bool hasChild : (model.items !== undefined) && (model.items.count !== 0)
property var items: model.items??[]
property var expanded: model.expanded??true
property int width_hint: calculateWidth()
property bool singleSelected: currentElement === model
property var itemModel: model
function calculateWidth(){
var w = Math.max(list_root.width, item_layout_row.implicitWidth + 10);
if(expanded){
for(var i = 0; i < repeater_items.count; i++) {
var child = repeater_items.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
}
return w;
}
Item{
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{
width: 3
color:FluTheme.primaryColor.dark
visible: item_layout.singleSelected && (selectionMode === FluTreeViewType.Single)
radius: 3
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
}
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 return
} }
model.expanded = !model.expanded var stack = []
var rawData = []
for (var item of treeData) {
stack.push({node:item,depth:0,isExpanded:true,__parent:undefined})
} }
contentItem: FluIcon{ stack = stack.reverse()
rotation: item_layout.expanded?0:-90 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 rawData
}
}
onDataSourceChanged: {
table_model.clear()
var data = d.handleTree(dataSource)
table_model.rows = data
table_view.forceLayout()
console.debug("共计:%1条数据".arg(table_model.rowCount))
}
TableModel {
id:table_model
TableModelColumn { display: "display" }
}
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
}
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 iconSource:FluentIcons.ChevronDown
iconSize: 15 iconSize: 15
Behavior on rotation { Layout.alignment: Qt.AlignVCenter
enabled: FluTheme.enableAnimation opacity: {
NumberAnimation{ if(display.children){
duration: 167 return true
easing.type: Easing.OutCubic }
return false
}
MouseArea{
anchors.fill: parent
onClicked: {
display.isExpanded = !display.isExpanded
d.refreshLayout()
} }
} }
} }
Rectangle{
id:item_layout_text
radius: 4
Layout.preferredWidth: item_text.implicitWidth+14
Layout.preferredHeight:item_text.implicitHeight+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)
}
} }
FluText { FluText {
text: item_layout.text id:item_text
Layout.alignment: Qt.AlignVCenter text: display.title
topPadding: 7 anchors.centerIn: parent
bottomPadding: 7
} }
} MouseArea{
}
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 {
id: list_root
anchors.fill: parent anchors.fill: parent
delegate: delegate_root onClicked: {
contentWidth: contentItem.childrenRect.width d.refreshLayout()
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();
} }
}
function count(){
return table_model.rowCount
}
} }

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{
width: calculateWidth()
property var itemModel: model
Repeater{
id: repeater_first_level
model: items
delegate: delegate_items
}
function calculateWidth(){
var w = 0;
for(var i = 0; i < repeater_first_level.count; i++) {
var child = repeater_first_level.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
return w;
}
}
}
Component{
id:delegate_items
Column{
id:item_layout
property real level: (mapToItem(list_root,0,0).x+list_root.contentX)/0.001
property var text: model.text??"Item"
property bool hasChild : (model.items !== undefined) && (model.items.count !== 0)
property var items: model.items??[]
property var expanded: model.expanded??true
property int width_hint: calculateWidth()
property bool singleSelected: currentElement === model
property var itemModel: model
function calculateWidth(){
var w = Math.max(list_root.width, item_layout_row.implicitWidth + 10);
if(expanded){
for(var i = 0; i < repeater_items.count; i++) {
var child = repeater_items.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
}
return w;
}
Item{
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{
width: 3
color:FluTheme.primaryColor.dark
visible: item_layout.singleSelected && (selectionMode === FluTreeViewType.Single)
radius: 3
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
}
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 return
} }
model.expanded = !model.expanded var stack = []
var rawData = []
for (var item of treeData) {
stack.push({node:item,depth:0,isExpanded:true,__parent:undefined})
} }
contentItem: FluIcon{ stack = stack.reverse()
rotation: item_layout.expanded?0:-90 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 rawData
}
}
onDataSourceChanged: {
table_model.clear()
var data = d.handleTree(dataSource)
table_model.rows = data
table_view.forceLayout()
console.debug("共计:%1条数据".arg(table_model.rowCount))
}
TableModel {
id:table_model
TableModelColumn { display: "display" }
}
ListView{
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
}
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 iconSource:FluentIcons.ChevronDown
iconSize: 15 iconSize: 15
Behavior on rotation { Layout.alignment: Qt.AlignVCenter
enabled: FluTheme.enableAnimation opacity: {
NumberAnimation{ if(display.children){
duration: 167 return true
easing.type: Easing.OutCubic }
return false
}
MouseArea{
anchors.fill: parent
onClicked: {
display.isExpanded = !display.isExpanded
d.refreshLayout()
} }
} }
} }
Rectangle{
id:item_layout_text
radius: 4
Layout.preferredWidth: item_text.implicitWidth+14
Layout.preferredHeight:item_text.implicitHeight+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)
}
} }
FluText { FluText {
text: item_layout.text id:item_text
Layout.alignment: Qt.AlignVCenter text: display.title
topPadding: 7 anchors.centerIn: parent
bottomPadding: 7
} }
} MouseArea{
}
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 {
id: list_root
anchors.fill: parent anchors.fill: parent
delegate: delegate_root onClicked: {
contentWidth: contentItem.childrenRect.width d.refreshLayout()
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();
} }
}
}
function count(){
return table_model.rowCount
}
} }

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
}
}
}
}