This commit is contained in:
朱子楚\zhuzi 2023-09-17 20:36:33 +08:00
parent be194e7624
commit 8fc74fe43b
81 changed files with 1368 additions and 930 deletions

View File

@ -8,6 +8,13 @@ Window {
id: app id: app
flags: Qt.SplashScreen flags: Qt.SplashScreen
Connections{
target: FluTheme
function onDarkModeChanged(){
SettingsHelper.saveDarkMode(FluTheme.darkMode)
}
}
FluHttpInterceptor{ FluHttpInterceptor{
id:interceptor id:interceptor
function onIntercept(request){ function onIntercept(request){
@ -26,7 +33,7 @@ Window {
Component.onCompleted: { Component.onCompleted: {
FluApp.init(app) FluApp.init(app)
FluTheme.darkMode = FluThemeType.System FluTheme.darkMode = SettingsHelper.getDarkMode()
FluTheme.enableAnimation = true FluTheme.enableAnimation = true
FluApp.routes = { FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml", "/":"qrc:/example/qml/window/MainWindow.qml",

View File

@ -80,7 +80,6 @@ FluExpander{
"FluIcon", "FluIcon",
"FluIconButton", "FluIconButton",
"FluInfoBar", "FluInfoBar",
"FluItem",
"FluMediaPlayer", "FluMediaPlayer",
"FluMenu", "FluMenu",
"FluMenuItem", "FluMenuItem",

View File

@ -477,6 +477,9 @@ FluObject{
} }
function getSearchData(){ function getSearchData(){
if(!navigationView){
return
}
var arr = [] var arr = []
var items = navigationView.getItems(); var items = navigationView.getItems();
for(var i=0;i<items.length;i++){ for(var i=0;i<items.length;i++){

View File

@ -47,7 +47,7 @@ FluScrollablePage{
height: 1200/4+20 height: 1200/4+20
paddings: 10 paddings: 10
Layout.topMargin: 10 Layout.topMargin: 10
FluRectangle{ FluClip{
width: 1920/4 width: 1920/4
height: 1200/4 height: 1200/4
radius:[8,8,8,8] radius:[8,8,8,8]

View File

@ -44,7 +44,6 @@ FluScrollablePage{
} }
FluCarousel{ FluCarousel{
anchors.fill: parent anchors.fill: parent
radius:[8,8,8,8]
delegate: Component{ delegate: Component{
Image { Image {
anchors.fill: parent anchors.fill: parent
@ -82,7 +81,6 @@ FluScrollablePage{
} }
FluCarousel{ FluCarousel{
anchors.fill: parent anchors.fill: parent
radius:[8,8,8,8]
loopTime:1500 loopTime:1500
indicatorGravity: Qt.AlignHCenter | Qt.AlignTop indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
indicatorMarginTop:15 indicatorMarginTop:15

View File

@ -27,6 +27,7 @@ FluScrollablePage{
id: bg id: bg
fillMode:Image.PreserveAspectCrop fillMode:Image.PreserveAspectCrop
anchors.fill: parent anchors.fill: parent
asynchronous: true
verticalAlignment: Qt.AlignTop verticalAlignment: Qt.AlignTop
sourceSize: Qt.size(960,640) sourceSize: Qt.size(960,640)
source: "qrc:/example/res/image/bg_home_header.png" source: "qrc:/example/res/image/bg_home_header.png"
@ -49,23 +50,9 @@ FluScrollablePage{
} }
} }
ListView{ Component{
id: list id:com_grallery
anchors{ Item{
left: parent.left
right: parent.right
bottom: parent.bottom
}
orientation: ListView.Horizontal
height: 240
model: model_header
header: Item{height: 10;width: 10}
footer: Item{height: 10;width: 10}
ScrollBar.horizontal: FluScrollBar{
id: scrollbar_header
}
clip: false
delegate:Item{
id: control id: control
width: 220 width: 220
height: 240 height: 240
@ -73,7 +60,7 @@ FluScrollablePage{
radius:5 radius:5
anchors.fill: item_content anchors.fill: item_content
} }
FluItem{ FluClip{
id:item_content id:item_content
radius: [5,5,5,5] radius: [5,5,5,5]
width: 200 width: 200
@ -90,19 +77,14 @@ FluScrollablePage{
Rectangle{ Rectangle{
anchors.fill: parent anchors.fill: parent
radius: 5 radius: 5
color:{ color:FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
if(FluTheme.dark){ visible: item_mouse.containsMouse
if(item_mouse.containsMouse){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0.0)
}else{
if(item_mouse.containsMouse){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0.0)
}
} }
Rectangle{
anchors.fill: parent
radius: 5
color:Qt.rgba(0,0,0,0.0)
visible: !item_mouse.containsMouse
} }
ColumnLayout{ ColumnLayout{
Image { Image {
@ -154,11 +136,31 @@ FluScrollablePage{
} }
} }
} }
ListView{
id: list
anchors{
left: parent.left
right: parent.right
bottom: parent.bottom
}
orientation: ListView.Horizontal
height: 240
model: model_header
header: Item{height: 10;width: 10}
footer: Item{height: 10;width: 10}
ScrollBar.horizontal: FluScrollBar{
id: scrollbar_header
}
clip: false
delegate: com_grallery
}
} }
Component{ Component{
id:com_item id:com_item
Item{ Item{
property string desc: modelData.desc
width: 320 width: 320
height: 120 height: 120
FluArea{ FluArea{
@ -194,7 +196,6 @@ FluScrollablePage{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
} }
FluText{ FluText{
id:item_title id:item_title
text:modelData.title text:modelData.title
@ -205,10 +206,9 @@ FluScrollablePage{
top: item_icon.top top: item_icon.top
} }
} }
FluText{ FluText{
id:item_desc id:item_desc
text:modelData.desc text:desc
color:FluColors.Grey120 color:FluColors.Grey120
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
elide: Text.ElideRight elide: Text.ElideRight

View File

@ -23,7 +23,7 @@ FluScrollablePage{
FluButton{ FluButton{
text:"Info" text:"Info"
onClicked: { onClicked: {
showInfo("这是一个Info样式的InfoBar",0,"123") showInfo("这是一个Info样式的InfoBar")
} }
} }
FluButton{ FluButton{

View File

@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Window import QtQuick.Window
import FluentUI import FluentUI
import Qt5Compat.GraphicalEffects
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
FluScrollablePage{ FluScrollablePage{

View File

@ -68,7 +68,7 @@ FluScrollablePage{
} }
RowLayout{ RowLayout{
spacing: 14 spacing: 14
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[25,0,25,25] radius:[25,0,25,25]
@ -79,7 +79,7 @@ FluScrollablePage{
sourceSize: Qt.size(width,height) sourceSize: Qt.size(width,height)
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[10,10,10,10] radius:[10,10,10,10]
@ -90,7 +90,7 @@ FluScrollablePage{
source: "qrc:/example/res/svg/avatar_2.svg" source: "qrc:/example/res/svg/avatar_2.svg"
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[25,25,25,25] radius:[25,25,25,25]
@ -101,7 +101,7 @@ FluScrollablePage{
source: "qrc:/example/res/svg/avatar_3.svg" source: "qrc:/example/res/svg/avatar_3.svg"
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[0,25,25,25] radius:[0,25,25,25]
@ -113,7 +113,7 @@ FluScrollablePage{
} }
} }
} }
FluRectangle{ FluClip{
width: 1920/5 width: 1920/5
height: 1200/5 height: 1200/5
radius:[8,8,8,8] radius:[8,8,8,8]

View File

@ -40,7 +40,7 @@ FluScrollablePage{
spacing: 20 spacing: 20
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
FluText{ FluText{
text:"当前版本 v%1".arg(appInfo.version) text:"当前版本 v%1".arg(AppInfo.version)
font: FluTextStyle.Body font: FluTextStyle.Body
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@ -56,6 +56,38 @@ FluScrollablePage{
} }
} }
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 50
paddings: 10
FluCheckBox{
text:"Software Render"
checked: SettingsHelper.getReander() === "software"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
if(SettingsHelper.getReander() === "software"){
SettingsHelper.saveRender("")
}else{
SettingsHelper.saveRender("software")
}
dialog_render.open()
}
}
}
FluContentDialog{
id:dialog_render
title:"友情提示"
message:"此操作需要重启才能生效,是否重新启动?"
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText: "取消"
positiveText:"确定"
onPositiveClicked:{
window.deleteWindow()
AppInfo.restart()
}
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
@ -141,10 +173,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: ["Zh","En"] model: ["Zh","En"]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked: appInfo.lang.objectName === modelData checked: AppInfo.lang.objectName === modelData
text:modelData text:modelData
clickListener:function(){ clickListener:function(){
appInfo.changeLang(modelData) AppInfo.changeLang(modelData)
} }
} }
} }

View File

@ -10,9 +10,9 @@ FluScrollablePage {
title:"TreeView" title:"TreeView"
function treeData(){ function treeData(){
const dig = (path = '0', level = 4) => { const dig = (path = '0', level = 3) => {
const list = []; const list = [];
for (let i = 0; i < 10; i += 1) { for (let i = 0; i < 8; i += 1) {
const key = `${path}-${i}`; const key = `${path}-${i}`;
const treeNode = { const treeNode = {
title: key, title: key,
@ -48,7 +48,7 @@ FluScrollablePage {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 10 Layout.topMargin: 10
paddings: 10 paddings: 10
height: 400 height: 640
Item{ Item{
anchors.fill: tree_view anchors.fill: tree_view
FluShadow{} FluShadow{}
@ -61,6 +61,7 @@ FluScrollablePage {
left:parent.left left:parent.left
bottom:parent.bottom bottom:parent.bottom
} }
draggable:switch_draggable.checked
showLine: switch_showline.checked showLine: switch_showline.checked
Component.onCompleted: { Component.onCompleted: {
var data = treeData() var data = treeData()
@ -68,6 +69,7 @@ FluScrollablePage {
} }
} }
Column{ Column{
spacing: 15
anchors{ anchors{
top:parent.top top:parent.top
topMargin: 10 topMargin: 10
@ -86,7 +88,7 @@ FluScrollablePage {
id:slider_width id:slider_width
value: 200 value: 200
from: 160 from: 160
to:320 to:460
} }
} }
@ -95,6 +97,12 @@ FluScrollablePage {
text:"showLine" text:"showLine"
checked: true checked: true
} }
FluToggleSwitch{
id:switch_draggable
text:"draggable"
checked: false
}
} }
} }
CodeExpander{ CodeExpander{

View File

@ -35,7 +35,7 @@ CustomWindow {
} }
} }
FluText{ FluText{
text:"v%1".arg(appInfo.version) text:"v%1".arg(AppInfo.version)
font: FluTextStyle.Body font: FluTextStyle.Body
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
} }

View File

@ -318,7 +318,7 @@ CustomWindow {
property string body property string body
id:dialog_update id:dialog_update
title:"升级提示" title:"升级提示"
message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+appInfo.version+" \n现在是否去下载新版本\n\n更新内容\n"+body message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+AppInfo.version+" \n现在是否去下载新版本\n\n更新内容\n"+body
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText: "取消" negativeText: "取消"
positiveText:"确定" positiveText:"确定"
@ -340,9 +340,9 @@ CustomWindow {
onSuccess: onSuccess:
(result)=>{ (result)=>{
var data = JSON.parse(result) var data = JSON.parse(result)
console.debug("current version "+appInfo.version) console.debug("current version "+AppInfo.version)
console.debug("new version "+data.tag_name) console.debug("new version "+data.tag_name)
if(data.tag_name !== appInfo.version){ if(data.tag_name !== AppInfo.version){
dialog_update.newVerson = data.tag_name dialog_update.newVerson = data.tag_name
dialog_update.body = data.body dialog_update.body = data.body
dialog_update.open() dialog_update.open()

View File

@ -8,6 +8,13 @@ Window {
id: app id: app
flags: Qt.SplashScreen flags: Qt.SplashScreen
Connections{
target: FluTheme
function onDarkModeChanged(){
SettingsHelper.saveDarkMode(FluTheme.darkMode)
}
}
FluHttpInterceptor{ FluHttpInterceptor{
id:interceptor id:interceptor
function onIntercept(request){ function onIntercept(request){
@ -26,7 +33,7 @@ Window {
Component.onCompleted: { Component.onCompleted: {
FluApp.init(app) FluApp.init(app)
FluTheme.darkMode = FluThemeType.System FluTheme.darkMode = SettingsHelper.getDarkMode()
FluTheme.enableAnimation = true FluTheme.enableAnimation = true
FluApp.routes = { FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml", "/":"qrc:/example/qml/window/MainWindow.qml",

View File

@ -80,7 +80,6 @@ FluExpander{
"FluIcon", "FluIcon",
"FluIconButton", "FluIconButton",
"FluInfoBar", "FluInfoBar",
"FluItem",
"FluMediaPlayer", "FluMediaPlayer",
"FluMenu", "FluMenu",
"FluMenuItem", "FluMenuItem",

View File

@ -477,6 +477,9 @@ FluObject{
} }
function getSearchData(){ function getSearchData(){
if(!navigationView){
return
}
var arr = [] var arr = []
var items = navigationView.getItems(); var items = navigationView.getItems();
for(var i=0;i<items.length;i++){ for(var i=0;i<items.length;i++){

View File

@ -48,7 +48,7 @@ FluScrollablePage{
height: 1200/4+20 height: 1200/4+20
paddings: 10 paddings: 10
Layout.topMargin: 10 Layout.topMargin: 10
FluRectangle{ FluClip{
width: 1920/4 width: 1920/4
height: 1200/4 height: 1200/4
radius:[8,8,8,8] radius:[8,8,8,8]

View File

@ -45,7 +45,6 @@ FluScrollablePage{
} }
FluCarousel{ FluCarousel{
anchors.fill: parent anchors.fill: parent
radius:[8,8,8,8]
delegate: Component{ delegate: Component{
Image { Image {
anchors.fill: parent anchors.fill: parent
@ -83,7 +82,6 @@ FluScrollablePage{
} }
FluCarousel{ FluCarousel{
anchors.fill: parent anchors.fill: parent
radius:[8,8,8,8]
loopTime:1500 loopTime:1500
indicatorGravity: Qt.AlignHCenter | Qt.AlignTop indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
indicatorMarginTop:15 indicatorMarginTop:15

View File

@ -2,6 +2,7 @@ import QtQuick 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtGraphicalEffects 1.0
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import FluentUI 1.0 import FluentUI 1.0
@ -50,23 +51,9 @@ FluScrollablePage{
} }
} }
ListView{ Component{
id: list id:com_grallery
anchors{ Item{
left: parent.left
right: parent.right
bottom: parent.bottom
}
orientation: ListView.Horizontal
height: 240
model: model_header
header: Item{height: 10;width: 10}
footer: Item{height: 10;width: 10}
ScrollBar.horizontal: FluScrollBar{
id: scrollbar_header
}
clip: false
delegate:Item{
id: control id: control
width: 220 width: 220
height: 240 height: 240
@ -74,7 +61,7 @@ FluScrollablePage{
radius:5 radius:5
anchors.fill: item_content anchors.fill: item_content
} }
FluItem{ FluClip{
id:item_content id:item_content
radius: [5,5,5,5] radius: [5,5,5,5]
width: 200 width: 200
@ -91,19 +78,14 @@ FluScrollablePage{
Rectangle{ Rectangle{
anchors.fill: parent anchors.fill: parent
radius: 5 radius: 5
color:{ color:FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
if(FluTheme.dark){ visible: item_mouse.containsMouse
if(item_mouse.containsMouse){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0.0)
}else{
if(item_mouse.containsMouse){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0.0)
}
} }
Rectangle{
anchors.fill: parent
radius: 5
color:Qt.rgba(0,0,0,0.0)
visible: !item_mouse.containsMouse
} }
ColumnLayout{ ColumnLayout{
Image { Image {
@ -155,11 +137,31 @@ FluScrollablePage{
} }
} }
} }
ListView{
id: list
anchors{
left: parent.left
right: parent.right
bottom: parent.bottom
}
orientation: ListView.Horizontal
height: 240
model: model_header
header: Item{height: 10;width: 10}
footer: Item{height: 10;width: 10}
ScrollBar.horizontal: FluScrollBar{
id: scrollbar_header
}
clip: false
delegate: com_grallery
}
} }
Component{ Component{
id:com_item id:com_item
Item{ Item{
property string desc: modelData.desc
width: 320 width: 320
height: 120 height: 120
FluArea{ FluArea{
@ -195,7 +197,6 @@ FluScrollablePage{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
} }
FluText{ FluText{
id:item_title id:item_title
text:modelData.title text:modelData.title
@ -206,10 +207,9 @@ FluScrollablePage{
top: item_icon.top top: item_icon.top
} }
} }
FluText{ FluText{
id:item_desc id:item_desc
text:modelData.desc text:desc
color:FluColors.Grey120 color:FluColors.Grey120
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
elide: Text.ElideRight elide: Text.ElideRight

View File

@ -24,7 +24,7 @@ FluScrollablePage{
FluButton{ FluButton{
text:"Info" text:"Info"
onClicked: { onClicked: {
showInfo("这是一个Info样式的InfoBar",0,"123") showInfo("这是一个Info样式的InfoBar")
} }
} }
FluButton{ FluButton{

View File

@ -3,7 +3,6 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import FluentUI 1.0 import FluentUI 1.0
import QtGraphicalEffects 1.15
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "../component" import "../component"

View File

@ -69,7 +69,7 @@ FluScrollablePage{
} }
RowLayout{ RowLayout{
spacing: 14 spacing: 14
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[25,0,25,25] radius:[25,0,25,25]
@ -80,7 +80,7 @@ FluScrollablePage{
sourceSize: Qt.size(width,height) sourceSize: Qt.size(width,height)
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[10,10,10,10] radius:[10,10,10,10]
@ -91,7 +91,7 @@ FluScrollablePage{
source: "qrc:/example/res/svg/avatar_2.svg" source: "qrc:/example/res/svg/avatar_2.svg"
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[25,25,25,25] radius:[25,25,25,25]
@ -102,7 +102,7 @@ FluScrollablePage{
source: "qrc:/example/res/svg/avatar_3.svg" source: "qrc:/example/res/svg/avatar_3.svg"
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[0,25,25,25] radius:[0,25,25,25]
@ -114,7 +114,7 @@ FluScrollablePage{
} }
} }
} }
FluRectangle{ FluClip{
width: 1920/5 width: 1920/5
height: 1200/5 height: 1200/5
radius:[8,8,8,8] radius:[8,8,8,8]

View File

@ -43,7 +43,7 @@ FluScrollablePage{
spacing: 20 spacing: 20
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
FluText{ FluText{
text:"当前版本 v%1".arg(appInfo.version) text:"当前版本 v%1".arg(AppInfo.version)
font: FluTextStyle.Body font: FluTextStyle.Body
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
@ -59,6 +59,38 @@ FluScrollablePage{
} }
} }
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 50
paddings: 10
FluCheckBox{
text:"Software Render"
checked: SettingsHelper.getReander() === "software"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
if(SettingsHelper.getReander() === "software"){
SettingsHelper.saveRender("")
}else{
SettingsHelper.saveRender("software")
}
dialog_render.open()
}
}
}
FluContentDialog{
id:dialog_render
title:"友情提示"
message:"此操作需要重启才能生效,是否重新启动?"
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText: "取消"
positiveText:"确定"
onPositiveClicked:{
window.deleteWindow()
AppInfo.restart()
}
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
@ -144,10 +176,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: ["Zh","En"] model: ["Zh","En"]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked: appInfo.lang.objectName === modelData checked: AppInfo.lang.objectName === modelData
text:modelData text:modelData
clickListener:function(){ clickListener:function(){
appInfo.changeLang(modelData) AppInfo.changeLang(modelData)
} }
} }
} }

View File

@ -11,9 +11,9 @@ FluScrollablePage {
title:"TreeView" title:"TreeView"
function treeData(){ function treeData(){
const dig = (path = '0', level = 4) => { const dig = (path = '0', level = 3) => {
const list = []; const list = [];
for (let i = 0; i < 10; i += 1) { for (let i = 0; i < 8; i += 1) {
const key = `${path}-${i}`; const key = `${path}-${i}`;
const treeNode = { const treeNode = {
title: key, title: key,
@ -49,7 +49,7 @@ FluScrollablePage {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 10 Layout.topMargin: 10
paddings: 10 paddings: 10
height: 400 height: 420
Item{ Item{
anchors.fill: tree_view anchors.fill: tree_view
FluShadow{} FluShadow{}
@ -62,6 +62,7 @@ FluScrollablePage {
left:parent.left left:parent.left
bottom:parent.bottom bottom:parent.bottom
} }
draggable:switch_draggable.checked
showLine: switch_showline.checked showLine: switch_showline.checked
Component.onCompleted: { Component.onCompleted: {
var data = treeData() var data = treeData()
@ -69,6 +70,7 @@ FluScrollablePage {
} }
} }
Column{ Column{
spacing: 15
anchors{ anchors{
top:parent.top top:parent.top
topMargin: 10 topMargin: 10
@ -87,7 +89,7 @@ FluScrollablePage {
id:slider_width id:slider_width
value: 200 value: 200
from: 160 from: 160
to:320 to:460
} }
} }
@ -96,6 +98,12 @@ FluScrollablePage {
text:"showLine" text:"showLine"
checked: true checked: true
} }
FluToggleSwitch{
id:switch_draggable
text:"draggable"
checked: false
}
} }
} }
CodeExpander{ CodeExpander{

View File

@ -36,7 +36,7 @@ CustomWindow {
} }
} }
FluText{ FluText{
text:"v%1".arg(appInfo.version) text:"v%1".arg(AppInfo.version)
font: FluTextStyle.Body font: FluTextStyle.Body
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
} }

View File

@ -321,7 +321,7 @@ CustomWindow {
property string body property string body
id:dialog_update id:dialog_update
title:"升级提示" title:"升级提示"
message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+appInfo.version+" \n现在是否去下载新版本\n\n更新内容\n"+body message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+AppInfo.version+" \n现在是否去下载新版本\n\n更新内容\n"+body
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText: "取消" negativeText: "取消"
positiveText:"确定" positiveText:"确定"
@ -343,9 +343,9 @@ CustomWindow {
onSuccess: onSuccess:
(result)=>{ (result)=>{
var data = JSON.parse(result) var data = JSON.parse(result)
console.debug("current version "+appInfo.version) console.debug("current version "+AppInfo.version)
console.debug("new version "+data.tag_name) console.debug("new version "+data.tag_name)
if(data.tag_name !== appInfo.version){ if(data.tag_name !== AppInfo.version){
dialog_update.newVerson = data.tag_name dialog_update.newVerson = data.tag_name
dialog_update.body = data.body dialog_update.body = data.body
dialog_update.open() dialog_update.open()

View File

@ -2,6 +2,7 @@
#include <QQmlContext> #include <QQmlContext>
#include <QDebug> #include <QDebug>
#include <QGuiApplication>
#include "lang/En.h" #include "lang/En.h"
#include "lang/Zh.h" #include "lang/Zh.h"
#include "Version.h" #include "Version.h"
@ -20,7 +21,6 @@ void AppInfo::init(QQmlApplicationEngine *engine){
QObject::connect(this,&AppInfo::langChanged,this,[=]{ QObject::connect(this,&AppInfo::langChanged,this,[=]{
context->setContextProperty("lang",this->lang()); context->setContextProperty("lang",this->lang());
}); });
context->setContextProperty("appInfo",this);
} }
void AppInfo::changeLang(const QString& locale){ void AppInfo::changeLang(const QString& locale){
@ -35,3 +35,7 @@ void AppInfo::changeLang(const QString& locale){
lang(new En()); lang(new En());
} }
} }
void AppInfo::restart(){
qApp->exit(931);
}

View File

@ -5,16 +5,20 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include "lang/Lang.h" #include "lang/Lang.h"
#include "stdafx.h" #include "stdafx.h"
#include "singleton.h"
class AppInfo : public QObject class AppInfo : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY_AUTO(QString,version) Q_PROPERTY_AUTO(QString,version)
Q_PROPERTY_AUTO(Lang*,lang) Q_PROPERTY_AUTO(Lang*,lang)
public: private:
explicit AppInfo(QObject *parent = nullptr); explicit AppInfo(QObject *parent = nullptr);
public:
SINGLETONG(AppInfo)
void init(QQmlApplicationEngine *engine); void init(QQmlApplicationEngine *engine);
Q_INVOKABLE void changeLang(const QString& locale); Q_INVOKABLE void changeLang(const QString& locale);
Q_INVOKABLE void restart();
}; };
#endif // APPINFO_H #endif // APPINFO_H

View File

@ -0,0 +1,40 @@
#include "SettingsHelper.h"
#include <QDataStream>
SettingsHelper::SettingsHelper(QObject *parent) : QObject(parent)
{
}
SettingsHelper::~SettingsHelper() = default;
void SettingsHelper::save(const QString& key,QVariant val)
{
QByteArray data = {};
QDataStream stream(&data, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_6);
stream << val;
m_settings->setValue(key, data);
}
QVariant SettingsHelper::get(const QString& key){
const QByteArray data = m_settings->value(key).toByteArray();
if (data.isEmpty()) {
return {};
}
QDataStream stream(data);
stream.setVersion(QDataStream::Qt_5_6);
QVariant val;
stream >> val;
return val;
}
void SettingsHelper::init(char *argv[]){
auto applicationPath = QString::fromStdString(argv[0]);
const QFileInfo fileInfo(applicationPath);
const QString iniFileName = fileInfo.completeBaseName() + ".ini";
const QString iniFilePath = fileInfo.dir().path() + "/" + iniFileName;
qDebug()<<iniFilePath;
m_settings.reset(new QSettings(iniFilePath, QSettings::IniFormat));
}

View File

@ -0,0 +1,33 @@
#ifndef SETTINGSHELPER_H
#define SETTINGSHELPER_H
#include <QtCore/qobject.h>
#include <QtQml/qqml.h>
#include <QSettings>
#include <QScopedPointer>
#include <QFileInfo>
#include <QCoreApplication>
#include <QDir>
#include "src/singleton.h"
class SettingsHelper : public QObject
{
Q_OBJECT
private:
explicit SettingsHelper(QObject* parent = nullptr);
public:
SINGLETONG(SettingsHelper)
~SettingsHelper() override;
void init(char *argv[]);
Q_INVOKABLE void saveRender(const QString& render){save("render",render);}
Q_INVOKABLE QString getReander(){return get("render").toString();}
Q_INVOKABLE void saveDarkMode(int darkModel){save("darkMode",darkModel);}
Q_INVOKABLE int getDarkMode(){return get("darkMode").toInt(0);}
private:
void save(const QString& key,QVariant val);
QVariant get(const QString& key);
private:
QScopedPointer<QSettings> m_settings;
};
#endif // SETTINGSHELPER_H

View File

@ -13,6 +13,8 @@
#include "src/component/CircularReveal.h" #include "src/component/CircularReveal.h"
#include "src/component/FileWatcher.h" #include "src/component/FileWatcher.h"
#include "src/component/FpsItem.h" #include "src/component/FpsItem.h"
#include "src/helper/SettingsHelper.h"
#ifdef FLUENTUI_BUILD_STATIC_LIB #ifdef FLUENTUI_BUILD_STATIC_LIB
#if (QT_VERSION > QT_VERSION_CHECK(6, 2, 0)) #if (QT_VERSION > QT_VERSION_CHECK(6, 2, 0))
Q_IMPORT_QML_PLUGIN(FluentUIPlugin) Q_IMPORT_QML_PLUGIN(FluentUIPlugin)
@ -24,6 +26,7 @@ FRAMELESSHELPER_USE_NAMESPACE
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
SettingsHelper::getInstance()->init(argv);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
@ -36,6 +39,13 @@ FRAMELESSHELPER_USE_NAMESPACE
QGuiApplication::setOrganizationName("ZhuZiChu"); QGuiApplication::setOrganizationName("ZhuZiChu");
QGuiApplication::setOrganizationDomain("https://zhuzichu520.github.io"); QGuiApplication::setOrganizationDomain("https://zhuzichu520.github.io");
QGuiApplication::setApplicationName("FluentUI"); QGuiApplication::setApplicationName("FluentUI");
if(SettingsHelper::getInstance()->getReander()=="software"){
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
#endif
}
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow); FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow);
@ -48,8 +58,10 @@ FRAMELESSHELPER_USE_NAMESPACE
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur,false); FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur,false);
#endif #endif
AppInfo* appInfo = new AppInfo();
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
AppInfo::getInstance()->init(&engine);
engine.rootContext()->setContextProperty("AppInfo",AppInfo::getInstance());
engine.rootContext()->setContextProperty("SettingsHelper",SettingsHelper::getInstance());
FramelessHelper::Quick::registerTypes(&engine); FramelessHelper::Quick::registerTypes(&engine);
#ifdef FLUENTUI_BUILD_STATIC_LIB #ifdef FLUENTUI_BUILD_STATIC_LIB
FluentUI::getInstance()->registerTypes(&engine); FluentUI::getInstance()->registerTypes(&engine);
@ -58,7 +70,6 @@ FRAMELESSHELPER_USE_NAMESPACE
qmlRegisterType<CircularReveal>("example", 1, 0, "CircularReveal"); qmlRegisterType<CircularReveal>("example", 1, 0, "CircularReveal");
qmlRegisterType<FileWatcher>("example", 1, 0, "FileWatcher"); qmlRegisterType<FileWatcher>("example", 1, 0, "FileWatcher");
qmlRegisterType<FpsItem>("example", 1, 0, "FpsItem"); qmlRegisterType<FpsItem>("example", 1, 0, "FpsItem");
appInfo->init(&engine);
const QUrl url(QStringLiteral("qrc:/example/qml/App.qml")); const QUrl url(QStringLiteral("qrc:/example/qml/App.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) { &app, [url](QObject *obj, const QUrl &objUrl) {
@ -66,5 +77,9 @@ FRAMELESSHELPER_USE_NAMESPACE
QCoreApplication::exit(-1); QCoreApplication::exit(-1);
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
engine.load(url); engine.load(url);
return app.exec(); const int exec = QGuiApplication::exec();
if (exec == 931) {
QProcess::startDetached(qApp->applicationFilePath(), QStringList());
}
return exec;
} }

47
example/src/singleton.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef SINGLETON_H
#define SINGLETON_H
#include <QMutex>
#include <QScopedPointer>
#include <memory>
#include <mutex>
template <typename T>
class Singleton {
public:
static T* getInstance();
Singleton(const Singleton& other) = delete;
Singleton<T>& operator=(const Singleton& other) = delete;
private:
static std::mutex mutex;
static T* instance;
};
template <typename T>
std::mutex Singleton<T>::mutex;
template <typename T>
T* Singleton<T>::instance;
template <typename T>
T* Singleton<T>::getInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> locker(mutex);
if (instance == nullptr) {
instance = new T();
}
}
return instance;
}
#define SINGLETONG(Class) \
private: \
friend class Singleton<Class>; \
friend struct QScopedPointerDeleter<Class>; \
\
public: \
static Class* getInstance() { \
return Singleton<Class>::getInstance(); \
}
#endif // SINGLETON_H

31
src/FluRectangle.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "FluRectangle.h"
#include <QPainterPath>
#include <QSGClipNode>
#include <QQuickWindow>
FluRectangle::FluRectangle(QQuickItem* parent) : QQuickPaintedItem(parent){
setFlag(ItemHasContents, true);
color(QColor(255,255,255,255));
radius({0,0,0,0});
connect(this,&FluRectangle::colorChanged,this,[=]{update();});
connect(this,&FluRectangle::radiusChanged,this,[=]{update();});
}
void FluRectangle::paint(QPainter* painter){
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
QPainterPath path;
QRectF rect = boundingRect();
path.moveTo(rect.bottomRight() - QPointF(0, _radius[2]));
path.lineTo(rect.topRight() + QPointF(0, _radius[1]));
path.arcTo(QRectF(QPointF(rect.topRight() - QPointF(_radius[1] * 2, 0)), QSize(_radius[1] * 2, _radius[1] * 2)), 0, 90);
path.lineTo(rect.topLeft() + QPointF(_radius[0], 0));
path.arcTo(QRectF(QPointF(rect.topLeft()), QSize(_radius[0] * 2, _radius[0] * 2)), 90, 90);
path.lineTo(rect.bottomLeft() - QPointF(0, _radius[3]));
path.arcTo(QRectF(QPointF(rect.bottomLeft() - QPointF(0, _radius[3] * 2)), QSize(_radius[3] * 2, _radius[3] * 2)), 180, 90);
path.lineTo(rect.bottomLeft() + QPointF(_radius[2], 0));
path.arcTo(QRectF(QPointF(rect.bottomRight() - QPointF(_radius[2] * 2, _radius[2] * 2)), QSize(_radius[2] * 2, _radius[2] * 2)), 270, 90);
painter->fillPath(path,_color);
painter->restore();
}

20
src/FluRectangle.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef FLURECTANGLE_H
#define FLURECTANGLE_H
#include <QQuickItem>
#include <QQuickPaintedItem>
#include <QPainter>
#include "stdafx.h"
class FluRectangle : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY_AUTO(QColor,color)
Q_PROPERTY_AUTO(QList<int>,radius)
QML_NAMED_ELEMENT(FluRectangle)
public:
explicit FluRectangle(QQuickItem *parent = nullptr);
void paint(QPainter* painter) override;
};
#endif // FLURECTANGLE_H

View File

@ -9,10 +9,15 @@
#include <QFileInfo> #include <QFileInfo>
#include <QProcess> #include <QProcess>
#include <QDir> #include <QDir>
#include <QOpenGLContext>
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QTextDocument> #include <QTextDocument>
#include <qDebug>
#include <QQuickWindow>
FluTools::FluTools(QObject *parent):QObject{parent}{ FluTools::FluTools(QObject *parent):QObject{parent}{
} }
void FluTools::clipText(const QString& text){ void FluTools::clipText(const QString& text){
@ -159,3 +164,7 @@ void FluTools::showFileInFolder(QString path){
QProcess::execute("/usr/bin/osascript", {"-e", "tell application \"Finder\" to activate"}); QProcess::execute("/usr/bin/osascript", {"-e", "tell application \"Finder\" to activate"});
#endif #endif
} }
bool FluTools::isSoftware(){
return QQuickWindow::sceneGraphBackend() == "software";
}

View File

@ -46,6 +46,7 @@ public:
Q_INVOKABLE bool removeDir(QString dirPath); Q_INVOKABLE bool removeDir(QString dirPath);
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();
}; };
#endif // FLUTOOLS_H #endif // FLUTOOLS_H

View File

@ -2,10 +2,16 @@
#include <QMetaEnum> #include <QMetaEnum>
Node::Node(QObject *parent)
: QObject{parent}
{
}
FluTreeModel::FluTreeModel(QObject *parent) FluTreeModel::FluTreeModel(QObject *parent)
: QAbstractTableModel{parent} : QAbstractTableModel{parent}
{ {
dataSourceSize(0);
} }
int FluTreeModel::rowCount(const QModelIndex &parent) const { int FluTreeModel::rowCount(const QModelIndex &parent) const {
@ -27,35 +33,162 @@ QVariant FluTreeModel::data(const QModelIndex &index, int role) const {
}; };
QHash<int, QByteArray> FluTreeModel::roleNames() const { QHash<int, QByteArray> FluTreeModel::roleNames() const {
return { {Qt::DisplayRole, "display"} }; return { {Qt::DisplayRole, "modelData"} };
}; };
void FluTreeModel::setData(QList<QObject*> data){ void FluTreeModel::setData(QList<Node*> data){
beginResetModel(); beginResetModel();
_rows = data; _rows = data;
endResetModel(); endResetModel();
} }
void FluTreeModel::removeRows(int row,int count){ void FluTreeModel::removeRows(int row,int count){
if (row < 0 || row + count > _rows.size()) if (row < 0 || row + count > _rows.size() || count==0)
return; return;
beginRemoveRows(QModelIndex(),row, row + count - 1); // beginRemoveRows(QModelIndex(),row, row + count - 1);
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
_rows.removeAt(row); _rows.removeAt(row);
} }
endRemoveRows(); // endRemoveRows();
} }
void FluTreeModel::insertRows(int row,QList<QObject*> data){ void FluTreeModel::insertRows(int row,QList<Node*> data){
if (row < 0 || row > _rows.size()) if (row < 0 || row > _rows.size() || data.size() == 0)
return; return;;
beginInsertRows(QModelIndex(), row, row + data.size() - 1); // beginInsertRows(QModelIndex(), row, row + data.size() - 1);
for (const auto& item : data) { for (const auto& item : data) {
_rows.insert(row++, item); _rows.insert(row++, item);
} }
endInsertRows(); // endInsertRows();
} }
QObject* FluTreeModel::getRow(int row){ QObject* FluTreeModel::getRow(int row){
return _rows.at(row); return _rows.at(row);
} }
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());
foreach (auto c, children) {
auto child = c.toMap();
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);
Q_EMIT dataChanged(index(row+1,0),index(row+1+removeCount,0));
}
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);
Q_EMIT dataChanged(index(row+1,0),index(row+1+insertData.count(),0));
}
void FluTreeModel::dragAnddrop(int dragIndex,int dropIndex){
if(dragIndex == dropIndex+1){
return;
}
auto dragItem = _rows[dragIndex];
auto dropItem = _rows[dropIndex];
if (!beginMoveRows(QModelIndex(), dragIndex, dragIndex, QModelIndex(), dropIndex > dragIndex ? dropIndex+1 : dropIndex)) {
return;
}
if(dragItem->_parent == dropItem->_parent){
if(dragItem->hasChildren()){
}else{
QList<Node*>* children = &(dragItem->_parent->_children);
int srcIndex = children->indexOf(dragItem);
int destIndex = children->indexOf(dropItem);
children->move(srcIndex,destIndex>srcIndex? destIndex : destIndex +1);
_rows.move(dragIndex,dropIndex>dragIndex? dropIndex : dropIndex+1);
}
}else{
if(dragItem->hasChildren()){
}else{
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;
srcChildren->removeAt(srcIndex);
destChildren->insert(destIndex+1,dragItem);
_rows.move(dragIndex,dropIndex>dragIndex? dropIndex : dropIndex+1);
}
}
endMoveRows();
}

View File

@ -3,11 +3,59 @@
#include <QObject> #include <QObject>
#include <QAbstractTableModel> #include <QAbstractTableModel>
#include <QJsonArray>
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
#include "stdafx.h"
class Node : public QObject{
Q_OBJECT
Q_PROPERTY(QString key READ key CONSTANT)
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(int depth READ depth CONSTANT)
Q_PROPERTY(bool isExpanded READ isExpanded CONSTANT)
public:
explicit Node(QObject *parent = nullptr);
Q_INVOKABLE QString key(){return _key;};
Q_INVOKABLE QString title(){return _title;};
Q_INVOKABLE int depth(){return _depth;};
Q_INVOKABLE bool isExpanded(){return _isExpanded;};
Q_INVOKABLE bool hasChildren(){ return !_children.isEmpty();};
Q_INVOKABLE bool hideLineFooter(){
if(_parent){
auto childIndex = _parent->_children.indexOf(this);
if(childIndex==_parent->_children.count()-1){
return true;
}
if(_parent->_children.at(childIndex+1)->hasChildren()){
return true;
}
return false;
}
return false;
};
bool isShown(){
auto p = _parent;
while (p) {
if(!p->_isExpanded){
return false;
}
p = p->_parent;
}
return true;
}
public:
QString _key="";
QString _title="";
int _depth=0;
bool _isExpanded=true;
QList<Node*> _children;
Node* _parent = nullptr;
};
class FluTreeModel : public QAbstractTableModel class FluTreeModel : public QAbstractTableModel
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY_AUTO(int,dataSourceSize)
QML_NAMED_ELEMENT(FluTreeModel) QML_NAMED_ELEMENT(FluTreeModel)
QML_ADDED_IN_MINOR_VERSION(1) QML_ADDED_IN_MINOR_VERSION(1)
public: public:
@ -17,11 +65,17 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override; QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void removeRows(int row,int count); Q_INVOKABLE void removeRows(int row,int count);
Q_INVOKABLE void insertRows(int row,QList<QObject*> data); Q_INVOKABLE void insertRows(int row,QList<Node*> data);
Q_INVOKABLE QObject* getRow(int row); Q_INVOKABLE QObject* getRow(int row);
Q_INVOKABLE void setData(QList<QObject*> data); Q_INVOKABLE void setData(QList<Node*> data);
Q_INVOKABLE void setDataSource(QList<QMap<QString,QVariant>> data);
Q_INVOKABLE void collapse(int row);
Q_INVOKABLE void expand(int row);
Q_INVOKABLE void dragAnddrop(int dragIndex,int dropIndex);
private: private:
QList<QObject*> _rows; QList<Node*> _rows;
QList<Node*> _dataSource;
Node* _root = nullptr;
}; };
#endif // FLUTREEMODEL_H #endif // FLUTREEMODEL_H

View File

@ -57,8 +57,9 @@ void PropertyObserver::_propertyChange(){
} }
FluViewModel::FluViewModel(QObject *parent):QObject{parent}{ FluViewModel::FluViewModel(QObject *parent):QObject{parent}{
ViewModelManager::getInstance()->insertViewModel(this);
scope(FluViewModelType::Scope::Window); scope(FluViewModelType::Scope::Window);
target(nullptr);
ViewModelManager::getInstance()->insertViewModel(this);
} }
FluViewModel::~FluViewModel(){ FluViewModel::~FluViewModel(){

View File

@ -31,7 +31,7 @@ public:
QString getKey(); QString getKey();
private: private:
QObject* _window = nullptr; QObject* _window = nullptr;
QString _key; QString _key = "";
}; };
class PropertyObserver: public QObject{ class PropertyObserver: public QObject{
@ -42,9 +42,9 @@ public:
private: private:
Q_SLOT void _propertyChange(); Q_SLOT void _propertyChange();
private: private:
QString _name; QString _name = "";
QQmlProperty _property; QQmlProperty _property;
QObject* _model; QObject* _model = nullptr;
}; };

View File

@ -16,6 +16,7 @@
#include "FluTreeModel.h" #include "FluTreeModel.h"
#include "FluViewModel.h" #include "FluViewModel.h"
#include "Screenshot.h" #include "Screenshot.h"
#include "FluRectangle.h"
#include "QRCode.h" #include "QRCode.h"
int major = 1; int major = 1;
@ -55,6 +56,7 @@ void FluentUI::registerTypes(const char *uri){
qmlRegisterType<FluEvent>(uri,major,minor,"FluEvent"); qmlRegisterType<FluEvent>(uri,major,minor,"FluEvent");
qmlRegisterType<FluViewModel>(uri,major,minor,"FluViewModel"); qmlRegisterType<FluViewModel>(uri,major,minor,"FluViewModel");
qmlRegisterType<FluTreeModel>(uri,major,minor,"FluTreeModel"); qmlRegisterType<FluTreeModel>(uri,major,minor,"FluTreeModel");
qmlRegisterType<FluRectangle>(uri,major,minor,"FluRectangle");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/ColorPicker.qml"),uri,major,minor,"ColorPicker"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/ColorPicker.qml"),uri,major,minor,"ColorPicker");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/Content/Checkerboard.qml"),uri,major,minor,"Checkerboard"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/Content/Checkerboard.qml"),uri,major,minor,"Checkerboard");
@ -93,7 +95,6 @@ void FluentUI::registerTypes(const char *uri){
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluIconButton.qml"),uri,major,minor,"FluIconButton"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluIconButton.qml"),uri,major,minor,"FluIconButton");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluImage.qml"),uri,major,minor,"FluImage"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluImage.qml"),uri,major,minor,"FluImage");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluInfoBar.qml"),uri,major,minor,"FluInfoBar"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluInfoBar.qml"),uri,major,minor,"FluInfoBar");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluItem.qml"),uri,major,minor,"FluItem");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluItemDelegate.qml"),uri,major,minor,"FluItemDelegate"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluItemDelegate.qml"),uri,major,minor,"FluItemDelegate");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluMenu.qml"),uri,major,minor,"FluMenu"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluMenu.qml"),uri,major,minor,"FluMenu");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluMenuBar.qml"),uri,major,minor,"FluMenuBar"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluMenuBar.qml"),uri,major,minor,"FluMenuBar");
@ -120,7 +121,6 @@ void FluentUI::registerTypes(const char *uri){
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRadioButton.qml"),uri,major,minor,"FluRadioButton"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRadioButton.qml"),uri,major,minor,"FluRadioButton");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRadioButtons.qml"),uri,major,minor,"FluRadioButtons"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRadioButtons.qml"),uri,major,minor,"FluRadioButtons");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRatingControl.qml"),uri,major,minor,"FluRatingControl"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRatingControl.qml"),uri,major,minor,"FluRatingControl");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRectangle.qml"),uri,major,minor,"FluRectangle");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRemoteLoader.qml"),uri,major,minor,"FluRemoteLoader"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRemoteLoader.qml"),uri,major,minor,"FluRemoteLoader");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluScreenshot.qml"),uri,major,minor,"FluScreenshot"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluScreenshot.qml"),uri,major,minor,"FluScreenshot");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluScrollBar.qml"),uri,major,minor,"FluScrollBar"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluScrollBar.qml"),uri,major,minor,"FluScrollBar");
@ -150,7 +150,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"); qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluClip.qml"),uri,major,minor,"FluClip");
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

@ -2,7 +2,7 @@ import QtQuick 2.15
import QtGraphicalEffects 1.15 import QtGraphicalEffects 1.15
import FluentUI 1.0 import FluentUI 1.0
FluItem { Item {
id: control id: control
property color tintColor: Qt.rgba(1,1,1,1) property color tintColor: Qt.rgba(1,1,1,1)
property real tintOpacity: 0.65 property real tintOpacity: 0.65

View File

@ -2,7 +2,7 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import FluentUI 1.0 import FluentUI 1.0
FluItem { Item {
property bool autoPlay: true property bool autoPlay: true
property int loopTime: 2000 property int loopTime: 2000
property var model property var model

View File

@ -0,0 +1,17 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.0
import FluentUI 1.0
FluRectangle {
id:control
color: "#00000000"
layer.enabled: !FluTools.isSoftware()
layer.effect: OpacityMask{
maskSource: FluRectangle{
radius: control.radius
width: control.width
height: control.height
}
}
}

View File

@ -62,7 +62,7 @@ ComboBox {
bottomInset:1 bottomInset:1
rightInset:1 rightInset:1
background: FluTextBoxBackground{ background: FluTextBoxBackground{
border.width: 0 borderWidth: 0
inputItem: contentItem inputItem: contentItem
} }
Component.onCompleted: { Component.onCompleted: {

View File

@ -5,12 +5,12 @@ import FluentUI 1.0
Rectangle { Rectangle {
property real spacing property real spacing
property alias separatorHeight:separator.height property alias separatorHeight:separator.height
id:control
id:root
color:Qt.rgba(0,0,0,0) color:Qt.rgba(0,0,0,0)
height: spacing*2+separator.height height: spacing*2+separator.height
Rectangle{ Rectangle{
id:separator id:separator
clip: true
color: FluTheme.dark ? Qt.rgba(80/255,80/255,80/255,1) : Qt.rgba(210/255,210/255,210/255,1) color: FluTheme.dark ? Qt.rgba(80/255,80/255,80/255,1) : Qt.rgba(210/255,210/255,210/255,1)
width:parent.width width:parent.width
anchors.centerIn: parent anchors.centerIn: parent

View File

@ -70,14 +70,15 @@ Item {
left: layout_header.left left: layout_header.left
} }
width: parent.width width: parent.width
clip: true
visible: contentHeight+container.y !== 0 visible: contentHeight+container.y !== 0
height: contentHeight+container.y height: contentHeight+container.y
clip: true
Rectangle{ Rectangle{
id:container id:container
width: parent.width width: parent.width
height: parent.height height: parent.height
radius: 4 radius: 4
clip: true
color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
border.color: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1) border.color: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
y: -contentHeight y: -contentHeight

View File

@ -1,57 +0,0 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
Item{
property var radius:[0,0,0,0]
default property alias contentItem: container.data
id:control
Item{
id:container
width: control.width
height: control.height
opacity: 0
}
onWidthChanged: {
canvas.requestPaint()
}
onHeightChanged: {
canvas.requestPaint()
}
onRadiusChanged: {
canvas.requestPaint()
}
Canvas {
id: canvas
anchors.fill: parent
visible: false
onPaint: {
var ctx = getContext("2d");
var x = 0;
var y = 0;
var w = control.width;
var h = control.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.beginPath();
ctx.moveTo(x + radius[0], y);
ctx.lineTo(x + w - radius[1], y);
ctx.arcTo(x + w, y, x + w, y + radius[1], radius[1]);
ctx.lineTo(x + w, y + h - radius[2]);
ctx.arcTo(x + w, y + h, x + w - radius[2], y + h, radius[2]);
ctx.lineTo(x + radius[3], y + h);
ctx.arcTo(x, y + h, x, y + h - radius[3], radius[3]);
ctx.lineTo(x, y + radius[0]);
ctx.arcTo(x, y, x + radius[0], y, radius[0]);
ctx.closePath();
ctx.fillStyle = control.color;
ctx.fill();
ctx.restore();
}
}
OpacityMask {
anchors.fill: container
source: container
maskSource: canvas
}
}

View File

@ -112,8 +112,16 @@ Item {
id:com_panel_item_separatorr id:com_panel_item_separatorr
FluDivider{ FluDivider{
width: layout_list.width width: layout_list.width
spacing: model.spacing spacing: {
if(model){
return model.spacing
}
return 1
}
separatorHeight: { separatorHeight: {
if(!model){
return 1
}
if(model.parent){ if(model.parent){
return model.parent.isExpand ? model.size : 0 return model.parent.isExpand ? model.size : 0
} }
@ -200,6 +208,9 @@ Item {
verticalCenterOffset: -8 verticalCenterOffset: -8
} }
visible: { visible: {
if(!model){
return false
}
if(!model.isExpand){ if(!model.isExpand){
for(var i=0;i<model.children.length;i++){ for(var i=0;i<model.children.length;i++){
var item = model.children[i] var item = model.children[i]
@ -220,6 +231,12 @@ Item {
radius: 1.5 radius: 1.5
color: FluTheme.primaryColor.dark color: FluTheme.primaryColor.dark
visible: { visible: {
if(!model){
return false
}
if(!model.children){
return false
}
for(var i=0;i<model.children.length;i++){ for(var i=0;i<model.children.length;i++){
var item = model.children[i] var item = model.children[i]
if(item._idx === nav_list.currentIndex && !model.isExpand){ if(item._idx === nav_list.currentIndex && !model.isExpand){
@ -234,7 +251,7 @@ Item {
} }
FluIcon{ FluIcon{
id:item_icon_expand id:item_icon_expand
rotation: model.isExpand?0:180 rotation: model&&model.isExpand?0:180
iconSource:FluentIcons.ChevronUp iconSource:FluentIcons.ChevronUp
iconSize: 15 iconSize: 15
anchors{ anchors{
@ -279,7 +296,7 @@ Item {
id:com_icon id:com_icon
FluIcon{ FluIcon{
iconSource: { iconSource: {
if(model.icon){ if(model&&model.icon){
return model.icon return model.icon
} }
return 0 return 0
@ -299,7 +316,7 @@ Item {
Loader{ Loader{
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: { sourceComponent: {
if(model.cusIcon){ if(model&&model.cusIcon){
return model.cusIcon return model.cusIcon
} }
return com_icon return com_icon
@ -308,7 +325,12 @@ Item {
} }
FluText{ FluText{
id:item_title id:item_title
text:model.title text:{
if(model){
return model.title
}
return ""
}
visible: { visible: {
if(d.isCompactAndNotPanel){ if(d.isCompactAndNotPanel){
return false return false
@ -341,7 +363,7 @@ Item {
if(d.isCompact){ if(d.isCompact){
return undefined return undefined
} }
return model.showEdit ? model.editDelegate : undefined return model&&model.showEdit ? model.editDelegate : undefined
} }
onStatusChanged: { onStatusChanged: {
if(status === Loader.Ready){ if(status === Loader.Ready){
@ -377,13 +399,13 @@ Item {
} }
} }
height: { height: {
if(model.parent){ if(model&&model.parent){
return model.parent.isExpand ? 38 : 0 return model.parent.isExpand ? 38 : 0
} }
return 38 return 38
} }
visible: { visible: {
if(model.parent){ if(model&&model.parent){
return model.parent.isExpand ? true : false return model.parent.isExpand ? true : false
} }
return true return true
@ -497,7 +519,7 @@ Item {
id:com_icon id:com_icon
FluIcon{ FluIcon{
iconSource: { iconSource: {
if(model.icon){ if(model&&model.icon){
return model.icon return model.icon
} }
return 0 return 0
@ -517,7 +539,7 @@ Item {
Loader{ Loader{
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: { sourceComponent: {
if(model.cusIcon){ if(model&&model.cusIcon){
return model.cusIcon return model.cusIcon
} }
return com_icon return com_icon
@ -526,7 +548,12 @@ Item {
} }
FluText{ FluText{
id:item_title id:item_title
text:model.title text:{
if(model){
return model.title
}
return ""
}
visible: { visible: {
if(d.isCompactAndNotPanel){ if(d.isCompactAndNotPanel){
return false return false
@ -559,6 +586,9 @@ Item {
if(d.isCompact){ if(d.isCompact){
return undefined return undefined
} }
if(!model){
return undefined
}
return model.showEdit ? model.editDelegate : undefined return model.showEdit ? model.editDelegate : undefined
} }
onStatusChanged: { onStatusChanged: {
@ -591,7 +621,7 @@ Item {
verticalCenterOffset: isDot ? -8 : 0 verticalCenterOffset: isDot ? -8 : 0
} }
sourceComponent: { sourceComponent: {
if(model.infoBadge){ if(model&&model.infoBadge){
return model.infoBadge return model.infoBadge
} }
return undefined return undefined
@ -771,9 +801,11 @@ Item {
anchors.fill: loader_content anchors.fill: loader_content
onDropped: onDropped:
(drag)=>{ (drag)=>{
if(drag.source.modelData){
drag.source.modelData.dropped(drag) drag.source.modelData.dropped(drag)
} }
} }
}
Loader{ Loader{
id:loader_content id:loader_content
anchors{ anchors{
@ -938,6 +970,8 @@ Item {
property var _idx: index property var _idx: index
property int type: 0 property int type: 0
sourceComponent: { sourceComponent: {
if(model === null || !model)
return undefined
if(modelData instanceof FluPaneItem){ if(modelData instanceof FluPaneItem){
return com_panel_item return com_panel_item
} }
@ -953,6 +987,7 @@ Item {
if(modelData instanceof FluPaneItemEmpty){ if(modelData instanceof FluPaneItemEmpty){
return com_panel_item_empty return com_panel_item_empty
} }
return undefined
} }
} }
} }

View File

@ -19,7 +19,7 @@ ProgressBar{
color: control.backgroundColor color: control.backgroundColor
radius: d._radius radius: d._radius
} }
contentItem: FluItem { contentItem: FluClip {
clip: true clip: true
radius: [d._radius,d._radius,d._radius,d._radius] radius: [d._radius,d._radius,d._radius,d._radius]
Rectangle { Rectangle {

View File

@ -40,7 +40,7 @@ Button {
id: control id: control
enabled: !disabled enabled: !disabled
horizontalPadding:12 horizontalPadding:12
background: FluItem{ background: FluClip{
implicitWidth: 28 implicitWidth: 28
implicitHeight: 28 implicitHeight: 28
radius: [4,4,4,4] radius: [4,4,4,4]

View File

@ -1,71 +0,0 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
import FluentUI 1.0
Item{
property var radius:[0,0,0,0]
property color color : FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
property bool shadow: true
default property alias contentItem: container.data
id:control
onWidthChanged: {
canvas.requestPaint()
}
onHeightChanged: {
canvas.requestPaint()
}
onRadiusChanged: {
canvas.requestPaint()
}
FluShadow{
anchors.fill: container
radius: control.radius[0]
visible: {
if(control.radius[0] === control.radius[1] && control.radius[0] === control.radius[2] && control.radius[0] === control.radius[3] && control.shadow){
return true
}
return false
}
}
Rectangle{
id:container
width: control.width
height: control.height
opacity: 0
color:control.color
}
Canvas {
id: canvas
anchors.fill: parent
visible: false
onPaint: {
var ctx = getContext("2d")
var x = 0
var y = 0
var w = control.width
var h = control.height
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.save()
ctx.beginPath();
ctx.moveTo(x + radius[0], y)
ctx.lineTo(x + w - radius[1], y)
ctx.arcTo(x + w, y, x + w, y + radius[1], radius[1])
ctx.lineTo(x + w, y + h - radius[2])
ctx.arcTo(x + w, y + h, x + w - radius[2], y + h, radius[2])
ctx.lineTo(x + radius[3], y + h)
ctx.arcTo(x, y + h, x, y + h - radius[3], radius[3])
ctx.lineTo(x, y + radius[0])
ctx.arcTo(x, y, x + radius[0], y, radius[0])
ctx.closePath()
ctx.fillStyle = "#000000"
ctx.fill()
ctx.restore()
}
}
OpacityMask {
anchors.fill: container
source: container
maskSource: canvas
}
}

View File

@ -23,24 +23,16 @@ T.Slider {
radius: 12 radius: 12
} }
Rectangle{ Rectangle{
width: 24 width: radius*2
height: 24 height: radius*2
radius: 12 radius:{
if(control.pressed){
return 5
}
return control.hovered ? 7 : 6
}
color:FluTheme.dark ? FluTheme.primaryColor.lighter :FluTheme.primaryColor.dark color:FluTheme.dark ? FluTheme.primaryColor.lighter :FluTheme.primaryColor.dark
anchors.centerIn: parent anchors.centerIn: parent
scale: {
if(control.pressed){
return 4/10
}
return control.hovered ? 6/10 : 5/10
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
} }
} }
background: Item { background: Item {

View File

@ -68,7 +68,7 @@ T.SpinBox {
} }
} }
up.indicator: FluItem { up.indicator: FluRectangle {
x: control.mirrored ? 0 : control.width - width x: control.mirrored ? 0 : control.width - width
height: control.height height: control.height
implicitWidth: 32 implicitWidth: 32
@ -103,7 +103,7 @@ T.SpinBox {
} }
down.indicator: FluItem { down.indicator: FluRectangle {
x: control.mirrored ? parent.width - width : 0 x: control.mirrored ? parent.width - width : 0
height: control.height height: control.height
implicitWidth: 32 implicitWidth: 32

View File

@ -76,7 +76,7 @@ Item {
id:item_layout id:item_layout
width: item_container.width width: item_container.width
height: item_container.height height: item_container.height
FluItem{ Item{
id:item_container id:item_container
property real timestamp: new Date().getTime() property real timestamp: new Date().getTime()
height: tab_nav.height height: tab_nav.height
@ -92,7 +92,6 @@ Item {
} }
return Math.max(Math.min(d.maxEqualWidth,tab_nav.width/tab_nav.count),41 + item_btn_close.width) return Math.max(Math.min(d.maxEqualWidth,tab_nav.width/tab_nav.count),41 + item_btn_close.width)
} }
radius: [6,6,0,0]
Behavior on x { enabled: d.dragBehavior; NumberAnimation { duration: 200 } } Behavior on x { enabled: d.dragBehavior; NumberAnimation { duration: 200 } }
Behavior on y { enabled: d.dragBehavior; NumberAnimation { duration: 200 } } Behavior on y { enabled: d.dragBehavior; NumberAnimation { duration: 200 } }
MouseArea{ MouseArea{
@ -186,8 +185,9 @@ Item {
} }
} }
} }
Rectangle{ FluRectangle{
anchors.fill: parent anchors.fill: parent
radius: [6,6,0,0]
color: { color: {
if(FluTheme.dark){ if(FluTheme.dark){
if(item_mouse_hove.containsMouse || item_btn_close.hovered){ if(item_mouse_hove.containsMouse || item_btn_close.hovered){

View File

@ -141,13 +141,15 @@ Rectangle {
FluText { FluText {
id:item_text id:item_text
text: itemData text: itemData
anchors.fill: parent
leftPadding: 11
rightPadding: 11
topPadding: 6
bottomPadding: 6
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
anchors{
fill: parent
leftMargin: 11
rightMargin: 11
topMargin: 6
bottomMargin: 6
}
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
HoverHandler{ HoverHandler{
id: hover_handler id: hover_handler

View File

@ -1,13 +1,15 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
import FluentUI 1.0 import FluentUI 1.0
Rectangle{ FluClip{
property Item inputItem property Item inputItem
id:content property int borderWidth: 1
id:control
radius: [4,4,4,4]
Rectangle{
radius: 4 radius: 4
layer.enabled: true anchors.fill: parent
color: { color: {
if(inputItem.disabled){ if(inputItem.disabled){
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1) return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
@ -20,20 +22,14 @@ Rectangle{
} }
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1) return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
} }
layer.effect:OpacityMask { border.width: control.borderWidth
maskSource: Rectangle {
width: content.width
height: content.height
radius: 4
}
}
border.width: 1
border.color: { border.color: {
if(inputItem.disabled){ if(inputItem.disabled){
return FluTheme.dark ? Qt.rgba(73/255,73/255,73/255,1) : Qt.rgba(237/255,237/255,237/255,1) return FluTheme.dark ? Qt.rgba(73/255,73/255,73/255,1) : Qt.rgba(237/255,237/255,237/255,1)
} }
return FluTheme.dark ? Qt.rgba(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1) return FluTheme.dark ? Qt.rgba(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
} }
}
Rectangle{ Rectangle{
width: parent.width width: parent.width
height: inputItem.activeFocus ? 2 : 1 height: inputItem.activeFocus ? 2 : 1

View File

@ -75,12 +75,12 @@ Button {
} }
return borderNormalColor return borderNormalColor
} }
Rectangle { FluIcon {
width: parent.height width: parent.height
x:checked ? control_backgound.width-width : 0 x:checked ? control_backgound.width-width : 0
height: width
radius: width/2
scale: hovered&enabled ? 7/10 : 6/10 scale: hovered&enabled ? 7/10 : 6/10
iconSource: FluentIcons.FullCircleMask
iconSize: 20
color: { color: {
if(!enabled){ if(!enabled){
return dotDisableColor return dotDisableColor
@ -93,12 +93,7 @@ Button {
Behavior on x { Behavior on x {
enabled: FluTheme.enableAnimation enabled: FluTheme.enableAnimation
NumberAnimation { NumberAnimation {
easing.type: Easing.OutCubic duration: 167
}
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation {
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }

View File

@ -1,22 +0,0 @@
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 int __childIndex: 0
property bool __expanded:{
var p = __parent;
while (p) {
if(!p.isExpanded){
return false
}
p = p.__parent;
}
return true
}
property int index: 0
}

View File

@ -9,55 +9,19 @@ Item {
property int currentIndex : -1 property int currentIndex : -1
property var dataSource property var dataSource
property bool showLine: true property bool showLine: true
property bool draggable: false
property color lineColor: FluTheme.dark ? Qt.rgba(111/255,111/255,111/255,1) : Qt.rgba(217/255,217/255,217/255,1) property color lineColor: FluTheme.dark ? Qt.rgba(111/255,111/255,111/255,1) : Qt.rgba(217/255,217/255,217/255,1)
id:control id:control
QtObject { QtObject {
id:d id:d
property var rowData: [] property var current
function handleTree(treeData) { property int dropIndex: -1
var comItem = Qt.createComponent("FluTreeItem.qml"); property int dragIndex: -1
if (comItem.status !== Component.Ready) { property color hitColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
return
}
var stack = []
var rawData = []
for (var item of treeData) {
stack.push({node:item,depth:0,isExpanded:true,__parent:undefined,__childIndex:0})
}
stack = stack.reverse()
var index =0
while (stack.length > 0) {
const { node, depth,isExpanded,__parent,__childIndex} = stack.pop();
node.depth = depth;
node.isExpanded = isExpanded;
node.__parent = __parent;
node.__childIndex = __childIndex;
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.__childIndex = node.__childIndex
objItem.index = index
index = index + 1;
rawData.push(objItem)
if (node.children && node.children.length > 0) {
const children = node.children.reverse();
var childIndex = children.length-1
for (const child of children) {
stack.push({ node: child, depth: depth + 1,isExpanded:true,__parent:objItem,__childIndex:childIndex});
childIndex=childIndex-1;
}
}
}
return rawData
}
} }
onDataSourceChanged: { onDataSourceChanged: {
d.rowData = d.handleTree(dataSource) tree_model.setDataSource(dataSource)
tree_model.setData(d.rowData)
} }
FluTreeModel{ FluTreeModel{
id:tree_model id:tree_model
@ -69,65 +33,205 @@ Item {
table_view.forceLayout() table_view.forceLayout()
} }
} }
TableView{ Component{
id:table_view id:com_item_container
ScrollBar.horizontal: FluScrollBar{} Item{
ScrollBar.vertical: FluScrollBar{} signal reused
boundsBehavior: Flickable.StopAtBounds signal pooled
model: tree_model
clip: true onReused: {
console.debug("----->onReused")
}
onPooled: {
console.debug("----->onPooled")
}
property bool isCurrent: d.current === itemModel
id:item_container
width: {
var w = 46 + item_layout_text.width + 30*itemModel.depth
if(control.width>w){
return control.width
}
return w
}
height: 30
function toggle(){
if(itemModel.isExpanded){
tree_model.collapse(rowIndex)
}else{
tree_model.expand(rowIndex)
}
}
Rectangle{
anchors.fill: parent anchors.fill: parent
onContentYChanged:{ radius: 4
timer_refresh.restart() anchors.leftMargin: 6
anchors.rightMargin: 6
border.color: d.hitColor
border.width: d.dragIndex === rowIndex ? 1 : 0
color: {
if(FluTheme.dark){
if(isCurrent){
return Qt.rgba(1,1,1,0.03)
} }
reuseItems: false if(item_mouse.containsMouse){
delegate: Item { return Qt.rgba(1,1,1,0.03)
property bool hasChildren: {
if(display.children){
return true
} }
return false return Qt.rgba(0,0,0,0)
}else{
if(isCurrent){
return Qt.rgba(0,0,0,0.06)
}
if(item_mouse.containsMouse){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
}
}
}
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
} }
property var itemData: display
property bool vlineVisible: display.depth !== 0 && control.showLine
property bool hlineVisible: display.depth !== 0 && control.showLine && !hasChildren
property bool isLastIndex : {
if(display.__parent && display.__parent.children){
return display.__childIndex === display.__parent.children.length-1
} }
return false
} }
property bool isCurrent: control.currentIndex === row
implicitWidth: 46 + item_layout_text.width + 30*display.depth
implicitHeight: 30
Rectangle{ Rectangle{
width: 1 width: 1
color: control.lineColor color: control.lineColor
visible: hlineVisible visible: itemModel.depth !== 0 && control.showLine && !itemModel.hasChildren()
height: isLastIndex ? parent.height/2 : parent.height height: itemModel.hideLineFooter() ? parent.height/2 : parent.height
anchors{ anchors{
top: parent.top top: parent.top
right: layout_row.left right: layout_row.left
rightMargin: -9
} }
} }
Rectangle{ Rectangle{
height: 1 height: 1
color: control.lineColor color: control.lineColor
visible: hlineVisible visible: itemModel.depth !== 0 && control.showLine && !itemModel.hasChildren()
width: 18 width: 18
anchors{ anchors{
right: layout_row.left right: layout_row.left
rightMargin: -18 rightMargin: -27
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
} }
Repeater{ Repeater{
model: Math.max(display.depth-1,0) model: Math.max(itemModel.depth-1,0)
delegate: Rectangle{ delegate: Rectangle{
required property int index required property int index
width: 1 width: 1
color: control.lineColor color: control.lineColor
visible: vlineVisible visible: itemModel.depth !== 0 && control.showLine
anchors{ anchors{
top:parent.top top:parent.top
bottom: parent.bottom bottom: parent.bottom
@ -140,101 +244,32 @@ Item {
id:layout_row id:layout_row
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 14 + 30*display.depth anchors.leftMargin: 14 + 30*itemModel.depth
FluIconButton{ FluIconButton{
Layout.preferredWidth: 20 Layout.preferredWidth: 20
Layout.preferredHeight: 20 Layout.preferredHeight: 20
enabled: opacity enabled: opacity
opacity: hasChildren opacity: itemModel.hasChildren()
contentItem: FluIcon{ contentItem: FluIcon{
rotation: itemData.isExpanded?0:-90 rotation: itemModel.isExpanded?0:-90
iconSource:FluentIcons.ChevronDown iconSource:FluentIcons.ChevronDown
iconSize: 16 iconSize: 16
Behavior on rotation{
NumberAnimation{
duration: FluTheme.enableAnimation ? 167 : 0
easing.type: Easing.OutCubic
}
}
} }
onClicked: { onClicked: {
var isExpanded = !itemData.isExpanded item_container.toggle()
itemData.isExpanded = isExpanded
var i,obj
if(isExpanded){
for( i=0;i<d.rowData.length;i++){
obj = d.rowData[i]
if(obj === itemData){
var data = []
for(var j=i+1;j<d.rowData.length;j++){
obj = d.rowData[j]
if(obj.depth === itemData.depth){
break
}
if(obj.__expanded){
data.push(obj)
} }
} }
tree_model.insertRows(row+1,data) Item{
break
}
}
}else{
var removeCount = 0
for( i=row+1;i<tree_model.rowCount();i++){
obj = tree_model.getRow(i)
if(obj.depth === itemData.depth){
break
}
removeCount = removeCount + 1;
}
tree_model.removeRows(row+1,removeCount)
}
}
}
Rectangle{
id:item_layout_text id:item_layout_text
radius: 4
Layout.preferredWidth: item_text.implicitWidth+14 Layout.preferredWidth: item_text.implicitWidth+14
Layout.preferredHeight:item_text.implicitHeight+14 Layout.preferredHeight:item_text.implicitHeight+14
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Rectangle{
width: 3
height: 18
radius: 1.5
color: FluTheme.primaryColor.dark
visible: isCurrent
anchors{
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_text_mousearea
anchors.fill: parent
hoverEnabled: true
onClicked: {
control.currentIndex = row
}
}
color: {
if(FluTheme.dark){
if(item_text_mousearea.containsMouse || isCurrent){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
}else{
if(item_text_mousearea.containsMouse || isCurrent){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
}
}
FluText { FluText {
id:item_text id:item_text
text: display.title text: itemModel.title
anchors.centerIn: parent anchors.centerIn: parent
color:{ color:{
if(item_text_mousearea.pressed){ if(item_mouse.pressed){
return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120 return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
} }
return FluTheme.dark ? FluColors.White : FluColors.Grey220 return FluTheme.dark ? FluColors.White : FluColors.Grey220
@ -244,8 +279,58 @@ Item {
} }
} }
} }
ScrollView{
anchors.fill: parent
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
TableView{
id:table_view
ScrollBar.horizontal: FluScrollBar{}
ScrollBar.vertical: FluScrollBar{}
boundsBehavior: Flickable.StopAtBounds
model: tree_model
clip: true
anchors.fill: parent
onContentYChanged:{
timer_refresh.restart()
}
onWidthChanged: {
timer_refresh.restart()
}
delegate: Item {
id:item_control
implicitWidth: item_loader_container.width
implicitHeight: item_loader_container.height
TableView.onReused: {
item_loader_container.item.reused()
}
TableView.onPooled: {
item_loader_container.item.pooled()
}
Loader{
property var itemControl: item_control
property var itemModel: modelData
property int rowIndex: row
id:item_loader_container
width: item.width
height: item.height
sourceComponent: {
if(modelData)
return com_item_container
return undefined
}
}
}
}
Loader{
id:loader_container
property var itemControl
property var itemModel
}
}
function count(){ function count(){
return d.rowData.length return tree_model.dataSourceSize
} }
function visibleCount(){ function visibleCount(){
return table_view.rows return table_view.rows

View File

@ -39,7 +39,6 @@ FluIcon 1.0 Controls/FluIcon.qml
FluIconButton 1.0 Controls/FluIconButton.qml FluIconButton 1.0 Controls/FluIconButton.qml
FluImage 1.0 Controls/FluImage.qml FluImage 1.0 Controls/FluImage.qml
FluInfoBar 1.0 Controls/FluInfoBar.qml FluInfoBar 1.0 Controls/FluInfoBar.qml
FluItem 1.0 Controls/FluItem.qml
FluItemDelegate 1.0 Controls/FluItemDelegate.qml FluItemDelegate 1.0 Controls/FluItemDelegate.qml
FluMenu 1.0 Controls/FluMenu.qml FluMenu 1.0 Controls/FluMenu.qml
FluMenuBar 1.0 Controls/FluMenuBar.qml FluMenuBar 1.0 Controls/FluMenuBar.qml
@ -66,7 +65,6 @@ FluQRCode 1.0 Controls/FluQRCode.qml
FluRadioButton 1.0 Controls/FluRadioButton.qml FluRadioButton 1.0 Controls/FluRadioButton.qml
FluRadioButtons 1.0 Controls/FluRadioButtons.qml FluRadioButtons 1.0 Controls/FluRadioButtons.qml
FluRatingControl 1.0 Controls/FluRatingControl.qml FluRatingControl 1.0 Controls/FluRatingControl.qml
FluRectangle 1.0 Controls/FluRectangle.qml
FluRemoteLoader 1.0 Controls/FluRemoteLoader.qml FluRemoteLoader 1.0 Controls/FluRemoteLoader.qml
FluScreenshot 1.0 Controls/FluScreenshot.qml FluScreenshot 1.0 Controls/FluScreenshot.qml
FluScrollBar 1.0 Controls/FluScrollBar.qml FluScrollBar 1.0 Controls/FluScrollBar.qml
@ -96,5 +94,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 FluClip 1.0 Controls/FluClip.qml
plugin fluentuiplugin plugin fluentuiplugin

View File

@ -33,7 +33,6 @@
<file>FluentUI/Controls/FluIconButton.qml</file> <file>FluentUI/Controls/FluIconButton.qml</file>
<file>FluentUI/Controls/FluImage.qml</file> <file>FluentUI/Controls/FluImage.qml</file>
<file>FluentUI/Controls/FluInfoBar.qml</file> <file>FluentUI/Controls/FluInfoBar.qml</file>
<file>FluentUI/Controls/FluItem.qml</file>
<file>FluentUI/Controls/FluItemDelegate.qml</file> <file>FluentUI/Controls/FluItemDelegate.qml</file>
<file>FluentUI/Controls/FluMenu.qml</file> <file>FluentUI/Controls/FluMenu.qml</file>
<file>FluentUI/Controls/FluMenuBar.qml</file> <file>FluentUI/Controls/FluMenuBar.qml</file>
@ -62,7 +61,6 @@
<file>FluentUI/Controls/FluRadioButtons.qml</file> <file>FluentUI/Controls/FluRadioButtons.qml</file>
<file>FluentUI/Controls/FluRangeSlider.qml</file> <file>FluentUI/Controls/FluRangeSlider.qml</file>
<file>FluentUI/Controls/FluRatingControl.qml</file> <file>FluentUI/Controls/FluRatingControl.qml</file>
<file>FluentUI/Controls/FluRectangle.qml</file>
<file>FluentUI/Controls/FluRemoteLoader.qml</file> <file>FluentUI/Controls/FluRemoteLoader.qml</file>
<file>FluentUI/Controls/FluScreenshot.qml</file> <file>FluentUI/Controls/FluScreenshot.qml</file>
<file>FluentUI/Controls/FluScrollablePage.qml</file> <file>FluentUI/Controls/FluScrollablePage.qml</file>
@ -96,6 +94,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> <file>FluentUI/Controls/FluClip.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -2,7 +2,7 @@ import QtQuick
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import FluentUI import FluentUI
FluItem { Item {
id: control id: control
property color tintColor: Qt.rgba(1,1,1,1) property color tintColor: Qt.rgba(1,1,1,1)
property real tintOpacity: 0.65 property real tintOpacity: 0.65

View File

@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import FluentUI import FluentUI
FluItem { Item {
property bool autoPlay: true property bool autoPlay: true
property int loopTime: 2000 property int loopTime: 2000
property var model property var model

View File

@ -0,0 +1,19 @@
import QtQuick
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
import FluentUI
FluRectangle {
id:control
color: "#00000000"
layer.enabled: !FluTools.isSoftware()
layer.effect: OpacityMask{
maskSource: ShaderEffectSource{
sourceItem: FluRectangle{
radius: control.radius
width: control.width
height: control.height
}
}
}
}

View File

@ -62,7 +62,7 @@ ComboBox {
bottomInset:1 bottomInset:1
rightInset:1 rightInset:1
background: FluTextBoxBackground{ background: FluTextBoxBackground{
border.width: 0 borderWidth: 0
inputItem: contentItem inputItem: contentItem
} }
Component.onCompleted: { Component.onCompleted: {

View File

@ -5,12 +5,12 @@ import FluentUI
Rectangle { Rectangle {
property real spacing property real spacing
property alias separatorHeight:separator.height property alias separatorHeight:separator.height
id:control
id:root
color:Qt.rgba(0,0,0,0) color:Qt.rgba(0,0,0,0)
height: spacing*2+separator.height height: spacing*2+separator.height
Rectangle{ Rectangle{
id:separator id:separator
clip: true
color: FluTheme.dark ? Qt.rgba(80/255,80/255,80/255,1) : Qt.rgba(210/255,210/255,210/255,1) color: FluTheme.dark ? Qt.rgba(80/255,80/255,80/255,1) : Qt.rgba(210/255,210/255,210/255,1)
width:parent.width width:parent.width
anchors.centerIn: parent anchors.centerIn: parent

View File

@ -78,6 +78,7 @@ Item {
width: parent.width width: parent.width
height: parent.height height: parent.height
radius: 4 radius: 4
clip: true
color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1) color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
border.color: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1) border.color: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1)
y: -contentHeight y: -contentHeight

View File

@ -1,57 +0,0 @@
import QtQuick
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
Item{
property var radius:[0,0,0,0]
default property alias contentItem: container.data
id:control
Item{
id:container
width: control.width
height: control.height
opacity: 0
}
onWidthChanged: {
canvas.requestPaint()
}
onHeightChanged: {
canvas.requestPaint()
}
onRadiusChanged: {
canvas.requestPaint()
}
Canvas {
id: canvas
anchors.fill: parent
visible: false
onPaint: {
var ctx = getContext("2d");
var x = 0;
var y = 0;
var w = control.width;
var h = control.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.beginPath();
ctx.moveTo(x + radius[0], y);
ctx.lineTo(x + w - radius[1], y);
ctx.arcTo(x + w, y, x + w, y + radius[1], radius[1]);
ctx.lineTo(x + w, y + h - radius[2]);
ctx.arcTo(x + w, y + h, x + w - radius[2], y + h, radius[2]);
ctx.lineTo(x + radius[3], y + h);
ctx.arcTo(x, y + h, x, y + h - radius[3], radius[3]);
ctx.lineTo(x, y + radius[0]);
ctx.arcTo(x, y, x + radius[0], y, radius[0]);
ctx.closePath();
ctx.fillStyle = control.color;
ctx.fill();
ctx.restore();
}
}
OpacityMask {
anchors.fill: container
source: container
maskSource: canvas
}
}

View File

@ -113,8 +113,16 @@ Item {
id:com_panel_item_separatorr id:com_panel_item_separatorr
FluDivider{ FluDivider{
width: layout_list.width width: layout_list.width
spacing: model.spacing spacing: {
if(model){
return model.spacing
}
return 1
}
separatorHeight: { separatorHeight: {
if(!model){
return 1
}
if(model.parent){ if(model.parent){
return model.parent.isExpand ? model.size : 0 return model.parent.isExpand ? model.size : 0
} }
@ -201,6 +209,9 @@ Item {
verticalCenterOffset: -8 verticalCenterOffset: -8
} }
visible: { visible: {
if(!model){
return false
}
if(!model.isExpand){ if(!model.isExpand){
for(var i=0;i<model.children.length;i++){ for(var i=0;i<model.children.length;i++){
var item = model.children[i] var item = model.children[i]
@ -221,6 +232,12 @@ Item {
radius: 1.5 radius: 1.5
color: FluTheme.primaryColor.dark color: FluTheme.primaryColor.dark
visible: { visible: {
if(!model){
return false
}
if(!model.children){
return false
}
for(var i=0;i<model.children.length;i++){ for(var i=0;i<model.children.length;i++){
var item = model.children[i] var item = model.children[i]
if(item._idx === nav_list.currentIndex && !model.isExpand){ if(item._idx === nav_list.currentIndex && !model.isExpand){
@ -235,7 +252,7 @@ Item {
} }
FluIcon{ FluIcon{
id:item_icon_expand id:item_icon_expand
rotation: model.isExpand?0:180 rotation: model&&model.isExpand?0:180
iconSource:FluentIcons.ChevronUp iconSource:FluentIcons.ChevronUp
iconSize: 15 iconSize: 15
anchors{ anchors{
@ -280,7 +297,7 @@ Item {
id:com_icon id:com_icon
FluIcon{ FluIcon{
iconSource: { iconSource: {
if(model.icon){ if(model&&model.icon){
return model.icon return model.icon
} }
return 0 return 0
@ -300,7 +317,7 @@ Item {
Loader{ Loader{
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: { sourceComponent: {
if(model.cusIcon){ if(model&&model.cusIcon){
return model.cusIcon return model.cusIcon
} }
return com_icon return com_icon
@ -309,7 +326,12 @@ Item {
} }
FluText{ FluText{
id:item_title id:item_title
text:model.title text:{
if(model){
return model.title
}
return ""
}
visible: { visible: {
if(d.isCompactAndNotPanel){ if(d.isCompactAndNotPanel){
return false return false
@ -342,7 +364,7 @@ Item {
if(d.isCompact){ if(d.isCompact){
return undefined return undefined
} }
return model.showEdit ? model.editDelegate : undefined return model&&model.showEdit ? model.editDelegate : undefined
} }
onStatusChanged: { onStatusChanged: {
if(status === Loader.Ready){ if(status === Loader.Ready){
@ -378,13 +400,13 @@ Item {
} }
} }
height: { height: {
if(model.parent){ if(model&&model.parent){
return model.parent.isExpand ? 38 : 0 return model.parent.isExpand ? 38 : 0
} }
return 38 return 38
} }
visible: { visible: {
if(model.parent){ if(model&&model.parent){
return model.parent.isExpand ? true : false return model.parent.isExpand ? true : false
} }
return true return true
@ -498,7 +520,7 @@ Item {
id:com_icon id:com_icon
FluIcon{ FluIcon{
iconSource: { iconSource: {
if(model.icon){ if(model&&model.icon){
return model.icon return model.icon
} }
return 0 return 0
@ -518,7 +540,7 @@ Item {
Loader{ Loader{
anchors.centerIn: parent anchors.centerIn: parent
sourceComponent: { sourceComponent: {
if(model.cusIcon){ if(model&&model.cusIcon){
return model.cusIcon return model.cusIcon
} }
return com_icon return com_icon
@ -527,7 +549,12 @@ Item {
} }
FluText{ FluText{
id:item_title id:item_title
text:model.title text:{
if(model){
return model.title
}
return ""
}
visible: { visible: {
if(d.isCompactAndNotPanel){ if(d.isCompactAndNotPanel){
return false return false
@ -560,6 +587,9 @@ Item {
if(d.isCompact){ if(d.isCompact){
return undefined return undefined
} }
if(!model){
return undefined
}
return model.showEdit ? model.editDelegate : undefined return model.showEdit ? model.editDelegate : undefined
} }
onStatusChanged: { onStatusChanged: {
@ -592,7 +622,7 @@ Item {
verticalCenterOffset: isDot ? -8 : 0 verticalCenterOffset: isDot ? -8 : 0
} }
sourceComponent: { sourceComponent: {
if(model.infoBadge){ if(model&&model.infoBadge){
return model.infoBadge return model.infoBadge
} }
return undefined return undefined
@ -772,9 +802,11 @@ Item {
anchors.fill: loader_content anchors.fill: loader_content
onDropped: onDropped:
(drag)=>{ (drag)=>{
if(drag.source.modelData){
drag.source.modelData.dropped(drag) drag.source.modelData.dropped(drag)
} }
} }
}
Loader{ Loader{
id:loader_content id:loader_content
anchors{ anchors{
@ -939,6 +971,8 @@ Item {
property var _idx: index property var _idx: index
property int type: 0 property int type: 0
sourceComponent: { sourceComponent: {
if(model === null || !model)
return undefined
if(modelData instanceof FluPaneItem){ if(modelData instanceof FluPaneItem){
return com_panel_item return com_panel_item
} }
@ -954,6 +988,7 @@ Item {
if(modelData instanceof FluPaneItemEmpty){ if(modelData instanceof FluPaneItemEmpty){
return com_panel_item_empty return com_panel_item_empty
} }
return undefined
} }
} }
} }

View File

@ -19,7 +19,7 @@ ProgressBar{
color: control.backgroundColor color: control.backgroundColor
radius: d._radius radius: d._radius
} }
contentItem: FluItem { contentItem: FluClip {
clip: true clip: true
radius: [d._radius,d._radius,d._radius,d._radius] radius: [d._radius,d._radius,d._radius,d._radius]
Rectangle { Rectangle {

View File

@ -41,7 +41,7 @@ Button {
id: control id: control
enabled: !disabled enabled: !disabled
horizontalPadding:12 horizontalPadding:12
background: FluItem{ background: FluClip{
implicitWidth: 28 implicitWidth: 28
implicitHeight: 28 implicitHeight: 28
radius: [4,4,4,4] radius: [4,4,4,4]

View File

@ -1,71 +0,0 @@
import QtQuick
import QtQuick.Controls
import Qt5Compat.GraphicalEffects
import FluentUI
Item{
property var radius:[0,0,0,0]
property color color : FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
property bool shadow: true
default property alias contentItem: container.data
id:control
onWidthChanged: {
canvas.requestPaint()
}
onHeightChanged: {
canvas.requestPaint()
}
onRadiusChanged: {
canvas.requestPaint()
}
FluShadow{
anchors.fill: container
radius: control.radius[0]
visible: {
if(control.radius[0] === control.radius[1] && control.radius[0] === control.radius[2] && control.radius[0] === control.radius[3] && control.shadow){
return true
}
return false
}
}
Rectangle{
id:container
width: control.width
height: control.height
opacity: 0
color:control.color
}
Canvas {
id: canvas
anchors.fill: parent
visible: false
onPaint: {
var ctx = getContext("2d")
var x = 0
var y = 0
var w = control.width
var h = control.height
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.save()
ctx.beginPath();
ctx.moveTo(x + radius[0], y)
ctx.lineTo(x + w - radius[1], y)
ctx.arcTo(x + w, y, x + w, y + radius[1], radius[1])
ctx.lineTo(x + w, y + h - radius[2])
ctx.arcTo(x + w, y + h, x + w - radius[2], y + h, radius[2])
ctx.lineTo(x + radius[3], y + h)
ctx.arcTo(x, y + h, x, y + h - radius[3], radius[3])
ctx.lineTo(x, y + radius[0])
ctx.arcTo(x, y, x + radius[0], y, radius[0])
ctx.closePath()
ctx.fillStyle = "#000000"
ctx.fill()
ctx.restore()
}
}
OpacityMask {
anchors.fill: container
source: container
maskSource: canvas
}
}

View File

@ -24,24 +24,16 @@ T.Slider {
radius: 12 radius: 12
} }
Rectangle{ Rectangle{
width: 24 width: radius*2
height: 24 height: radius*2
radius: 12 radius:{
if(control.pressed){
return 5
}
return control.hovered ? 7 : 6
}
color:FluTheme.dark ? FluTheme.primaryColor.lighter :FluTheme.primaryColor.dark color:FluTheme.dark ? FluTheme.primaryColor.lighter :FluTheme.primaryColor.dark
anchors.centerIn: parent anchors.centerIn: parent
scale: {
if(control.pressed){
return 4/10
}
return control.hovered ? 6/10 : 5/10
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
} }
} }
background: Item { background: Item {

View File

@ -69,7 +69,7 @@ T.SpinBox {
} }
} }
up.indicator: FluItem { up.indicator: FluRectangle {
x: control.mirrored ? 0 : control.width - width x: control.mirrored ? 0 : control.width - width
height: control.height height: control.height
implicitWidth: 32 implicitWidth: 32
@ -104,7 +104,7 @@ T.SpinBox {
} }
down.indicator: FluItem { down.indicator: FluRectangle {
x: control.mirrored ? parent.width - width : 0 x: control.mirrored ? parent.width - width : 0
height: control.height height: control.height
implicitWidth: 32 implicitWidth: 32

View File

@ -76,7 +76,7 @@ Item {
id:item_layout id:item_layout
width: item_container.width width: item_container.width
height: item_container.height height: item_container.height
FluItem{ Item{
id:item_container id:item_container
property real timestamp: new Date().getTime() property real timestamp: new Date().getTime()
height: tab_nav.height height: tab_nav.height
@ -92,7 +92,6 @@ Item {
} }
return Math.max(Math.min(d.maxEqualWidth,tab_nav.width/tab_nav.count),41 + item_btn_close.width) return Math.max(Math.min(d.maxEqualWidth,tab_nav.width/tab_nav.count),41 + item_btn_close.width)
} }
radius: [6,6,0,0]
Behavior on x { enabled: d.dragBehavior; NumberAnimation { duration: 200 } } Behavior on x { enabled: d.dragBehavior; NumberAnimation { duration: 200 } }
Behavior on y { enabled: d.dragBehavior; NumberAnimation { duration: 200 } } Behavior on y { enabled: d.dragBehavior; NumberAnimation { duration: 200 } }
MouseArea{ MouseArea{
@ -186,8 +185,9 @@ Item {
} }
} }
} }
Rectangle{ FluRectangle{
anchors.fill: parent anchors.fill: parent
radius: [6,6,0,0]
color: { color: {
if(FluTheme.dark){ if(FluTheme.dark){
if(item_mouse_hove.containsMouse || item_btn_close.hovered){ if(item_mouse_hove.containsMouse || item_btn_close.hovered){

View File

@ -143,13 +143,15 @@ Rectangle {
FluText { FluText {
id:item_text id:item_text
text: itemData text: itemData
anchors.fill: parent
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
leftPadding: 11 anchors{
rightPadding: 11 fill: parent
topPadding: 6 leftMargin: 11
bottomPadding: 6 rightMargin: 11
topMargin: 6
bottomMargin: 6
}
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
HoverHandler{ HoverHandler{
id: hover_handler id: hover_handler

View File

@ -1,13 +1,15 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Qt5Compat.GraphicalEffects
import FluentUI import FluentUI
Rectangle{ FluClip{
property Item inputItem property Item inputItem
id:content property int borderWidth: 1
id:control
radius: [4,4,4,4]
Rectangle{
radius: 4 radius: 4
layer.enabled: true anchors.fill: parent
color: { color: {
if(inputItem.disabled){ if(inputItem.disabled){
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1) return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
@ -20,20 +22,14 @@ Rectangle{
} }
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1) return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
} }
layer.effect:OpacityMask { border.width: control.borderWidth
maskSource: Rectangle {
width: content.width
height: content.height
radius: 4
}
}
border.width: 1
border.color: { border.color: {
if(inputItem.disabled){ if(inputItem.disabled){
return FluTheme.dark ? Qt.rgba(73/255,73/255,73/255,1) : Qt.rgba(237/255,237/255,237/255,1) return FluTheme.dark ? Qt.rgba(73/255,73/255,73/255,1) : Qt.rgba(237/255,237/255,237/255,1)
} }
return FluTheme.dark ? Qt.rgba(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1) return FluTheme.dark ? Qt.rgba(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
} }
}
Rectangle{ Rectangle{
width: parent.width width: parent.width
height: inputItem.activeFocus ? 2 : 1 height: inputItem.activeFocus ? 2 : 1

View File

@ -76,12 +76,12 @@ Button {
} }
return borderNormalColor return borderNormalColor
} }
Rectangle { FluIcon {
width: parent.height width: parent.height
x:checked ? control_backgound.width-width : 0 x:checked ? control_backgound.width-width : 0
height: width
radius: width/2
scale: hovered&enabled ? 7/10 : 6/10 scale: hovered&enabled ? 7/10 : 6/10
iconSource: FluentIcons.FullCircleMask
iconSize: 20
color: { color: {
if(!enabled){ if(!enabled){
return dotDisableColor return dotDisableColor
@ -94,12 +94,7 @@ Button {
Behavior on x { Behavior on x {
enabled: FluTheme.enableAnimation enabled: FluTheme.enableAnimation
NumberAnimation { NumberAnimation {
easing.type: Easing.OutCubic duration: 167
}
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation {
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }

View File

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

View File

@ -9,55 +9,19 @@ Item {
property int currentIndex : -1 property int currentIndex : -1
property var dataSource property var dataSource
property bool showLine: true property bool showLine: true
property bool draggable: false
property color lineColor: FluTheme.dark ? Qt.rgba(111/255,111/255,111/255,1) : Qt.rgba(217/255,217/255,217/255,1) property color lineColor: FluTheme.dark ? Qt.rgba(111/255,111/255,111/255,1) : Qt.rgba(217/255,217/255,217/255,1)
id:control id:control
QtObject { QtObject {
id:d id:d
property var rowData: [] property var current
function handleTree(treeData) { property int dropIndex: -1
var comItem = Qt.createComponent("FluTreeItem.qml"); property int dragIndex: -1
if (comItem.status !== Component.Ready) { property color hitColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
return
}
var stack = []
var rawData = []
for (var item of treeData) {
stack.push({node:item,depth:0,isExpanded:true,__parent:undefined,__childIndex:0})
}
stack = stack.reverse()
var index =0
while (stack.length > 0) {
const { node, depth,isExpanded,__parent,__childIndex} = stack.pop();
node.depth = depth;
node.isExpanded = isExpanded;
node.__parent = __parent;
node.__childIndex = __childIndex;
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.__childIndex = node.__childIndex
objItem.index = index
index = index + 1;
rawData.push(objItem)
if (node.children && node.children.length > 0) {
const children = node.children.reverse();
var childIndex = children.length-1
for (const child of children) {
stack.push({ node: child, depth: depth + 1,isExpanded:true,__parent:objItem,__childIndex:childIndex});
childIndex=childIndex-1;
}
}
}
return rawData
}
} }
onDataSourceChanged: { onDataSourceChanged: {
d.rowData = d.handleTree(dataSource) tree_model.setDataSource(dataSource)
tree_model.setData(d.rowData)
} }
FluTreeModel{ FluTreeModel{
id:tree_model id:tree_model
@ -69,65 +33,205 @@ Item {
table_view.forceLayout() table_view.forceLayout()
} }
} }
TableView{ Component{
id:table_view id:com_item_container
ScrollBar.horizontal: FluScrollBar{} Item{
ScrollBar.vertical: FluScrollBar{} signal reused
boundsBehavior: Flickable.StopAtBounds signal pooled
model: tree_model
clip: true onReused: {
console.debug("----->onReused")
}
onPooled: {
console.debug("----->onPooled")
}
property bool isCurrent: d.current === itemModel
id:item_container
width: {
var w = 46 + item_layout_text.width + 30*itemModel.depth
if(control.width>w){
return control.width
}
return w
}
height: 30
function toggle(){
if(itemModel.isExpanded){
tree_model.collapse(rowIndex)
}else{
tree_model.expand(rowIndex)
}
}
Rectangle{
anchors.fill: parent anchors.fill: parent
onContentYChanged:{ radius: 4
timer_refresh.restart() anchors.leftMargin: 6
anchors.rightMargin: 6
border.color: d.hitColor
border.width: d.dragIndex === rowIndex ? 1 : 0
color: {
if(FluTheme.dark){
if(isCurrent){
return Qt.rgba(1,1,1,0.03)
} }
reuseItems: false if(item_mouse.containsMouse){
delegate: Item { return Qt.rgba(1,1,1,0.03)
property bool hasChildren: {
if(display.children){
return true
} }
return false return Qt.rgba(0,0,0,0)
}else{
if(isCurrent){
return Qt.rgba(0,0,0,0.06)
}
if(item_mouse.containsMouse){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
}
}
}
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
} }
property var itemData: display
property bool vlineVisible: display.depth !== 0 && control.showLine
property bool hlineVisible: display.depth !== 0 && control.showLine && !hasChildren
property bool isLastIndex : {
if(display.__parent && display.__parent.children){
return display.__childIndex === display.__parent.children.length-1
} }
return false
} }
property bool isCurrent: control.currentIndex === row
implicitWidth: 46 + item_layout_text.width + 30*display.depth
implicitHeight: 30
Rectangle{ Rectangle{
width: 1 width: 1
color: control.lineColor color: control.lineColor
visible: hlineVisible visible: itemModel.depth !== 0 && control.showLine && !itemModel.hasChildren()
height: isLastIndex ? parent.height/2 : parent.height height: itemModel.hideLineFooter() ? parent.height/2 : parent.height
anchors{ anchors{
top: parent.top top: parent.top
right: layout_row.left right: layout_row.left
rightMargin: -9
} }
} }
Rectangle{ Rectangle{
height: 1 height: 1
color: control.lineColor color: control.lineColor
visible: hlineVisible visible: itemModel.depth !== 0 && control.showLine && !itemModel.hasChildren()
width: 18 width: 18
anchors{ anchors{
right: layout_row.left right: layout_row.left
rightMargin: -18 rightMargin: -27
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
} }
Repeater{ Repeater{
model: Math.max(display.depth-1,0) model: Math.max(itemModel.depth-1,0)
delegate: Rectangle{ delegate: Rectangle{
required property int index required property int index
width: 1 width: 1
color: control.lineColor color: control.lineColor
visible: vlineVisible visible: itemModel.depth !== 0 && control.showLine
anchors{ anchors{
top:parent.top top:parent.top
bottom: parent.bottom bottom: parent.bottom
@ -140,101 +244,32 @@ Item {
id:layout_row id:layout_row
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 14 + 30*display.depth anchors.leftMargin: 14 + 30*itemModel.depth
FluIconButton{ FluIconButton{
Layout.preferredWidth: 20 Layout.preferredWidth: 20
Layout.preferredHeight: 20 Layout.preferredHeight: 20
enabled: opacity enabled: opacity
opacity: hasChildren opacity: itemModel.hasChildren()
contentItem: FluIcon{ contentItem: FluIcon{
rotation: itemData.isExpanded?0:-90 rotation: itemModel.isExpanded?0:-90
iconSource:FluentIcons.ChevronDown iconSource:FluentIcons.ChevronDown
iconSize: 16 iconSize: 16
Behavior on rotation{
NumberAnimation{
duration: FluTheme.enableAnimation ? 167 : 0
easing.type: Easing.OutCubic
}
}
} }
onClicked: { onClicked: {
var isExpanded = !itemData.isExpanded item_container.toggle()
itemData.isExpanded = isExpanded
var i,obj
if(isExpanded){
for( i=0;i<d.rowData.length;i++){
obj = d.rowData[i]
if(obj === itemData){
var data = []
for(var j=i+1;j<d.rowData.length;j++){
obj = d.rowData[j]
if(obj.depth === itemData.depth){
break
}
if(obj.__expanded){
data.push(obj)
} }
} }
tree_model.insertRows(row+1,data) Item{
break
}
}
}else{
var removeCount = 0
for( i=row+1;i<tree_model.rowCount();i++){
obj = tree_model.getRow(i)
if(obj.depth === itemData.depth){
break
}
removeCount = removeCount + 1;
}
tree_model.removeRows(row+1,removeCount)
}
}
}
Rectangle{
id:item_layout_text id:item_layout_text
radius: 4
Layout.preferredWidth: item_text.implicitWidth+14 Layout.preferredWidth: item_text.implicitWidth+14
Layout.preferredHeight:item_text.implicitHeight+14 Layout.preferredHeight:item_text.implicitHeight+14
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Rectangle{
width: 3
height: 18
radius: 1.5
color: FluTheme.primaryColor.dark
visible: isCurrent
anchors{
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_text_mousearea
anchors.fill: parent
hoverEnabled: true
onClicked: {
control.currentIndex = row
}
}
color: {
if(FluTheme.dark){
if(item_text_mousearea.containsMouse || isCurrent){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
}else{
if(item_text_mousearea.containsMouse || isCurrent){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
}
}
FluText { FluText {
id:item_text id:item_text
text: display.title text: itemModel.title
anchors.centerIn: parent anchors.centerIn: parent
color:{ color:{
if(item_text_mousearea.pressed){ if(item_mouse.pressed){
return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120 return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
} }
return FluTheme.dark ? FluColors.White : FluColors.Grey220 return FluTheme.dark ? FluColors.White : FluColors.Grey220
@ -244,8 +279,68 @@ Item {
} }
} }
} }
ScrollView{
anchors.fill: parent
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
TableView{
id:table_view
ScrollBar.horizontal: FluScrollBar{}
ScrollBar.vertical: FluScrollBar{}
boundsBehavior: Flickable.StopAtBounds
model: tree_model
clip: true
anchors.fill: parent
onContentYChanged:{
timer_refresh.restart()
}
onWidthChanged: {
timer_refresh.restart()
}
delegate: Item {
id:item_control
implicitWidth: item_loader_container.width
implicitHeight: item_loader_container.height
TableView.onReused: {
item_loader_container.item.reused()
}
TableView.onPooled: {
item_loader_container.item.pooled()
}
Loader{
property var itemControl: item_control
property var itemModel: modelData
property int rowIndex: row
id:item_loader_container
width:{
if(item){
return item.width
}
return 1
}
height: {
if(item){
return item.height
}
return 1
}
sourceComponent: {
if(modelData)
return com_item_container
return undefined
}
}
}
}
Loader{
id:loader_container
property var itemControl
property var itemModel
}
}
function count(){ function count(){
return d.rowData.length return tree_model.dataSourceSize
} }
function visibleCount(){ function visibleCount(){
return table_view.rows return table_view.rows