Compare commits

...

21 Commits
1.5.8 ... 1.5.9

Author SHA1 Message Date
7a1776407f update 2023-09-23 15:55:29 +08:00
f88b330f8e update 2023-09-22 17:35:02 +08:00
67ef7f13aa update 2023-09-22 09:48:54 +08:00
23ec52ce6a update 2023-09-22 09:31:47 +08:00
5b7fdab1d9 update 2023-09-22 09:21:17 +08:00
4c1a96c03e update 2023-09-22 01:00:32 +08:00
ab4090ea9b update 2023-09-22 00:40:09 +08:00
8fb2ef723e update 2023-09-22 00:31:25 +08:00
77d9b4bde9 update 2023-09-22 00:11:58 +08:00
a96191b2af update 2023-09-21 18:29:09 +08:00
28e1799ca4 update 2023-09-20 18:38:15 +08:00
8337e278ff update 2023-09-19 23:41:56 +08:00
7ad8c969da update 2023-09-19 18:31:29 +08:00
66ae37a023 update 2023-09-19 00:31:49 +08:00
b27a88d261 update 2023-09-18 18:10:27 +08:00
257f3a7b3d update 2023-09-18 00:12:39 +08:00
4710379324 update 2023-09-17 21:34:02 +08:00
8fc74fe43b update 2023-09-17 20:36:33 +08:00
be194e7624 update 2023-09-15 19:11:55 +08:00
e6d9de34ea update 2023-09-15 01:28:03 +08:00
c47fa5ebc7 update 2023-09-14 18:50:36 +08:00
98 changed files with 2811 additions and 1545 deletions

View File

@ -18,9 +18,10 @@ on:
jobs: jobs:
build: build:
name: Build name: Build
runs-on: windows-2022 runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
os: [windows-2022]
include: include:
- qt_arch: win64_mingw - qt_arch: win64_mingw
qt_ver: 6.5.0 qt_ver: 6.5.0
@ -34,6 +35,11 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
- name: Setup ninja
uses: seanmiddleditch/gha-setup-ninja@master
with:
version: 1.10.2
- name: Install Qt - name: Install Qt
uses: jurplel/install-qt-action@v3 uses: jurplel/install-qt-action@v3
@ -62,6 +68,7 @@ jobs:
run: | run: |
mkdir build mkdir build
cd build cd build
ninja --version
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\FluentUI\Qt\6.5.0\mingw_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -GNinja .. cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\FluentUI\Qt\6.5.0\mingw_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
cmake --build . --target all --config Release --parallel cmake --build . --target all --config Release --parallel

View File

@ -49,6 +49,7 @@ jobs:
shell: cmd shell: cmd
run: | run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.msvc_arch }} call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.msvc_arch }}
ninja --version
mkdir build mkdir build
cd build cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\FluentUI\Qt\6.5.0\msvc2019_64 -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -GNinja .. cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\FluentUI\Qt\6.5.0\msvc2019_64 -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -GNinja ..

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,9 +33,8 @@ 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
FluTheme.nativeText = true
FluApp.routes = { FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml", "/":"qrc:/example/qml/window/MainWindow.qml",
"/about":"qrc:/example/qml/window/AboutWindow.qml", "/about":"qrc:/example/qml/window/AboutWindow.qml",

View File

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

@ -46,7 +46,6 @@ FluScrollablePage{
} }
FluComboBox { FluComboBox {
editable: true editable: true
font:FluTextStyle.BodyStrong
model: ListModel { model: ListModel {
id: model_2 id: model_2
ListElement { text: "Banana" } ListElement { text: "Banana" }

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) Rectangle{
} anchors.fill: parent
return Qt.rgba(0,0,0,0.0) radius: 5
}else{ color:Qt.rgba(0,0,0,0.0)
if(item_mouse.containsMouse){ visible: !item_mouse.containsMouse
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0.0)
}
}
} }
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

@ -12,7 +12,7 @@ FluScrollablePage{
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
height: 240 height: 270
paddings: 10 paddings: 10
ColumnLayout{ ColumnLayout{
spacing: 14 spacing: 14
@ -44,6 +44,12 @@ FluScrollablePage{
showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar") showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar")
} }
} }
FluButton{
text:"手动关闭的InfoBar"
onClicked: {
showInfo("这是一个Info样式的InfoBar",0,"支持手动关闭")
}
}
FluButton{ FluButton{
text:"Loading" text:"Loading"
onClicked: { onClicked: {

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

@ -5,152 +5,127 @@ import QtQuick.Controls
import FluentUI import FluentUI
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
FluScrollablePage { FluContentPage {
title:"TreeView" title:"TreeView"
function randomName() { function treeData(){
var names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十"] const dig = (path = '0', level = 4) => {
return names[Math.floor(Math.random() * names.length)] const list = [];
for (let i = 0; i < 6; i += 1) {
const key = `${path}-${i}`;
const treeNode = {
title: key,
key,
};
if (level > 0) {
treeNode.children = dig(key, level - 1);
}
list.push(treeNode);
}
return list;
};
return dig();
} }
function randomCompany() { Column{
var companies = ["阿里巴巴", "腾讯", "百度", "京东", "华为", "小米", "字节跳动", "美团", "滴滴"] id:layout_column
return companies[Math.floor(Math.random() * companies.length)] spacing: 12
} width: 300
anchors{
function randomDepartment() { topMargin: 20
var departments = ["技术部", "销售部", "市场部", "人事部", "财务部", "客服部", "产品部", "设计部", "运营部"] top:parent.top
return departments[Math.floor(Math.random() * departments.length)] left: parent.left
} leftMargin: 10
bottom:parent.bottom
function createEmployee() { bottomMargin: 20
var name = randomName()
return tree_view.createItem(name, false)
}
function createSubtree(numEmployees) {
var employees = []
for (var i = 0; i < numEmployees; i++) {
employees.push(createEmployee())
} }
return tree_view.createItem(randomDepartment(), true, employees)
}
function createOrg(numLevels, numSubtrees, numEmployees) { FluText{
if (numLevels === 0) { text:"共计%1条数据当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount())
return [] }
FluText{
text:"共计选中%1条数据".arg(tree_view.selectionModel().length)
} }
var subtrees = []
for (var i = 0; i < numSubtrees; i++) {
subtrees.push(createSubtree(numEmployees))
}
return [tree_view.createItem(randomCompany(), true, subtrees)].concat(createOrg(numLevels - 1, numSubtrees, numEmployees))
}
FluArea{
id:layout_actions
Layout.fillWidth: true
Layout.topMargin: 20
height: 50
paddings: 10
RowLayout{ RowLayout{
spacing: 14 spacing: 10
FluDropDownButton{ FluText{
id:btn_selection_model text:"cellHeight:"
Layout.preferredWidth: 140 Layout.alignment: Qt.AlignVCenter
text:"None"
FluMenuItem{
text:"None"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.Equal
}
}
FluMenuItem{
text:"Single"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.SizeToContent
}
}
FluMenuItem{
text:"Muiltple"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.Compact
}
}
} }
FluFilledButton{ FluSlider{
text:"获取选中的数据" id:slider_cell_height
onClicked: { value: 30
if(tree_view.selectionMode === FluTreeViewType.None){ from: 30
showError("当前非选择模式,没有选中的数据") to:100
} }
if(tree_view.selectionMode === FluTreeViewType.Single){ }
if(!tree_view.signleData()){ RowLayout{
showError("没有选中数据") spacing: 10
return FluText{
} text:"depthPadding:"
showSuccess(tree_view.signleData().text) Layout.alignment: Qt.AlignVCenter
} }
if(tree_view.selectionMode === FluTreeViewType.Multiple){ FluSlider{
if(tree_view.multipData().length===0){ id:slider_depth_padding
showError("没有选中数据") value: 30
return from: 30
} to:100
var info = [] }
tree_view.multipData().map((value)=>info.push(value.text)) }
showSuccess(info.join(",")) FluToggleSwitch{
} id:switch_showline
} text:"showLine"
checked: false
}
FluToggleSwitch{
id:switch_draggable
text:"draggable"
checked: false
}
FluToggleSwitch{
id:switch_checkable
text:"checkable"
checked: false
}
FluButton{
text:"all expand"
onClicked: {
tree_view.allExpand()
}
}
FluButton{
text:"all collapse"
onClicked: {
tree_view.allCollapse()
} }
} }
} }
FluArea{ FluArea{
Layout.fillWidth: true anchors{
Layout.topMargin: 10 left: layout_column.right
paddings: 10 top: parent.top
height: 400 bottom: parent.bottom
right: parent.right
rightMargin: 5
topMargin: 5
bottomMargin: 5
}
FluShadow{}
FluTreeView{ FluTreeView{
id:tree_view id:tree_view
width:240 anchors.fill: parent
anchors{ cellHeight: slider_cell_height.value
top:parent.top draggable:switch_draggable.checked
left:parent.left showLine: switch_showline.checked
bottom:parent.bottom checkable:switch_checkable.checked
} depthPadding: slider_depth_padding.value
onItemClicked:
(model)=>{
showSuccess(model.text)
}
Component.onCompleted: { Component.onCompleted: {
var org = createOrg(3, 3, 3) var data = treeData()
createItem() dataSource = data
updateData(org)
} }
} }
} }
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluTreeView{
id:tree_view
width:240
height:600
Component.onCompleted: {
var datas = []
datas.push(createItem("Node1",false))
datas.push(createItem("Node2",false))
datas.push(createItem("Node2",true,[createItem("Node2-1",false),createItem("Node2-2",false)]))
updateData(datas)
}
}
'
}
} }

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

@ -183,7 +183,7 @@ CustomWindow {
z:999 z:999
//Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式 //Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式
// pageMode: FluNavigationViewType.Stack // pageMode: FluNavigationViewType.Stack
//NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐 //NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存可以配合FluViewModel保存页面数据(推荐)
pageMode: FluNavigationViewType.NoStack pageMode: FluNavigationViewType.NoStack
items: ItemsOriginal items: ItemsOriginal
footerItems:ItemsFooter footerItems:ItemsFooter
@ -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,9 +33,8 @@ 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
FluTheme.nativeText = true
FluApp.routes = { FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml", "/":"qrc:/example/qml/window/MainWindow.qml",
"/about":"qrc:/example/qml/window/AboutWindow.qml", "/about":"qrc:/example/qml/window/AboutWindow.qml",

View File

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

@ -47,7 +47,6 @@ FluScrollablePage{
} }
FluComboBox { FluComboBox {
editable: true editable: true
font:FluTextStyle.BodyStrong
model: ListModel { model: ListModel {
id: model_2 id: model_2
ListElement { text: "Banana" } ListElement { text: "Banana" }

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) Rectangle{
} anchors.fill: parent
return Qt.rgba(0,0,0,0.0) radius: 5
}else{ color:Qt.rgba(0,0,0,0.0)
if(item_mouse.containsMouse){ visible: !item_mouse.containsMouse
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0.0)
}
}
} }
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

@ -13,7 +13,7 @@ FluScrollablePage{
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
height: 240 height: 270
paddings: 10 paddings: 10
ColumnLayout{ ColumnLayout{
spacing: 14 spacing: 14
@ -45,6 +45,12 @@ FluScrollablePage{
showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar") showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar")
} }
} }
FluButton{
text:"手动关闭的InfoBar"
onClicked: {
showInfo("这是一个Info样式的InfoBar",0,"支持手动关闭")
}
}
FluButton{ FluButton{
text:"Loading" text:"Loading"
onClicked: { onClicked: {

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

@ -6,152 +6,127 @@ import FluentUI 1.0
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "../component" import "../component"
FluScrollablePage { FluContentPage {
title:"TreeView" title:"TreeView"
function randomName() { function treeData(){
var names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十"] const dig = (path = '0', level = 4) => {
return names[Math.floor(Math.random() * names.length)] const list = [];
for (let i = 0; i < 6; i += 1) {
const key = `${path}-${i}`;
const treeNode = {
title: key,
key,
};
if (level > 0) {
treeNode.children = dig(key, level - 1);
}
list.push(treeNode);
}
return list;
};
return dig();
} }
function randomCompany() { Column{
var companies = ["阿里巴巴", "腾讯", "百度", "京东", "华为", "小米", "字节跳动", "美团", "滴滴"] id:layout_column
return companies[Math.floor(Math.random() * companies.length)] spacing: 12
} width: 300
anchors{
function randomDepartment() { topMargin: 20
var departments = ["技术部", "销售部", "市场部", "人事部", "财务部", "客服部", "产品部", "设计部", "运营部"] top:parent.top
return departments[Math.floor(Math.random() * departments.length)] left: parent.left
} leftMargin: 10
bottom:parent.bottom
function createEmployee() { bottomMargin: 20
var name = randomName()
return tree_view.createItem(name, false)
}
function createSubtree(numEmployees) {
var employees = []
for (var i = 0; i < numEmployees; i++) {
employees.push(createEmployee())
} }
return tree_view.createItem(randomDepartment(), true, employees)
}
function createOrg(numLevels, numSubtrees, numEmployees) { FluText{
if (numLevels === 0) { text:"共计%1条数据当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount())
return [] }
FluText{
text:"共计选中%1条数据".arg(tree_view.selectionModel().length)
} }
var subtrees = []
for (var i = 0; i < numSubtrees; i++) {
subtrees.push(createSubtree(numEmployees))
}
return [tree_view.createItem(randomCompany(), true, subtrees)].concat(createOrg(numLevels - 1, numSubtrees, numEmployees))
}
FluArea{
id:layout_actions
Layout.fillWidth: true
Layout.topMargin: 20
height: 50
paddings: 10
RowLayout{ RowLayout{
spacing: 14 spacing: 10
FluDropDownButton{ FluText{
id:btn_selection_model text:"cellHeight:"
Layout.preferredWidth: 140 Layout.alignment: Qt.AlignVCenter
text:"None"
FluMenuItem{
text:"None"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.Equal
}
}
FluMenuItem{
text:"Single"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.SizeToContent
}
}
FluMenuItem{
text:"Muiltple"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabViewType.Compact
}
}
} }
FluFilledButton{ FluSlider{
text:"获取选中的数据" id:slider_cell_height
onClicked: { value: 30
if(tree_view.selectionMode === FluTreeViewType.None){ from: 30
showError("当前非选择模式,没有选中的数据") to:100
} }
if(tree_view.selectionMode === FluTreeViewType.Single){ }
if(!tree_view.signleData()){ RowLayout{
showError("没有选中数据") spacing: 10
return FluText{
} text:"depthPadding:"
showSuccess(tree_view.signleData().text) Layout.alignment: Qt.AlignVCenter
} }
if(tree_view.selectionMode === FluTreeViewType.Multiple){ FluSlider{
if(tree_view.multipData().length===0){ id:slider_depth_padding
showError("没有选中数据") value: 30
return from: 30
} to:100
var info = [] }
tree_view.multipData().map((value)=>info.push(value.text)) }
showSuccess(info.join(",")) FluToggleSwitch{
} id:switch_showline
} text:"showLine"
checked: false
}
FluToggleSwitch{
id:switch_draggable
text:"draggable"
checked: false
}
FluToggleSwitch{
id:switch_checkable
text:"checkable"
checked: false
}
FluButton{
text:"all expand"
onClicked: {
tree_view.allExpand()
}
}
FluButton{
text:"all collapse"
onClicked: {
tree_view.allCollapse()
} }
} }
} }
FluArea{ FluArea{
Layout.fillWidth: true anchors{
Layout.topMargin: 10 left: layout_column.right
paddings: 10 top: parent.top
height: 400 bottom: parent.bottom
right: parent.right
rightMargin: 5
topMargin: 5
bottomMargin: 5
}
FluShadow{}
FluTreeView{ FluTreeView{
id:tree_view id:tree_view
width:240 anchors.fill: parent
anchors{ cellHeight: slider_cell_height.value
top:parent.top draggable:switch_draggable.checked
left:parent.left showLine: switch_showline.checked
bottom:parent.bottom checkable:switch_checkable.checked
} depthPadding: slider_depth_padding.value
onItemClicked:
(model)=>{
showSuccess(model.text)
}
Component.onCompleted: { Component.onCompleted: {
var org = createOrg(3, 3, 3) var data = treeData()
createItem() dataSource = data
updateData(org)
} }
} }
} }
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluTreeView{
id:tree_view
width:240
height:600
Component.onCompleted: {
var datas = []
datas.push(createItem("Node1",false))
datas.push(createItem("Node2",false))
datas.push(createItem("Node2",true,[createItem("Node2-1",false),createItem("Node2-2",false)]))
updateData(datas)
}
}
'
}
} }

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

@ -186,7 +186,7 @@ CustomWindow {
z:999 z:999
//Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式 //Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式
// pageMode: FluNavigationViewType.Stack // pageMode: FluNavigationViewType.Stack
//NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐 //NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存可以配合FluViewModel保存页面数据(推荐)
pageMode: FluNavigationViewType.NoStack pageMode: FluNavigationViewType.NoStack
items: ItemsOriginal items: ItemsOriginal
footerItems:ItemsFooter footerItems:ItemsFooter
@ -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)
@ -22,8 +24,9 @@ Q_IMPORT_QML_PLUGIN(FluentUIPlugin)
FRAMELESSHELPER_USE_NAMESPACE 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

View File

@ -119,7 +119,8 @@ void FluHttp::post(HttpRequest* r,HttpCallable* c){
QString result = QString::fromUtf8(reply->readAll()); QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString(); QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError; QNetworkReply::NetworkError error = reply->error();
bool isSuccess = error == QNetworkReply::NoError;
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if (isSuccess) { if (isSuccess) {
@ -134,6 +135,9 @@ void FluHttp::post(HttpRequest* r,HttpCallable* c){
onError(callable,status,errorString,result); onError(callable,status,errorString,result);
} }
} }
if(error == QNetworkReply::OperationCanceledError){
break;
}
} }
onFinish(callable,request); onFinish(callable,request);
}); });
@ -174,7 +178,8 @@ void FluHttp::postString(HttpRequest* r,HttpCallable* c){
QString result = QString::fromUtf8(reply->readAll()); QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString(); QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError; QNetworkReply::NetworkError error = reply->error();
bool isSuccess = error == QNetworkReply::NoError;
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if (isSuccess) { if (isSuccess) {
@ -189,6 +194,9 @@ void FluHttp::postString(HttpRequest* r,HttpCallable* c){
onError(callable,status,errorString,result); onError(callable,status,errorString,result);
} }
} }
if(error == QNetworkReply::OperationCanceledError){
break;
}
} }
onFinish(callable,request); onFinish(callable,request);
}); });
@ -228,7 +236,8 @@ void FluHttp::postJson(HttpRequest* r,HttpCallable* c){
QString result = QString::fromUtf8(reply->readAll()); QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString(); QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError; QNetworkReply::NetworkError error = reply->error();
bool isSuccess = error == QNetworkReply::NoError;
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if (isSuccess) { if (isSuccess) {
@ -243,6 +252,9 @@ void FluHttp::postJson(HttpRequest* r,HttpCallable* c){
onError(callable,status,errorString,result); onError(callable,status,errorString,result);
} }
} }
if(error == QNetworkReply::OperationCanceledError){
break;
}
} }
onFinish(callable,request); onFinish(callable,request);
}); });
@ -273,14 +285,15 @@ void FluHttp::get(HttpRequest* r,HttpCallable* c){
QNetworkRequest req(url); QNetworkRequest req(url);
addHeaders(&req,data["headers"].toMap()); addHeaders(&req,data["headers"].toMap());
QEventLoop loop; QEventLoop loop;
QNetworkReply* reply = manager.get(req); auto reply = QPointer(manager.get(req));
_cacheReply.append(reply); _cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();}); connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();});
connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();}); connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();});
loop.exec(); loop.exec();
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString(); QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError; QNetworkReply::NetworkError error = reply->error();
bool isSuccess = error == QNetworkReply::NoError;
QString result = QString::fromUtf8(reply->readAll()); QString result = QString::fromUtf8(reply->readAll());
if (isSuccess) { if (isSuccess) {
handleCache(httpId,result); handleCache(httpId,result);
@ -296,6 +309,9 @@ void FluHttp::get(HttpRequest* r,HttpCallable* c){
} }
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if(error == QNetworkReply::OperationCanceledError){
break;
}
} }
onFinish(callable,request); onFinish(callable,request);
}); });

28
src/FluRectangle.cpp Normal file
View File

@ -0,0 +1,28 @@
#include "FluRectangle.h"
#include <QPainterPath>
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.bottomRight() - 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,14 @@
#include <QFileInfo> #include <QFileInfo>
#include <QProcess> #include <QProcess>
#include <QDir> #include <QDir>
#include <QOpenGLContext>
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QTextDocument> #include <QTextDocument>
#include <QQuickWindow>
#include <QDateTime>
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 +163,15 @@ 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";
}
QPoint FluTools::cursorPos(){
return QCursor::pos();
}
qint64 FluTools::currentTimestamp(){
return QDateTime::currentMSecsSinceEpoch();
}

View File

@ -46,6 +46,9 @@ 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();
Q_INVOKABLE qint64 currentTimestamp();
Q_INVOKABLE QPoint cursorPos();
}; };
#endif // FLUTOOLS_H #endif // FLUTOOLS_H

368
src/FluTreeModel.cpp Normal file
View File

@ -0,0 +1,368 @@
#include "FluTreeModel.h"
#include <QMetaEnum>
Node::Node(QObject *parent): QObject{parent}{
}
FluTreeModel::FluTreeModel(QObject *parent): QAbstractItemModel{parent}{
dataSourceSize(0);
}
QModelIndex FluTreeModel::parent(const QModelIndex &child) const{
return QModelIndex();
}
QModelIndex FluTreeModel::index(int row, int column,const QModelIndex &parent) const{
if (!hasIndex(row, column, parent) || parent.isValid())
return QModelIndex();
return createIndex(row, column, _rows.at(row));
}
int FluTreeModel::rowCount(const QModelIndex &parent) const {
return _rows.count();
};
int FluTreeModel::columnCount(const QModelIndex &parent) const {
return 1;;
};
QVariant FluTreeModel::data(const QModelIndex &index, int role) const {
switch (role) {
case Qt::DisplayRole:
return QVariant::fromValue(_rows.at(index.row()));
default:
break;
}
return QVariant();
};
QHash<int, QByteArray> FluTreeModel::roleNames() const {
return { {Qt::DisplayRole, "modelData"} };
};
void FluTreeModel::setData(QList<Node*> data){
beginResetModel();
_rows = data;
endResetModel();
}
void FluTreeModel::removeRows(int row,int count){
if (row < 0 || row + count > _rows.size() || count==0)
return;
beginRemoveRows(QModelIndex(),row, row + count - 1);
QList<Node*> firstPart = _rows.mid(0,row);
QList<Node*> secondPart = _rows.mid(row + count);
_rows.clear();
_rows.append(firstPart);
_rows.append(secondPart);
endRemoveRows();
}
void FluTreeModel::insertRows(int row,QList<Node*> data){
if (row < 0 || row > _rows.size() || data.size() == 0)
return;;
beginInsertRows(QModelIndex(), row, row + data.size() - 1);
QList<Node*> firstPart = _rows.mid(0, row);
QList<Node*> secondPart = _rows.mid(row);
_rows.clear();
_rows.append(firstPart);
_rows.append(data);
_rows.append(secondPart);
endInsertRows();
}
QObject* FluTreeModel::getRow(int row){
return _rows.at(row);
}
void FluTreeModel::checkRow(int row,bool chekced){
auto itemData = _rows.at(row);
if(itemData->hasChildren()){
QList<Node*> stack = itemData->_children;
std::reverse(stack.begin(), stack.end());
while (stack.count() > 0) {
auto item = stack.at(stack.count()-1);
stack.pop_back();
if(!item->hasChildren()){
item->_checked = chekced;
}
QList<Node*> children = item->_children;
if(!children.isEmpty()){
std::reverse(children.begin(), children.end());
foreach (auto c, children) {
stack.append(c);
}
}
}
}else{
if(itemData->_checked == chekced){
return;
}
itemData->_checked = chekced;
}
Q_EMIT layoutChanged(QList<QPersistentModelIndex>(),QAbstractItemModel::VerticalSortHint);
QList<Node*> data;
foreach (auto item, _dataSource) {
if(!item->hasChildren()){
if(item->_checked){
data.append(item);
}
}
}
selectionModel(data);
}
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());
for (int i = 0; i < children.count(); ++i) {
auto child = children.at(i).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);
}
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);
}
void FluTreeModel::dragAnddrop(int dragIndex,int dropIndex,bool isDropTopArea){
if(dropIndex>_rows.count() || dropIndex<0){
return;
}
auto dragItem = _rows[dragIndex];
auto dropItem = _rows[dropIndex];
int targetIndex;
if(dropIndex > dragIndex){
if(isDropTopArea){
targetIndex = dropIndex;
}else{
targetIndex = dropIndex+1;
}
}else{
if(isDropTopArea){
targetIndex = dropIndex;
}else{
targetIndex = dropIndex+1;
}
}
if (!beginMoveRows(QModelIndex(), dragIndex, dragIndex, QModelIndex(), targetIndex)) {
return;
}
if(dropIndex > dragIndex){
if(isDropTopArea){
targetIndex = dropIndex-1;
}else{
targetIndex = dropIndex;
}
}else{
if(isDropTopArea){
targetIndex = dropIndex;
}else{
targetIndex = dropIndex+1;
}
}
_rows.move(dragIndex,targetIndex);
endMoveRows();
Q_EMIT layoutAboutToBeChanged();
if(dragItem->_parent == dropItem->_parent){
QList<Node*>* children = &(dragItem->_parent->_children);
int srcIndex = children->indexOf(dragItem);
int destIndex = children->indexOf(dropItem);
if(dropIndex > dragIndex){
if(isDropTopArea){
targetIndex = destIndex-1;
}else{
targetIndex = destIndex;
}
}else{
if(isDropTopArea){
targetIndex = destIndex;
}else{
targetIndex = destIndex+1;
}
}
children->move(srcIndex,targetIndex);
}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;
if(dragItem->hasChildren()){
QList<Node*> stack = dragItem->_children;
foreach (auto node, stack) {
node->_depth = dragItem->_depth+1;
}
std::reverse(stack.begin(), stack.end());
while (stack.count() > 0) {
auto item = stack.at(stack.count()-1);
stack.pop_back();
QList<Node*> children = item->_children;
if(!children.isEmpty()){
std::reverse(children.begin(), children.end());
foreach (auto c, children) {
c->_depth = item->_depth+1;
stack.append(c);
}
}
}
}
srcChildren->removeAt(srcIndex);
if(dropIndex > dragIndex){
if(isDropTopArea){
targetIndex = destIndex;
}else{
targetIndex = destIndex + 1;
}
}else{
if(isDropTopArea){
targetIndex = destIndex;
}else{
targetIndex = destIndex + 1;
}
}
destChildren->insert(targetIndex,dragItem);
}
changePersistentIndex(index(qMin(dragIndex,dropIndex),0),index(qMax(dragIndex,dropIndex),0));
Q_EMIT layoutChanged(QList<QPersistentModelIndex>(),QAbstractItemModel::VerticalSortHint);
}
bool FluTreeModel::hitHasChildrenExpanded(int row){
auto itemData = _rows.at(row);
if(itemData->hasChildren() && itemData->_isExpanded){
return true;
}
return false;
}
void FluTreeModel::refreshNode(int row){
Q_EMIT dataChanged(index(row,0),index(row,0));
};
Node* FluTreeModel::getNode(int row){
return _rows.at(row);
}
void FluTreeModel::allExpand(){
beginResetModel();
QList<Node*> data;
QList<Node*> stack = _root->_children;
std::reverse(stack.begin(), stack.end());
while (stack.count() > 0) {
auto item = stack.at(stack.count()-1);
stack.pop_back();
if(item->hasChildren()){
item->_isExpanded = true;
}
data.append(item);
QList<Node*> children = item->_children;
if(!children.isEmpty()){
std::reverse(children.begin(), children.end());
foreach (auto c, children) {
stack.append(c);
}
}
}
_rows = data;
endResetModel();
}
void FluTreeModel::allCollapse(){
beginResetModel();
QList<Node*> stack = _root->_children;
std::reverse(stack.begin(), stack.end());
while (stack.count() > 0) {
auto item = stack.at(stack.count()-1);
stack.pop_back();
if(item->hasChildren()){
item->_isExpanded = false;
}
QList<Node*> children = item->_children;
if(!children.isEmpty()){
std::reverse(children.begin(), children.end());
foreach (auto c, children) {
stack.append(c);
}
}
}
_rows = _root->_children;
endResetModel();
}

115
src/FluTreeModel.h Normal file
View File

@ -0,0 +1,115 @@
#ifndef FLUTREEMODEL_H
#define FLUTREEMODEL_H
#include <QObject>
#include <QAbstractTableModel>
#include <QJsonArray>
#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)
Q_PROPERTY(bool checked READ checked 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 hasNextNodeByIndex(int index){
Node* p = this;
for(int i=0;i<(_depth - index -1);i++){
p = p->_parent;
}
if(p->_parent->_children.indexOf(p) == p->_parent->_children.count()-1){
return false;
}
return true;
}
Q_INVOKABLE bool checked(){
if(!hasChildren()){
return _checked;
}
foreach (auto item, _children) {
if(!item->checked()){
return false;
}
}
return true;
};
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 _checked = false;
bool _isExpanded=true;
QList<Node*> _children;
Node* _parent = nullptr;
};
class FluTreeModel : public QAbstractItemModel
{
Q_OBJECT
Q_PROPERTY_AUTO(int,dataSourceSize)
Q_PROPERTY_AUTO(QList<Node*>,selectionModel)
QML_NAMED_ELEMENT(FluTreeModel)
QML_ADDED_IN_MINOR_VERSION(1)
public:
explicit FluTreeModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
QModelIndex parent(const QModelIndex &child) const override;
QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE void removeRows(int row,int count);
Q_INVOKABLE void insertRows(int row,QList<Node*> data);
Q_INVOKABLE QObject* getRow(int row);
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,bool isDropTopArea);
Q_INVOKABLE Node* getNode(int row);
Q_INVOKABLE void refreshNode(int row);
Q_INVOKABLE void checkRow(int row,bool chekced);
Q_INVOKABLE bool hitHasChildrenExpanded(int row);
Q_INVOKABLE void allExpand();
Q_INVOKABLE void allCollapse();
private:
QList<Node*> _rows;
QList<Node*> _dataSource;
Node* _root = nullptr;
};
#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

@ -13,8 +13,10 @@
#include "FluWatermark.h" #include "FluWatermark.h"
#include "FluCaptcha.h" #include "FluCaptcha.h"
#include "FluEventBus.h" #include "FluEventBus.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;
@ -53,6 +55,8 @@ void FluentUI::registerTypes(const char *uri){
qmlRegisterType<HttpRequest>(uri,major,minor,"HttpRequest"); qmlRegisterType<HttpRequest>(uri,major,minor,"HttpRequest");
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<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");
@ -91,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");
@ -118,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");
@ -148,6 +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/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

@ -13,11 +13,14 @@ Rectangle{
property string closeText : "关闭" property string closeText : "关闭"
property color textColor: FluTheme.dark ? "#FFFFFF" : "#000000" property color textColor: FluTheme.dark ? "#FFFFFF" : "#000000"
property color minimizeNormalColor: Qt.rgba(0,0,0,0) property color minimizeNormalColor: Qt.rgba(0,0,0,0)
property color minimizeHoverColor: FluTheme.dark ? Qt.rgba(1,1,1,0.1) : Qt.rgba(0,0,0,0.06) property color minimizeHoverColor: FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
property color minimizePressColor: FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06)
property color maximizeNormalColor: Qt.rgba(0,0,0,0) property color maximizeNormalColor: Qt.rgba(0,0,0,0)
property color maximizeHoverColor: FluTheme.dark ? Qt.rgba(1,1,1,0.1) : Qt.rgba(0,0,0,0.06) property color maximizeHoverColor: FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
property color maximizePressColor: FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06)
property color closeNormalColor: Qt.rgba(0,0,0,0) property color closeNormalColor: Qt.rgba(0,0,0,0)
property color closeHoverColor: Qt.rgba(251/255,115/255,115/255,1) property color closeHoverColor: Qt.rgba(251/255,115/255,115/255,1)
property color closePressColor: Qt.rgba(251/255,115/255,115/255,0.8)
property bool showDark: false property bool showDark: false
property bool showClose: true property bool showClose: true
property bool showMinimize: true property bool showMinimize: true
@ -114,7 +117,12 @@ Rectangle{
radius: 0 radius: 0
visible: !isMac && showMinimize visible: !isMac && showMinimize
iconColor: control.textColor iconColor: control.textColor
color: hovered ? minimizeHoverColor : minimizeNormalColor color: {
if(pressed){
return minimizePressColor
}
return hovered ? minimizeHoverColor : minimizeNormalColor
}
onClicked: minClickListener() onClicked: minClickListener()
} }
FluIconButton{ FluIconButton{
@ -122,7 +130,12 @@ Rectangle{
Layout.preferredWidth: 40 Layout.preferredWidth: 40
Layout.preferredHeight: 30 Layout.preferredHeight: 30
iconSource : d.isRestore ? FluentIcons.ChromeRestore : FluentIcons.ChromeMaximize iconSource : d.isRestore ? FluentIcons.ChromeRestore : FluentIcons.ChromeMaximize
color: hovered ? maximizeHoverColor : maximizeNormalColor color: {
if(pressed){
return maximizePressColor
}
return hovered ? maximizeHoverColor : maximizeNormalColor
}
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
visible: d.resizable && !isMac && showMaximize visible: d.resizable && !isMac && showMaximize
radius: 0 radius: 0
@ -142,7 +155,12 @@ Rectangle{
radius: 0 radius: 0
iconSize: 10 iconSize: 10
iconColor: hovered ? Qt.rgba(1,1,1,1) : control.textColor iconColor: hovered ? Qt.rgba(1,1,1,1) : control.textColor
color:hovered ? closeHoverColor : closeNormalColor color:{
if(pressed){
return closePressColor
}
return hovered ? closeHoverColor : closeNormalColor
}
onClicked: closeClickListener() onClicked: closeClickListener()
} }
} }

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

@ -22,6 +22,7 @@ Button {
property alias textColor: btn_text.textColor property alias textColor: btn_text.textColor
property bool textRight: true property bool textRight: true
property real textSpacing: 6 property real textSpacing: 6
property bool enableAnimation: FluTheme.enableAnimation
property var clickListener : function(){ property var clickListener : function(){
checked = !checked checked = !checked
} }
@ -39,8 +40,9 @@ Button {
visible: control.activeFocus visible: control.activeFocus
} }
} }
horizontalPadding:2 horizontalPadding:0
verticalPadding: 2 verticalPadding: 0
padding: 0
Accessible.role: Accessible.Button Accessible.role: Accessible.Button
Accessible.name: control.text Accessible.name: control.text
Accessible.description: contentDescription Accessible.description: contentDescription
@ -91,7 +93,7 @@ Button {
return normalColor return normalColor
} }
Behavior on color { Behavior on color {
enabled: FluTheme.enableAnimation enabled: control.enableAnimation
ColorAnimation{ ColorAnimation{
duration: 83 duration: 83
} }
@ -103,7 +105,7 @@ Button {
visible: checked visible: checked
iconColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1) iconColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
Behavior on visible { Behavior on visible {
enabled: FluTheme.enableAnimation enabled: control.enableAnimation
NumberAnimation{ NumberAnimation{
duration: 83 duration: 83
} }

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

@ -4,7 +4,7 @@ import QtQuick.Window 2.15
import FluentUI 1.0 import FluentUI 1.0
import QtQuick.Templates 2.15 as T import QtQuick.Templates 2.15 as T
ComboBox { T.ComboBox {
id: control id: control
signal commit(string text) signal commit(string text)
property bool disabled: false property bool disabled: false
@ -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: {
@ -72,6 +72,7 @@ ComboBox {
Keys.onReturnPressed:(event)=> handleCommit(event) Keys.onReturnPressed:(event)=> handleCommit(event)
function handleCommit(event){ function handleCommit(event){
control.commit(control.editText) control.commit(control.editText)
accepted()
} }
} }

View File

@ -5,14 +5,13 @@ 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{ FluRectangle{
id:separator id:separator
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

@ -83,7 +83,7 @@ FluObject {
} }
Timer { Timer {
id:delayTimer id:delayTimer
interval: duration; running: true; repeat: true interval: duration; running: duration > 0; repeat: duration > 0
onTriggered: content.close(); onTriggered: content.close();
} }
Loader{ Loader{
@ -184,10 +184,47 @@ FluObject {
} }
} }
FluText{ Column{
text:_super.text spacing: 5
wrapMode: Text.WrapAnywhere FluText{
width: Math.min(implicitWidth,mcontrol.maxWidth) text:_super.text
wrapMode: Text.WrapAnywhere
width: Math.min(implicitWidth,mcontrol.maxWidth)
}
FluText{
text: _super.moremsg
visible: _super.moremsg
wrapMode : Text.WordWrap
textColor: FluColors.Grey120
}
}
FluIconButton{
iconSource: FluentIcons.ChromeClose
iconSize: 10
y:5
x:parent.width-35
visible: _super.duration<=0
iconColor: {
if(FluTheme.dark){
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1);
case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1);
case mcontrol.const_info: return FluTheme.primaryColor.lighter;
case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1);
}
return "#FFFFFF"
}else{
switch(_super.type){
case mcontrol.const_success: return "#0f7b0f";
case mcontrol.const_warning: return "#9d5d00";
case mcontrol.const_info: return "#0066b4";
case mcontrol.const_error: return "#c42b1c";
}
return "#FFFFFF"
}
}
onClicked: _super.close()
} }
} }
} }

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

@ -4,28 +4,28 @@ import FluentUI 1.0
T.ItemDelegate { T.ItemDelegate {
id: control id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding) implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding, implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding) implicitIndicatorHeight + topPadding + bottomPadding)
padding: 0
padding: 12 verticalPadding: 8
spacing: 8 horizontalPadding: 10
icon.width: 24
icon.height: 24
icon.color: control.palette.text icon.color: control.palette.text
contentItem:FluText {
contentItem: FluText {
text: control.text text: control.text
font: control.font font: control.font
color:{
if(control.down){
return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
}
return FluTheme.dark ? FluColors.White : FluColors.Grey220
}
} }
background: Rectangle { background: Rectangle {
implicitWidth: 100 implicitWidth: 100
implicitHeight: 40 implicitHeight: 30
color:{ color:{
if(FluTheme.dark){ if(FluTheme.dark){
return Qt.rgba(1,1,1,0.05) return Qt.rgba(1,1,1,0.05)

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,7 +801,9 @@ Item {
anchors.fill: loader_content anchors.fill: loader_content
onDropped: onDropped:
(drag)=>{ (drag)=>{
drag.source.modelData.dropped(drag) if(drag.source.modelData){
drag.source.modelData.dropped(drag)
}
} }
} }
Loader{ Loader{
@ -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

@ -31,27 +31,18 @@ T.RangeSlider {
radius: 12 radius: 12
} }
Rectangle{ Rectangle{
width: 24 width: radius*2
height: 24 height: radius*2
radius: 12 radius:{
if(control.first.pressed){
return 5
}
return control.first.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.first.pressed){
return 4/10
}
return control.first.hovered ? 6/10 : 5/10
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
} }
} }
second.handle: Rectangle { second.handle: Rectangle {
x: control.leftPadding + (control.horizontal ? control.second.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2) x: control.leftPadding + (control.horizontal ? control.second.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2)
y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.second.visualPosition * (control.availableHeight - height)) y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.second.visualPosition * (control.availableHeight - height))
@ -63,27 +54,18 @@ T.RangeSlider {
radius: 12 radius: 12
} }
Rectangle{ Rectangle{
width: 24 width: radius*2
height: 24 height: radius*2
radius: 12 radius:{
if(control.second.pressed){
return 5
}
return control.second.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.second.pressed){
return 4/10
}
return control.second.hovered ? 6/10 : 5/10
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
} }
} }
background: Item { background: Item {
x: control.leftPadding + (control.horizontal ? 0 : (control.availableWidth - width) / 2) x: control.leftPadding + (control.horizontal ? 0 : (control.availableWidth - width) / 2)
y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0) y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0)

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,38 +1,34 @@
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
radius: 4 id:control
layer.enabled: true radius: [4,4,4,4]
color: { Rectangle{
if(inputItem.disabled){ radius: 4
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1) anchors.fill: parent
color: {
if(inputItem.disabled){
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
}
if(inputItem.activeFocus){
return FluTheme.dark ? Qt.rgba(36/255,36/255,36/255,1) : Qt.rgba(1,1,1,1)
}
if(inputItem.hovered){
return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
}
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
} }
if(inputItem.activeFocus){ border.width: control.borderWidth
return FluTheme.dark ? Qt.rgba(36/255,36/255,36/255,1) : Qt.rgba(1,1,1,1) border.color: {
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(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
} }
if(inputItem.hovered){
return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
}
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
}
layer.effect:OpacityMask {
maskSource: Rectangle {
width: content.width
height: content.height
radius: 4
}
}
border.width: 1
border.color: {
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(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
} }
Rectangle{ Rectangle{
width: parent.width width: parent.width

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
@ -91,14 +91,9 @@ Button {
return dotNormalColor return dotNormalColor
} }
Behavior on x { Behavior on x {
enabled: FluTheme.enableAnimation
NumberAnimation {
easing.type: Easing.OutCubic
}
}
Behavior on scale {
enabled: FluTheme.enableAnimation enabled: FluTheme.enableAnimation
NumberAnimation { NumberAnimation {
duration: 167
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }

View File

@ -2,291 +2,453 @@ import QtQuick 2.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import Qt.labs.qmlmodels 1.0
import FluentUI 1.0 import FluentUI 1.0
Item { Item {
property int selectionMode: FluTreeViewType.None property int currentIndex : -1
property var currentElement property var dataSource
property var currentParentElement property bool showLine: true
property var rootModel: tree_model.get(0).items property bool draggable: false
signal itemClicked(var item) property int cellHeight: 30
id:root property int depthPadding: 30
ListModel{ property bool checkable: false
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
QtObject {
id:d
property int dy
property var current
property int dropIndex: -1
property bool isDropTopArea: false
property int dragIndex: -1
property color hitColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
}
onDataSourceChanged: {
tree_model.setDataSource(dataSource)
}
FluTreeModel{
id:tree_model id:tree_model
ListElement{
text: "根节点"
expanded:true
items:[]
key:"123456"
multipSelected:false
multipIndex:0
multipParentKey:""
}
} }
Component{ ListView{
id: delegate_root id:table_view
Column{ ScrollBar.horizontal: FluScrollBar{}
width: calculateWidth() ScrollBar.vertical: FluScrollBar{}
property var itemModel: model boundsBehavior: Flickable.StopAtBounds
Repeater{
id: repeater_first_level
model: items
delegate: delegate_items
}
function calculateWidth(){
var w = 0;
for(var i = 0; i < repeater_first_level.count; i++) {
var child = repeater_first_level.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
return w;
}
}
}
Component{
id:delegate_items
Column{
id:item_layout
property real level: (mapToItem(list_root,0,0).x+list_root.contentX)/0.001
property var text: model.text??"Item"
property bool hasChild : (model.items !== undefined) && (model.items.count !== 0)
property var items: model.items??[]
property var expanded: model.expanded??true
property int width_hint: calculateWidth()
property bool singleSelected: currentElement === model
property var itemModel: model
function calculateWidth(){
var w = Math.max(list_root.width, item_layout_row.implicitWidth + 10);
if(expanded){
for(var i = 0; i < repeater_items.count; i++) {
var child = repeater_items.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
}
return w;
}
Item{
id:item_layout_rect
width: list_root.contentWidth
height: item_layout_row.implicitHeight
Rectangle{
anchors.fill: parent
anchors.margins: 2
color:{
if(FluTheme.dark){
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){
return Qt.rgba(62/255,62/255,62/255,1)
}
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(62/255,62/255,62/255,1):Qt.rgba(0,0,0,0)
}else{
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){
return Qt.rgba(0,0,0,0.06)
}
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(0,0,0,0.03):Qt.rgba(0,0,0,0)
}
}
Rectangle{
width: 3
color:FluTheme.primaryColor.dark
visible: item_layout.singleSelected && (selectionMode === FluTreeViewType.Single)
radius: 3
height: 20
anchors{
left: parent.left
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_layout_mouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
item_layout_rect.onClickItem()
}
}
}
function onClickItem(){
if(selectionMode === FluTreeViewType.None){
itemClicked(model)
}
if(selectionMode === FluTreeViewType.Single){
currentElement = model
if(item_layout.parent.parent.parent.itemModel){
currentParentElement = item_layout.parent.parent.parent.itemModel
}else{
if(item_layout.parent.itemModel){
currentParentElement = item_layout.parent.itemModel
}
}
itemClicked(model)
}
if(selectionMode === FluTreeViewType.Multiple){
}
}
RowLayout{
id:item_layout_row
anchors.verticalCenter: item_layout_rect.verticalCenter
Item{
width: 15*level
Layout.alignment: Qt.AlignVCenter
}
FluCheckBox{
id:item_layout_checkbox
text:""
checked: itemModel.multipSelected
visible: selectionMode === FluTreeViewType.Multiple
Layout.leftMargin: 5
function refreshCheckBox(){
const stack = [tree_model.get(0)];
const result = [];
while (stack.length > 0) {
const curr = stack.pop();
result.unshift(curr);
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipIndex",i)
curr.items.setProperty(i,"multipParentKey",curr.key)
stack.push(curr.items.get(i));
}
}
}
for(var j=0 ; j<result.length-1 ; j++){
var item = result[j]
let obj = result.find(function(o) {
return o.key === item.multipParentKey;
});
if((item.items !== undefined) && (item.items.count !== 0)){
var items = item.items
for(var k=0 ; k<items.count ; k++){
if(items.get(k).multipSelected === false){
obj.items.setProperty(item.multipIndex,"multipSelected",false)
break
}
obj.items.setProperty(item.multipIndex,"multipSelected",true)
}
}
}
}
clickListener:function(){
if(hasChild){
const stack = [itemModel];
while (stack.length > 0) {
const curr = stack.pop();
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipSelected",!itemModel.multipSelected)
stack.push(curr.items.get(i));
}
}
}
refreshCheckBox()
}else{
itemModel.multipSelected = !itemModel.multipSelected
refreshCheckBox()
}
}
}
FluIconButton{
id:item_layout_expanded
color:"#00000000"
opacity: item_layout.hasChild
onClicked: {
if(!item_layout.hasChild){
item_layout_rect.onClickItem()
return
}
model.expanded = !model.expanded
}
contentItem: FluIcon{
rotation: item_layout.expanded?0:-90
iconSource:FluentIcons.ChevronDown
iconSize: 15
Behavior on rotation {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
}
}
FluText {
text: item_layout.text
Layout.alignment: Qt.AlignVCenter
topPadding: 7
bottomPadding: 7
}
}
}
Item{
id:item_sub
visible: {
if(!hasChild){
return false
}
return item_layout.expanded??false
}
width: item_sub_layout.implicitWidth
height: item_sub_layout.implicitHeight
x:0.001
Column{
id: item_sub_layout
Repeater{
id:repeater_items
model: item_layout.items
delegate: delegate_items
}
}
}
}
}
ListView {
id: list_root
anchors.fill: parent
delegate: delegate_root
contentWidth: contentItem.childrenRect.width
model: tree_model model: tree_model
flickableDirection: Flickable.HorizontalAndVerticalFlick anchors.fill: parent
clip: true clip: true
boundsBehavior: ListView.StopAtBounds flickableDirection: Flickable.HorizontalAndVerticalFlick
ScrollBar.vertical: FluScrollBar {} contentWidth: contentItem.childrenRect.width
ScrollBar.horizontal: FluScrollBar { } reuseItems: true
} removeDisplaced : Transition{
function updateData(items){ ParallelAnimation{
rootModel.clear() NumberAnimation {
rootModel.append(items) properties: "y"
} duration: 167
function signleData(){ from: d.dy + table_view.height
return currentElement easing.type: Easing.OutCubic
} }
function multipData(){ NumberAnimation {
const stack = [tree_model.get(0)]; properties: "opacity"
const result = []; duration: 88
while (stack.length > 0) { from: 0
const curr = stack.pop(); to: 1
if(curr.multipSelected){ }
result.push(curr)
}
for(var i=0 ; i<curr.items.count ; i++){
stack.push(curr.items.get(i));
} }
} }
return result move: Transition {
} NumberAnimation { property: "y"; duration: 200 }
function createItem(text="",expanded=true,items=[],data={}){ }
return {text:text,expanded:expanded,items:items,key:uniqueRandom(),multipSelected:false,multipIndex:0,multipParentKey:"",data:data}; add: Transition{
} ParallelAnimation{
function uniqueRandom() { NumberAnimation {
var timestamp = Date.now(); properties: "y"
var random = Math.floor(Math.random() * 1000000); duration: 167
return timestamp.toString() + random.toString(); from: d.dy
easing.type: Easing.OutCubic
}
NumberAnimation {
properties: "opacity"
duration: 88
from: 0
to: 1
}
}
}
delegate: Item {
id:item_control
implicitWidth: item_loader_container.width
implicitHeight: item_loader_container.height
ListView.onReused: {
item_loader_container.item.reused()
}
ListView.onPooled: {
item_loader_container.item.pooled()
}
Loader{
property var itemControl: item_control
property var itemModel: modelData
property int rowIndex: index
property bool isItemLoader: true
id:item_loader_container
sourceComponent: com_item_container
}
}
Loader{
id:loader_container
property var itemControl
property var itemModel
property bool isItemLoader: false
}
} }
Component{
id:com_item_container
Item{
signal reused
signal pooled
onReused: {
}
onPooled: {
}
property bool isCurrent: d.current === itemModel
id:item_container
width: {
var w = 46 + item_loader_cell.width + control.depthPadding*itemModel.depth
if(control.width>w){
return control.width
}
return w
}
height: control.cellHeight
implicitWidth: width
implicitHeight: height
function toggle(){
var pos = FluTools.cursorPos()
var viewPos = table_view.mapToGlobal(0,0)
d.dy = table_view.contentY + pos.y-viewPos.y
if(itemModel.isExpanded){
tree_model.collapse(rowIndex)
}else{
tree_model.expand(rowIndex)
}
}
Rectangle{
width: 3
height: 18
radius: 1.5
color: FluTheme.primaryColor.dark
visible: isCurrent
anchors{
left: parent.left
leftMargin: 6
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_mouse
property point clickPos: Qt.point(0,0)
anchors.fill: parent
drag.target:control.draggable ? loader_container : undefined
hoverEnabled: true
drag.onActiveChanged: {
if(drag.active){
if(itemModel.isExpanded && itemModel.hasChildren()){
tree_model.collapse(rowIndex)
}
d.dragIndex = rowIndex
loader_container.sourceComponent = com_item_container
}
}
onPressed:
(mouse)=>{
clickPos = Qt.point(mouse.x,mouse.y)
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 = 0
loader_container.y = cellPosition.y
}
onClicked: {
d.current = itemModel
}
onDoubleClicked: {
if(itemModel.hasChildren()){
item_container.toggle()
}
}
onPositionChanged:
(mouse)=> {
if(!drag.active){
return
}
var cellPosition = item_container.mapToItem(table_view, 0, 0)
if(mouse.y+cellPosition.y<0 || mouse.y+cellPosition.y>table_view.height){
d.dropIndex = -1
return
}
if((mouse.x-table_view.contentX)>table_view.width || (mouse.x-table_view.contentX)<0){
d.dropIndex = -1
return
}
var pos = FluTools.cursorPos()
var viewPos = table_view.mapToGlobal(0,0)
var y = table_view.contentY + pos.y-viewPos.y
var index = Math.floor(y/control.cellHeight)
if(index<0 || index>table_view.count-1){
d.dropIndex = -1
return
}
console.debug(index)
if(tree_model.hitHasChildrenExpanded(index) && y>index*control.cellHeight+control.cellHeight/2){
d.dropIndex = index + 1
d.isDropTopArea = true
}else{
d.dropIndex = index
if(y>index*control.cellHeight+control.cellHeight/2){
d.isDropTopArea = false
}else{
d.isDropTopArea = true
}
}
}
onCanceled: {
loader_container.sourceComponent = undefined
loader_container.x = 0
loader_container.y = 0
d.dropIndex = -1
d.dragIndex = -1
}
onReleased: {
loader_container.sourceComponent = undefined
if(d.dropIndex !== -1){
tree_model.dragAnddrop(d.dragIndex,d.dropIndex,d.isDropTopArea)
}
d.dropIndex = -1
d.dragIndex = -1
loader_container.x = 0
loader_container.y = 0
}
}
Drag.active: item_mouse.drag.active
Rectangle{
id:item_line_drop_tip
anchors{
left: layout_row.left
leftMargin: 26
right: parent.right
rightMargin: 10
bottom: parent.bottom
bottomMargin: -1.5
top: undefined
}
states: [
State {
when:d.isDropTopArea
AnchorChanges {
target: item_line_drop_tip
anchors.top: item_container.top
anchors.bottom: undefined
}
PropertyChanges {
target: item_line_drop_tip
anchors.topMargin: -1.5
}
}
]
height: 3
radius: 1.5
color: d.hitColor
visible: d.dropIndex === rowIndex
Rectangle{
width: 10
height: 10
radius: 5
border.width: 3
border.color: d.hitColor
color: FluTheme.dark ? FluColors.Black : FluColors.White
anchors{
top: parent.top
left: parent.left
topMargin: -3
leftMargin: -5
}
}
}
FluRectangle{
width: 1
color: control.lineColor
visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren()
height: itemModel.hideLineFooter() ? parent.height/2 : parent.height
anchors{
top: parent.top
left: item_line_h.left
}
}
FluRectangle{
id:item_line_h
height: 1
color: control.lineColor
visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren()
width: depthPadding - 10
anchors{
right: layout_row.left
rightMargin: -24
verticalCenter: parent.verticalCenter
}
}
Repeater{
model: Math.max(itemModel.depth-1,0)
delegate: FluRectangle{
required property int index
width: 1
color: control.lineColor
visible: control.showLine && isItemLoader && itemModel.depth !== 0 && itemModel.hasNextNodeByIndex(index)
anchors{
top:parent.top
bottom: parent.bottom
left: parent.left
leftMargin: control.depthPadding*(index+1) + 24
}
}
}
Rectangle{
anchors.fill: parent
radius: 4
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.06)
}
if(item_mouse.containsMouse || item_check_box.hovered){
return Qt.rgba(1,1,1,0.03)
}
if(item_loader_expand.item && item_loader_expand.item.hovered){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
}else{
if(isCurrent){
return Qt.rgba(0,0,0,0.06)
}
if(item_mouse.containsMouse || item_check_box.hovered){
return Qt.rgba(0,0,0,0.03)
}
if(item_loader_expand.item && item_loader_expand.item.hovered){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
}
}
}
RowLayout{
id:layout_row
height: parent.height
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
spacing: 0
anchors.leftMargin: 14 + control.depthPadding*itemModel.depth
Component{
id:com_icon_btn
FluIconButton{
opacity: itemModel.hasChildren()
onClicked: {
item_container.toggle()
}
contentItem:FluIcon{
rotation: itemModel.isExpanded?0:-90
iconSource:FluentIcons.ChevronDown
iconSize: 16
anchors.centerIn: parent
}
}
}
Loader{
id:item_loader_expand
Layout.preferredWidth: 20
Layout.preferredHeight: 20
sourceComponent: itemModel.hasChildren() ? com_icon_btn : undefined
Layout.alignment: Qt.AlignVCenter
}
FluCheckBox{
id:item_check_box
Layout.preferredWidth: 18
Layout.preferredHeight: 18
Layout.leftMargin: 5
horizontalPadding:0
verticalPadding: 0
checked: itemModel.checked
enableAnimation:false
visible: control.checkable
padding: 0
clickListener: function(){
tree_model.checkRow(rowIndex,!itemModel.checked)
}
Layout.alignment: Qt.AlignVCenter
}
Loader{
property var modelData: itemModel
property var itemMouse: item_mouse
id:item_loader_cell
Layout.leftMargin: 10
Layout.preferredWidth: {
if(item){
return item.width
}
return 0
}
Layout.fillHeight: true
sourceComponent:com_item_text
}
}
}
}
Component{
id:com_item_text
Item{
width: item_text.width
FluText {
id:item_text
text: modelData.title
rightPadding: 14
anchors.centerIn: parent
color:{
if(itemMouse.pressed){
return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
}
return FluTheme.dark ? FluColors.White : FluColors.Grey220
}
}
}
}
function selectionModel(){
return tree_model.selectionModel
}
function count(){
return tree_model.dataSourceSize
}
function visibleCount(){
return table_view.count
}
function collapse(rowIndex){
tree_model.collapse(rowIndex)
}
function expand(rowIndex){
tree_model.expand(rowIndex)
}
function allExpand(){
tree_model.allExpand()
}
function allCollapse(){
tree_model.allCollapse()
}
} }

View File

@ -107,7 +107,9 @@ Window {
MouseArea{ MouseArea{
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
popup_loading.visible = false if (cancel){
popup_loading.visible = false
}
} }
} }
ColumnLayout{ ColumnLayout{

View File

@ -29,6 +29,7 @@ Module {
exports: ["FluentUI/FluCaptcha 1.0"] exports: ["FluentUI/FluCaptcha 1.0"]
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
Property { name: "font"; type: "QFont" } Property { name: "font"; type: "QFont" }
Property { name: "ignoreCase"; type: "bool" }
Method { name: "refresh" } Method { name: "refresh" }
Method { Method {
name: "verify" name: "verify"
@ -185,6 +186,15 @@ Module {
} }
} }
} }
Component {
name: "FluRectangle"
defaultProperty: "data"
prototype: "QQuickPaintedItem"
exports: ["FluentUI/FluRectangle 1.0"]
exportMetaObjectRevisions: [0]
Property { name: "color"; type: "QColor" }
Property { name: "radius"; type: "QList<int>" }
}
Component { Component {
name: "FluScreenshotType" name: "FluScreenshotType"
exports: ["FluentUI/FluScreenshotType 1.0"] exports: ["FluentUI/FluScreenshotType 1.0"]
@ -276,6 +286,49 @@ Module {
} }
} }
} }
Component {
name: "FluTreeModel"
prototype: "QAbstractTableModel"
exports: ["FluentUI/FluTreeModel 1.0"]
exportMetaObjectRevisions: [0]
Property { name: "dataSourceSize"; type: "int" }
Method {
name: "removeRows"
Parameter { name: "row"; type: "int" }
Parameter { name: "count"; type: "int" }
}
Method {
name: "insertRows"
Parameter { name: "row"; type: "int" }
Parameter { name: "data"; type: "QList<Node*>" }
}
Method {
name: "getRow"
type: "QObject*"
Parameter { name: "row"; type: "int" }
}
Method {
name: "setData"
Parameter { name: "data"; type: "QList<Node*>" }
}
Method {
name: "setDataSource"
Parameter { name: "data"; type: "QList<QMap<QString,QVariant> >" }
}
Method {
name: "collapse"
Parameter { name: "row"; type: "int" }
}
Method {
name: "expand"
Parameter { name: "row"; type: "int" }
}
Method {
name: "dragAnddrop"
Parameter { name: "dragIndex"; type: "int" }
Parameter { name: "dropIndex"; type: "int" }
}
}
Component { Component {
name: "FluTreeViewType" name: "FluTreeViewType"
exports: ["FluentUI/FluTreeViewType 1.0"] exports: ["FluentUI/FluTreeViewType 1.0"]
@ -1797,6 +1850,280 @@ Module {
Property { name: "downloadSavePath"; type: "string" } Property { name: "downloadSavePath"; type: "string" }
Method { name: "httpId"; type: "string" } Method { name: "httpId"; type: "string" }
} }
Component {
name: "QAbstractItemModel"
prototype: "QObject"
Enum {
name: "LayoutChangeHint"
values: {
"NoLayoutChangeHint": 0,
"VerticalSortHint": 1,
"HorizontalSortHint": 2
}
}
Enum {
name: "CheckIndexOption"
values: {
"NoOption": 0,
"IndexIsValid": 1,
"DoNotUseParent": 2,
"ParentIsInvalid": 4
}
}
Signal {
name: "dataChanged"
Parameter { name: "topLeft"; type: "QModelIndex" }
Parameter { name: "bottomRight"; type: "QModelIndex" }
Parameter { name: "roles"; type: "QVector<int>" }
}
Signal {
name: "dataChanged"
Parameter { name: "topLeft"; type: "QModelIndex" }
Parameter { name: "bottomRight"; type: "QModelIndex" }
}
Signal {
name: "headerDataChanged"
Parameter { name: "orientation"; type: "Qt::Orientation" }
Parameter { name: "first"; type: "int" }
Parameter { name: "last"; type: "int" }
}
Signal {
name: "layoutChanged"
Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
}
Signal {
name: "layoutChanged"
Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
}
Signal { name: "layoutChanged" }
Signal {
name: "layoutAboutToBeChanged"
Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
Parameter { name: "hint"; type: "QAbstractItemModel::LayoutChangeHint" }
}
Signal {
name: "layoutAboutToBeChanged"
Parameter { name: "parents"; type: "QList<QPersistentModelIndex>" }
}
Signal { name: "layoutAboutToBeChanged" }
Signal {
name: "rowsAboutToBeInserted"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "first"; type: "int" }
Parameter { name: "last"; type: "int" }
}
Signal {
name: "rowsInserted"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "first"; type: "int" }
Parameter { name: "last"; type: "int" }
}
Signal {
name: "rowsAboutToBeRemoved"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "first"; type: "int" }
Parameter { name: "last"; type: "int" }
}
Signal {
name: "rowsRemoved"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "first"; type: "int" }
Parameter { name: "last"; type: "int" }
}
Signal {
name: "columnsAboutToBeInserted"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "first"; type: "int" }
Parameter { name: "last"; type: "int" }
}
Signal {
name: "columnsInserted"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "first"; type: "int" }
Parameter { name: "last"; type: "int" }
}
Signal {
name: "columnsAboutToBeRemoved"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "first"; type: "int" }
Parameter { name: "last"; type: "int" }
}
Signal {
name: "columnsRemoved"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "first"; type: "int" }
Parameter { name: "last"; type: "int" }
}
Signal { name: "modelAboutToBeReset" }
Signal { name: "modelReset" }
Signal {
name: "rowsAboutToBeMoved"
Parameter { name: "sourceParent"; type: "QModelIndex" }
Parameter { name: "sourceStart"; type: "int" }
Parameter { name: "sourceEnd"; type: "int" }
Parameter { name: "destinationParent"; type: "QModelIndex" }
Parameter { name: "destinationRow"; type: "int" }
}
Signal {
name: "rowsMoved"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "start"; type: "int" }
Parameter { name: "end"; type: "int" }
Parameter { name: "destination"; type: "QModelIndex" }
Parameter { name: "row"; type: "int" }
}
Signal {
name: "columnsAboutToBeMoved"
Parameter { name: "sourceParent"; type: "QModelIndex" }
Parameter { name: "sourceStart"; type: "int" }
Parameter { name: "sourceEnd"; type: "int" }
Parameter { name: "destinationParent"; type: "QModelIndex" }
Parameter { name: "destinationColumn"; type: "int" }
}
Signal {
name: "columnsMoved"
Parameter { name: "parent"; type: "QModelIndex" }
Parameter { name: "start"; type: "int" }
Parameter { name: "end"; type: "int" }
Parameter { name: "destination"; type: "QModelIndex" }
Parameter { name: "column"; type: "int" }
}
Method { name: "submit"; type: "bool" }
Method { name: "revert" }
Method {
name: "hasIndex"
type: "bool"
Parameter { name: "row"; type: "int" }
Parameter { name: "column"; type: "int" }
Parameter { name: "parent"; type: "QModelIndex" }
}
Method {
name: "hasIndex"
type: "bool"
Parameter { name: "row"; type: "int" }
Parameter { name: "column"; type: "int" }
}
Method {
name: "index"
type: "QModelIndex"
Parameter { name: "row"; type: "int" }
Parameter { name: "column"; type: "int" }
Parameter { name: "parent"; type: "QModelIndex" }
}
Method {
name: "index"
type: "QModelIndex"
Parameter { name: "row"; type: "int" }
Parameter { name: "column"; type: "int" }
}
Method {
name: "parent"
type: "QModelIndex"
Parameter { name: "child"; type: "QModelIndex" }
}
Method {
name: "sibling"
type: "QModelIndex"
Parameter { name: "row"; type: "int" }
Parameter { name: "column"; type: "int" }
Parameter { name: "idx"; type: "QModelIndex" }
}
Method {
name: "rowCount"
type: "int"
Parameter { name: "parent"; type: "QModelIndex" }
}
Method { name: "rowCount"; type: "int" }
Method {
name: "columnCount"
type: "int"
Parameter { name: "parent"; type: "QModelIndex" }
}
Method { name: "columnCount"; type: "int" }
Method {
name: "hasChildren"
type: "bool"
Parameter { name: "parent"; type: "QModelIndex" }
}
Method { name: "hasChildren"; type: "bool" }
Method {
name: "data"
type: "QVariant"
Parameter { name: "index"; type: "QModelIndex" }
Parameter { name: "role"; type: "int" }
}
Method {
name: "data"
type: "QVariant"
Parameter { name: "index"; type: "QModelIndex" }
}
Method {
name: "setData"
type: "bool"
Parameter { name: "index"; type: "QModelIndex" }
Parameter { name: "value"; type: "QVariant" }
Parameter { name: "role"; type: "int" }
}
Method {
name: "setData"
type: "bool"
Parameter { name: "index"; type: "QModelIndex" }
Parameter { name: "value"; type: "QVariant" }
}
Method {
name: "headerData"
type: "QVariant"
Parameter { name: "section"; type: "int" }
Parameter { name: "orientation"; type: "Qt::Orientation" }
Parameter { name: "role"; type: "int" }
}
Method {
name: "headerData"
type: "QVariant"
Parameter { name: "section"; type: "int" }
Parameter { name: "orientation"; type: "Qt::Orientation" }
}
Method {
name: "fetchMore"
Parameter { name: "parent"; type: "QModelIndex" }
}
Method {
name: "canFetchMore"
type: "bool"
Parameter { name: "parent"; type: "QModelIndex" }
}
Method {
name: "flags"
type: "Qt::ItemFlags"
Parameter { name: "index"; type: "QModelIndex" }
}
Method {
name: "match"
type: "QModelIndexList"
Parameter { name: "start"; type: "QModelIndex" }
Parameter { name: "role"; type: "int" }
Parameter { name: "value"; type: "QVariant" }
Parameter { name: "hits"; type: "int" }
Parameter { name: "flags"; type: "Qt::MatchFlags" }
}
Method {
name: "match"
type: "QModelIndexList"
Parameter { name: "start"; type: "QModelIndex" }
Parameter { name: "role"; type: "int" }
Parameter { name: "value"; type: "QVariant" }
Parameter { name: "hits"; type: "int" }
}
Method {
name: "match"
type: "QModelIndexList"
Parameter { name: "start"; type: "QModelIndex" }
Parameter { name: "role"; type: "int" }
Parameter { name: "value"; type: "QVariant" }
}
}
Component { name: "QAbstractTableModel"; prototype: "QAbstractItemModel" }
Component { Component {
name: "QRCode" name: "QRCode"
defaultProperty: "data" defaultProperty: "data"
@ -1934,7 +2261,7 @@ Module {
exports: ["FluentUI/FluAcrylic 1.0"] exports: ["FluentUI/FluAcrylic 1.0"]
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
isComposite: true isComposite: true
defaultProperty: "contentItem" defaultProperty: "data"
Property { name: "tintColor"; type: "QColor" } Property { name: "tintColor"; type: "QColor" }
Property { name: "tintOpacity"; type: "double" } Property { name: "tintOpacity"; type: "double" }
Property { name: "luminosity"; type: "double" } Property { name: "luminosity"; type: "double" }
@ -1942,8 +2269,6 @@ Module {
Property { name: "blurRadius"; type: "int" } Property { name: "blurRadius"; type: "int" }
Property { name: "targetRect"; type: "QRectF" } Property { name: "targetRect"; type: "QRectF" }
Property { name: "target"; type: "QQuickItem"; isPointer: true } Property { name: "target"; type: "QQuickItem"; isPointer: true }
Property { name: "radius"; type: "QVariant" }
Property { name: "contentItem"; type: "QObject"; isList: true; isReadonly: true }
} }
Component { Component {
prototype: "QQuickRectangle" prototype: "QQuickRectangle"
@ -2159,7 +2484,7 @@ Module {
exports: ["FluentUI/FluCarousel 1.0"] exports: ["FluentUI/FluCarousel 1.0"]
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
isComposite: true isComposite: true
defaultProperty: "contentItem" defaultProperty: "data"
Property { name: "autoPlay"; type: "bool" } Property { name: "autoPlay"; type: "bool" }
Property { name: "loopTime"; type: "int" } Property { name: "loopTime"; type: "int" }
Property { name: "model"; type: "QVariant" } Property { name: "model"; type: "QVariant" }
@ -2178,8 +2503,6 @@ Module {
type: "QVariant" type: "QVariant"
Parameter { name: "index"; type: "QVariant" } Parameter { name: "index"; type: "QVariant" }
} }
Property { name: "radius"; type: "QVariant" }
Property { name: "contentItem"; type: "QObject"; isList: true; isReadonly: true }
} }
Component { Component {
prototype: "QQuickCanvasItem" prototype: "QQuickCanvasItem"
@ -2229,6 +2552,14 @@ Module {
Property { name: "clickListener"; type: "QVariant" } Property { name: "clickListener"; type: "QVariant" }
Property { name: "textColor"; type: "QColor" } Property { name: "textColor"; type: "QColor" }
} }
Component {
prototype: "FluRectangle"
name: "FluentUI/FluClip 1.0"
exports: ["FluentUI/FluClip 1.0"]
exportMetaObjectRevisions: [0]
isComposite: true
defaultProperty: "data"
}
Component { Component {
prototype: "QQuickButton" prototype: "QQuickButton"
name: "FluentUI/FluColorPicker 1.0" name: "FluentUI/FluColorPicker 1.0"
@ -2530,16 +2861,6 @@ Module {
} }
Property { name: "children"; type: "QObject"; isList: true; isReadonly: true } Property { name: "children"; type: "QObject"; isList: true; isReadonly: true }
} }
Component {
prototype: "QQuickItem"
name: "FluentUI/FluItem 1.0"
exports: ["FluentUI/FluItem 1.0"]
exportMetaObjectRevisions: [0]
isComposite: true
defaultProperty: "contentItem"
Property { name: "radius"; type: "QVariant" }
Property { name: "contentItem"; type: "QObject"; isList: true; isReadonly: true }
}
Component { Component {
prototype: "QQuickItemDelegate" prototype: "QQuickItemDelegate"
name: "FluentUI/FluItemDelegate 1.0" name: "FluentUI/FluItemDelegate 1.0"
@ -2638,16 +2959,16 @@ Module {
defaultProperty: "data" defaultProperty: "data"
Property { name: "logo"; type: "QUrl" } Property { name: "logo"; type: "QUrl" }
Property { name: "title"; type: "string" } Property { name: "title"; type: "string" }
Property { name: "items"; type: "FluObject_QMLTYPE_156"; isPointer: true } Property { name: "items"; type: "FluObject_QMLTYPE_148"; isPointer: true }
Property { name: "footerItems"; type: "FluObject_QMLTYPE_156"; isPointer: true } Property { name: "footerItems"; type: "FluObject_QMLTYPE_148"; isPointer: true }
Property { name: "displayMode"; type: "int" } Property { name: "displayMode"; type: "int" }
Property { name: "autoSuggestBox"; type: "QQmlComponent"; isPointer: true } Property { name: "autoSuggestBox"; type: "QQmlComponent"; isPointer: true }
Property { name: "actionItem"; type: "QQmlComponent"; isPointer: true } Property { name: "actionItem"; type: "QQmlComponent"; isPointer: true }
Property { name: "topPadding"; type: "int" } Property { name: "topPadding"; type: "int" }
Property { name: "navWidth"; type: "int" } Property { name: "navWidth"; type: "int" }
Property { name: "pageMode"; type: "int" } Property { name: "pageMode"; type: "int" }
Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_48"; isPointer: true } Property { name: "navItemRightMenu"; type: "FluMenu_QMLTYPE_52"; isPointer: true }
Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_48"; isPointer: true } Property { name: "navItemExpanderRightMenu"; type: "FluMenu_QMLTYPE_52"; isPointer: true }
Signal { name: "logoClicked" } Signal { name: "logoClicked" }
Method { name: "collapseAll"; type: "QVariant" } Method { name: "collapseAll"; type: "QVariant" }
Method { Method {
@ -2961,18 +3282,6 @@ Module {
Property { name: "size"; type: "int" } Property { name: "size"; type: "int" }
Property { name: "value"; type: "int" } Property { name: "value"; type: "int" }
} }
Component {
prototype: "QQuickItem"
name: "FluentUI/FluRectangle 1.0"
exports: ["FluentUI/FluRectangle 1.0"]
exportMetaObjectRevisions: [0]
isComposite: true
defaultProperty: "contentItem"
Property { name: "radius"; type: "QVariant" }
Property { name: "color"; type: "QColor" }
Property { name: "shadow"; type: "bool" }
Property { name: "contentItem"; type: "QObject"; isList: true; isReadonly: true }
}
Component { Component {
prototype: "QQuickItem" prototype: "QQuickItem"
name: "FluentUI/FluRemoteLoader 1.0" name: "FluentUI/FluRemoteLoader 1.0"
@ -3221,13 +3530,14 @@ Module {
} }
} }
Component { Component {
prototype: "QQuickRectangle" prototype: "FluRectangle"
name: "FluentUI/FluTextBoxBackground 1.0" name: "FluentUI/FluTextBoxBackground 1.0"
exports: ["FluentUI/FluTextBoxBackground 1.0"] exports: ["FluentUI/FluTextBoxBackground 1.0"]
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
isComposite: true isComposite: true
defaultProperty: "data" defaultProperty: "data"
Property { name: "inputItem"; type: "QQuickItem"; isPointer: true } Property { name: "inputItem"; type: "QQuickItem"; isPointer: true }
Property { name: "borderWidth"; type: "int" }
} }
Component { Component {
prototype: "QQuickMenu" prototype: "QQuickMenu"
@ -3362,30 +3672,13 @@ Module {
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
isComposite: true isComposite: true
defaultProperty: "data" defaultProperty: "data"
Property { name: "selectionMode"; type: "int" } Property { name: "currentIndex"; type: "int" }
Property { name: "currentElement"; type: "QVariant" } Property { name: "dataSource"; type: "QVariant" }
Property { name: "currentParentElement"; type: "QVariant" } Property { name: "showLine"; type: "bool" }
Property { name: "rootModel"; type: "QVariant" } Property { name: "draggable"; type: "bool" }
Signal { Property { name: "lineColor"; type: "QColor" }
name: "itemClicked" Method { name: "count"; type: "QVariant" }
Parameter { name: "item"; type: "QVariant" } Method { name: "visibleCount"; type: "QVariant" }
}
Method {
name: "updateData"
type: "QVariant"
Parameter { name: "items"; type: "QVariant" }
}
Method { name: "signleData"; type: "QVariant" }
Method { name: "multipData"; type: "QVariant" }
Method {
name: "createItem"
type: "QVariant"
Parameter { name: "text"; type: "QVariant" }
Parameter { name: "expanded"; type: "QVariant" }
Parameter { name: "items"; type: "QVariant" }
Parameter { name: "data"; type: "QVariant" }
}
Method { name: "uniqueRandom"; type: "QVariant" }
} }
Component { Component {
prototype: "QQuickWindowQmlImpl" prototype: "QQuickWindowQmlImpl"

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,4 +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
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,5 +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/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

@ -23,6 +23,7 @@ Button {
property alias textColor: btn_text.textColor property alias textColor: btn_text.textColor
property bool textRight: true property bool textRight: true
property real textSpacing: 6 property real textSpacing: 6
property bool enableAnimation: FluTheme.enableAnimation
property var clickListener : function(){ property var clickListener : function(){
checked = !checked checked = !checked
} }
@ -35,8 +36,9 @@ Button {
visible: control.activeFocus visible: control.activeFocus
} }
} }
horizontalPadding:2 horizontalPadding:0
verticalPadding: 2 verticalPadding: 0
padding: 0
Accessible.role: Accessible.Button Accessible.role: Accessible.Button
Accessible.name: control.text Accessible.name: control.text
Accessible.description: contentDescription Accessible.description: contentDescription
@ -87,7 +89,7 @@ Button {
return normalColor return normalColor
} }
Behavior on color { Behavior on color {
enabled: FluTheme.enableAnimation enabled: control.enableAnimation
ColorAnimation{ ColorAnimation{
duration: 83 duration: 83
} }
@ -99,7 +101,7 @@ Button {
visible: checked visible: checked
iconColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1) iconColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
Behavior on visible { Behavior on visible {
enabled: FluTheme.enableAnimation enabled: control.enableAnimation
NumberAnimation{ NumberAnimation{
duration: 83 duration: 83
} }

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

@ -4,7 +4,7 @@ import QtQuick.Controls.Basic
import FluentUI import FluentUI
import QtQuick.Templates as T import QtQuick.Templates as T
ComboBox { T.ComboBox {
id: control id: control
signal commit(string text) signal commit(string text)
property bool disabled: false property bool disabled: false
@ -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: {
@ -72,6 +72,7 @@ ComboBox {
Keys.onReturnPressed:(event)=> handleCommit(event) Keys.onReturnPressed:(event)=> handleCommit(event)
function handleCommit(event){ function handleCommit(event){
control.commit(control.editText) control.commit(control.editText)
accepted()
} }
} }

View File

@ -5,14 +5,13 @@ 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{ FluRectangle{
id:separator id:separator
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

@ -83,7 +83,7 @@ FluObject {
} }
Timer { Timer {
id:delayTimer id:delayTimer
interval: duration; running: true; repeat: true interval: duration; running: duration > 0; repeat: duration > 0
onTriggered: content.close(); onTriggered: content.close();
} }
Loader{ Loader{
@ -184,10 +184,47 @@ FluObject {
} }
} }
FluText{ Column{
text:_super.text spacing: 5
wrapMode: Text.WrapAnywhere FluText{
width: Math.min(implicitWidth,mcontrol.maxWidth) text:_super.text
wrapMode: Text.WrapAnywhere
width: Math.min(implicitWidth,mcontrol.maxWidth)
}
FluText{
text: _super.moremsg
visible: _super.moremsg
wrapMode : Text.WordWrap
textColor: FluColors.Grey120
}
}
FluIconButton{
iconSource: FluentIcons.ChromeClose
iconSize: 10
y:5
x:parent.width-35
visible: _super.duration<=0
iconColor: {
if(FluTheme.dark){
switch(_super.type){
case mcontrol.const_success: return Qt.rgba(108/255,203/255,95/255,1);
case mcontrol.const_warning: return Qt.rgba(252/255,225/255,0/255,1);
case mcontrol.const_info: return FluTheme.primaryColor.lighter;
case mcontrol.const_error: return Qt.rgba(255/255,153/255,164/255,1);
}
return "#FFFFFF"
}else{
switch(_super.type){
case mcontrol.const_success: return "#0f7b0f";
case mcontrol.const_warning: return "#9d5d00";
case mcontrol.const_info: return "#0066b4";
case mcontrol.const_error: return "#c42b1c";
}
return "#FFFFFF"
}
}
onClicked: _super.close()
} }
} }
} }

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

@ -5,28 +5,28 @@ import FluentUI
T.ItemDelegate { T.ItemDelegate {
id: control id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding) implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding, implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding) implicitIndicatorHeight + topPadding + bottomPadding)
padding: 0
padding: 12 verticalPadding: 8
spacing: 8 horizontalPadding: 10
icon.width: 24
icon.height: 24
icon.color: control.palette.text icon.color: control.palette.text
contentItem:FluText {
contentItem: FluText {
text: control.text text: control.text
font: control.font font: control.font
color:{
if(control.down){
return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
}
return FluTheme.dark ? FluColors.White : FluColors.Grey220
}
} }
background: Rectangle { background: Rectangle {
implicitWidth: 100 implicitWidth: 100
implicitHeight: 40 implicitHeight: 30
color:{ color:{
if(FluTheme.dark){ if(FluTheme.dark){
return Qt.rgba(1,1,1,0.05) return Qt.rgba(1,1,1,0.05)

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,7 +802,9 @@ Item {
anchors.fill: loader_content anchors.fill: loader_content
onDropped: onDropped:
(drag)=>{ (drag)=>{
drag.source.modelData.dropped(drag) if(drag.source.modelData){
drag.source.modelData.dropped(drag)
}
} }
} }
Loader{ Loader{
@ -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

@ -8,6 +8,7 @@ Item {
property int launchMode: FluPageType.SingleTop property int launchMode: FluPageType.SingleTop
property bool animDisabled: false property bool animDisabled: false
property string url : "" property string url : ""
signal animationEnd()
id: control id: control
opacity: visible opacity: visible
visible: false visible: false
@ -30,5 +31,13 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
visible = true visible = true
timer.restart()
}
Timer{
id:timer
interval: !animDisabled && FluTheme.enableAnimation ? 200 : 0
onTriggered: {
control.animationEnd()
}
} }
} }

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

@ -32,27 +32,18 @@ T.RangeSlider {
radius: 12 radius: 12
} }
Rectangle{ Rectangle{
width: 24 width: radius*2
height: 24 height: radius*2
radius: 12 radius:{
if(control.first.pressed){
return 5
}
return control.first.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.first.pressed){
return 4/10
}
return control.first.hovered ? 6/10 : 5/10
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
} }
} }
second.handle: Rectangle { second.handle: Rectangle {
x: control.leftPadding + (control.horizontal ? control.second.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2) x: control.leftPadding + (control.horizontal ? control.second.visualPosition * (control.availableWidth - width) : (control.availableWidth - width) / 2)
y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.second.visualPosition * (control.availableHeight - height)) y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : control.second.visualPosition * (control.availableHeight - height))
@ -64,27 +55,18 @@ T.RangeSlider {
radius: 12 radius: 12
} }
Rectangle{ Rectangle{
width: 24 width: radius*2
height: 24 height: radius*2
radius: 12 radius:{
if(control.second.pressed){
return 5
}
return control.second.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.second.pressed){
return 4/10
}
return control.second.hovered ? 6/10 : 5/10
}
Behavior on scale {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
} }
} }
background: Item { background: Item {
x: control.leftPadding + (control.horizontal ? 0 : (control.availableWidth - width) / 2) x: control.leftPadding + (control.horizontal ? 0 : (control.availableWidth - width) / 2)
y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0) y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0)

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

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

View File

@ -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,38 +1,34 @@
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
radius: 4 id:control
layer.enabled: true radius: [4,4,4,4]
color: { Rectangle{
if(inputItem.disabled){ radius: 4
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1) anchors.fill: parent
color: {
if(inputItem.disabled){
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
}
if(inputItem.activeFocus){
return FluTheme.dark ? Qt.rgba(36/255,36/255,36/255,1) : Qt.rgba(1,1,1,1)
}
if(inputItem.hovered){
return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
}
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
} }
if(inputItem.activeFocus){ border.width: control.borderWidth
return FluTheme.dark ? Qt.rgba(36/255,36/255,36/255,1) : Qt.rgba(1,1,1,1) border.color: {
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(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
} }
if(inputItem.hovered){
return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
}
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
}
layer.effect:OpacityMask {
maskSource: Rectangle {
width: content.width
height: content.height
radius: 4
}
}
border.width: 1
border.color: {
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(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
} }
Rectangle{ Rectangle{
width: parent.width width: parent.width

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
@ -92,14 +92,9 @@ Button {
return dotNormalColor return dotNormalColor
} }
Behavior on x { Behavior on x {
enabled: FluTheme.enableAnimation
NumberAnimation {
easing.type: Easing.OutCubic
}
}
Behavior on scale {
enabled: FluTheme.enableAnimation enabled: FluTheme.enableAnimation
NumberAnimation { NumberAnimation {
duration: 167
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }

View File

@ -2,291 +2,453 @@ import QtQuick
import QtQuick.Window import QtQuick.Window
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls import QtQuick.Controls
import Qt.labs.qmlmodels
import FluentUI import FluentUI
Item { Item {
property int selectionMode: FluTreeViewType.None property int currentIndex : -1
property var currentElement property var dataSource
property var currentParentElement property bool showLine: true
property var rootModel: tree_model.get(0).items property bool draggable: false
signal itemClicked(var item) property int cellHeight: 30
id:root property int depthPadding: 30
ListModel{ property bool checkable: false
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
QtObject {
id:d
property int dy
property var current
property int dropIndex: -1
property bool isDropTopArea: false
property int dragIndex: -1
property color hitColor: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
}
onDataSourceChanged: {
tree_model.setDataSource(dataSource)
}
FluTreeModel{
id:tree_model id:tree_model
ListElement{
text: "根节点"
expanded:true
items:[]
key:"123456"
multipSelected:false
multipIndex:0
multipParentKey:""
}
} }
Component{ ListView{
id: delegate_root id:table_view
Column{ ScrollBar.horizontal: FluScrollBar{}
width: calculateWidth() ScrollBar.vertical: FluScrollBar{}
property var itemModel: model boundsBehavior: Flickable.StopAtBounds
Repeater{
id: repeater_first_level
model: items
delegate: delegate_items
}
function calculateWidth(){
var w = 0;
for(var i = 0; i < repeater_first_level.count; i++) {
var child = repeater_first_level.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
return w;
}
}
}
Component{
id:delegate_items
Column{
id:item_layout
property real level: (mapToItem(list_root,0,0).x+list_root.contentX)/0.001
property var text: model.text??"Item"
property bool hasChild : (model.items !== undefined) && (model.items.count !== 0)
property var items: model.items??[]
property var expanded: model.expanded??true
property int width_hint: calculateWidth()
property bool singleSelected: currentElement === model
property var itemModel: model
function calculateWidth(){
var w = Math.max(list_root.width, item_layout_row.implicitWidth + 10);
if(expanded){
for(var i = 0; i < repeater_items.count; i++) {
var child = repeater_items.itemAt(i)
if(w < child.width_hint){
w = child.width_hint;
}
}
}
return w;
}
Item{
id:item_layout_rect
width: list_root.contentWidth
height: item_layout_row.implicitHeight
Rectangle{
anchors.fill: parent
anchors.margins: 2
color:{
if(FluTheme.dark){
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){
return Qt.rgba(62/255,62/255,62/255,1)
}
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(62/255,62/255,62/255,1):Qt.rgba(0,0,0,0)
}else{
if(item_layout.singleSelected && selectionMode === FluTreeViewType.Single){
return Qt.rgba(0,0,0,0.06)
}
return (item_layout_mouse.containsMouse || item_layout_expanded.hovered || item_layout_checkbox.hovered)?Qt.rgba(0,0,0,0.03):Qt.rgba(0,0,0,0)
}
}
Rectangle{
width: 3
color:FluTheme.primaryColor.dark
visible: item_layout.singleSelected && (selectionMode === FluTreeViewType.Single)
radius: 3
height: 20
anchors{
left: parent.left
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_layout_mouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
item_layout_rect.onClickItem()
}
}
}
function onClickItem(){
if(selectionMode === FluTreeViewType.None){
itemClicked(model)
}
if(selectionMode === FluTreeViewType.Single){
currentElement = model
if(item_layout.parent.parent.parent.itemModel){
currentParentElement = item_layout.parent.parent.parent.itemModel
}else{
if(item_layout.parent.itemModel){
currentParentElement = item_layout.parent.itemModel
}
}
itemClicked(model)
}
if(selectionMode === FluTreeViewType.Multiple){
}
}
RowLayout{
id:item_layout_row
anchors.verticalCenter: item_layout_rect.verticalCenter
Item{
width: 15*level
Layout.alignment: Qt.AlignVCenter
}
FluCheckBox{
id:item_layout_checkbox
text:""
checked: itemModel.multipSelected
visible: selectionMode === FluTreeViewType.Multiple
Layout.leftMargin: 5
function refreshCheckBox(){
const stack = [tree_model.get(0)];
const result = [];
while (stack.length > 0) {
const curr = stack.pop();
result.unshift(curr);
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipIndex",i)
curr.items.setProperty(i,"multipParentKey",curr.key)
stack.push(curr.items.get(i));
}
}
}
for(var j=0 ; j<result.length-1 ; j++){
var item = result[j]
let obj = result.find(function(o) {
return o.key === item.multipParentKey;
});
if((item.items !== undefined) && (item.items.count !== 0)){
var items = item.items
for(var k=0 ; k<items.count ; k++){
if(items.get(k).multipSelected === false){
obj.items.setProperty(item.multipIndex,"multipSelected",false)
break
}
obj.items.setProperty(item.multipIndex,"multipSelected",true)
}
}
}
}
clickListener:function(){
if(hasChild){
const stack = [itemModel];
while (stack.length > 0) {
const curr = stack.pop();
if (curr.items) {
for(var i=0 ; i<curr.items.count ; i++){
curr.items.setProperty(i,"multipSelected",!itemModel.multipSelected)
stack.push(curr.items.get(i));
}
}
}
refreshCheckBox()
}else{
itemModel.multipSelected = !itemModel.multipSelected
refreshCheckBox()
}
}
}
FluIconButton{
id:item_layout_expanded
color:"#00000000"
opacity: item_layout.hasChild
onClicked: {
if(!item_layout.hasChild){
item_layout_rect.onClickItem()
return
}
model.expanded = !model.expanded
}
contentItem: FluIcon{
rotation: item_layout.expanded?0:-90
iconSource:FluentIcons.ChevronDown
iconSize: 15
Behavior on rotation {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
}
}
FluText {
text: item_layout.text
Layout.alignment: Qt.AlignVCenter
topPadding: 7
bottomPadding: 7
}
}
}
Item{
id:item_sub
visible: {
if(!hasChild){
return false
}
return item_layout.expanded??false
}
width: item_sub_layout.implicitWidth
height: item_sub_layout.implicitHeight
x:0.001
Column{
id: item_sub_layout
Repeater{
id:repeater_items
model: item_layout.items
delegate: delegate_items
}
}
}
}
}
ListView {
id: list_root
anchors.fill: parent
delegate: delegate_root
contentWidth: contentItem.childrenRect.width
model: tree_model model: tree_model
flickableDirection: Flickable.HorizontalAndVerticalFlick anchors.fill: parent
clip: true clip: true
boundsBehavior: ListView.StopAtBounds flickableDirection: Flickable.HorizontalAndVerticalFlick
ScrollBar.vertical: FluScrollBar {} contentWidth: contentItem.childrenRect.width
ScrollBar.horizontal: FluScrollBar { } reuseItems: true
} removeDisplaced : Transition{
function updateData(items){ ParallelAnimation{
rootModel.clear() NumberAnimation {
rootModel.append(items) properties: "y"
} duration: 167
function signleData(){ from: d.dy + table_view.height
return currentElement easing.type: Easing.OutCubic
} }
function multipData(){ NumberAnimation {
const stack = [tree_model.get(0)]; properties: "opacity"
const result = []; duration: 88
while (stack.length > 0) { from: 0
const curr = stack.pop(); to: 1
if(curr.multipSelected){ }
result.push(curr)
}
for(var i=0 ; i<curr.items.count ; i++){
stack.push(curr.items.get(i));
} }
} }
return result move: Transition {
} NumberAnimation { property: "y"; duration: 200 }
function createItem(text="",expanded=true,items=[],data={}){ }
return {text:text,expanded:expanded,items:items,key:uniqueRandom(),multipSelected:false,multipIndex:0,multipParentKey:"",data:data}; add: Transition{
} ParallelAnimation{
function uniqueRandom() { NumberAnimation {
var timestamp = Date.now(); properties: "y"
var random = Math.floor(Math.random() * 1000000); duration: 167
return timestamp.toString() + random.toString(); from: d.dy
easing.type: Easing.OutCubic
}
NumberAnimation {
properties: "opacity"
duration: 88
from: 0
to: 1
}
}
}
delegate: Item {
id:item_control
implicitWidth: item_loader_container.width
implicitHeight: item_loader_container.height
ListView.onReused: {
item_loader_container.item.reused()
}
ListView.onPooled: {
item_loader_container.item.pooled()
}
Loader{
property var itemControl: item_control
property var itemModel: modelData
property int rowIndex: index
property bool isItemLoader: true
id:item_loader_container
sourceComponent: com_item_container
}
}
Loader{
id:loader_container
property var itemControl
property var itemModel
property bool isItemLoader: false
}
} }
Component{
id:com_item_container
Item{
signal reused
signal pooled
onReused: {
}
onPooled: {
}
property bool isCurrent: d.current === itemModel
id:item_container
width: {
var w = 46 + item_loader_cell.width + control.depthPadding*itemModel.depth
if(control.width>w){
return control.width
}
return w
}
height: control.cellHeight
implicitWidth: width
implicitHeight: height
function toggle(){
var pos = FluTools.cursorPos()
var viewPos = table_view.mapToGlobal(0,0)
d.dy = table_view.contentY + pos.y-viewPos.y
if(itemModel.isExpanded){
tree_model.collapse(rowIndex)
}else{
tree_model.expand(rowIndex)
}
}
Rectangle{
width: 3
height: 18
radius: 1.5
color: FluTheme.primaryColor.dark
visible: isCurrent
anchors{
left: parent.left
leftMargin: 6
verticalCenter: parent.verticalCenter
}
}
MouseArea{
id:item_mouse
property point clickPos: Qt.point(0,0)
anchors.fill: parent
drag.target:control.draggable ? loader_container : undefined
hoverEnabled: true
drag.onActiveChanged: {
if(drag.active){
if(itemModel.isExpanded && itemModel.hasChildren()){
tree_model.collapse(rowIndex)
}
d.dragIndex = rowIndex
loader_container.sourceComponent = com_item_container
}
}
onPressed:
(mouse)=>{
clickPos = Qt.point(mouse.x,mouse.y)
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 = 0
loader_container.y = cellPosition.y
}
onClicked: {
d.current = itemModel
}
onDoubleClicked: {
if(itemModel.hasChildren()){
item_container.toggle()
}
}
onPositionChanged:
(mouse)=> {
if(!drag.active){
return
}
var cellPosition = item_container.mapToItem(table_view, 0, 0)
if(mouse.y+cellPosition.y<0 || mouse.y+cellPosition.y>table_view.height){
d.dropIndex = -1
return
}
if((mouse.x-table_view.contentX)>table_view.width || (mouse.x-table_view.contentX)<0){
d.dropIndex = -1
return
}
var pos = FluTools.cursorPos()
var viewPos = table_view.mapToGlobal(0,0)
var y = table_view.contentY + pos.y-viewPos.y
var index = Math.floor(y/control.cellHeight)
if(index<0 || index>table_view.count-1){
d.dropIndex = -1
return
}
console.debug(index)
if(tree_model.hitHasChildrenExpanded(index) && y>index*control.cellHeight+control.cellHeight/2){
d.dropIndex = index + 1
d.isDropTopArea = true
}else{
d.dropIndex = index
if(y>index*control.cellHeight+control.cellHeight/2){
d.isDropTopArea = false
}else{
d.isDropTopArea = true
}
}
}
onCanceled: {
loader_container.sourceComponent = undefined
loader_container.x = 0
loader_container.y = 0
d.dropIndex = -1
d.dragIndex = -1
}
onReleased: {
loader_container.sourceComponent = undefined
if(d.dropIndex !== -1){
tree_model.dragAnddrop(d.dragIndex,d.dropIndex,d.isDropTopArea)
}
d.dropIndex = -1
d.dragIndex = -1
loader_container.x = 0
loader_container.y = 0
}
}
Drag.active: item_mouse.drag.active
Rectangle{
id:item_line_drop_tip
anchors{
left: layout_row.left
leftMargin: 26
right: parent.right
rightMargin: 10
bottom: parent.bottom
bottomMargin: -1.5
top: undefined
}
states: [
State {
when:d.isDropTopArea
AnchorChanges {
target: item_line_drop_tip
anchors.top: item_container.top
anchors.bottom: undefined
}
PropertyChanges {
target: item_line_drop_tip
anchors.topMargin: -1.5
}
}
]
height: 3
radius: 1.5
color: d.hitColor
visible: d.dropIndex === rowIndex
Rectangle{
width: 10
height: 10
radius: 5
border.width: 3
border.color: d.hitColor
color: FluTheme.dark ? FluColors.Black : FluColors.White
anchors{
top: parent.top
left: parent.left
topMargin: -3
leftMargin: -5
}
}
}
FluRectangle{
width: 1
color: control.lineColor
visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren()
height: itemModel.hideLineFooter() ? parent.height/2 : parent.height
anchors{
top: parent.top
left: item_line_h.left
}
}
FluRectangle{
id:item_line_h
height: 1
color: control.lineColor
visible: control.showLine && isItemLoader && itemModel.depth !== 0 && !itemModel.hasChildren()
width: depthPadding - 10
anchors{
right: layout_row.left
rightMargin: -24
verticalCenter: parent.verticalCenter
}
}
Repeater{
model: Math.max(itemModel.depth-1,0)
delegate: FluRectangle{
required property int index
width: 1
color: control.lineColor
visible: control.showLine && isItemLoader && itemModel.depth !== 0 && itemModel.hasNextNodeByIndex(index)
anchors{
top:parent.top
bottom: parent.bottom
left: parent.left
leftMargin: control.depthPadding*(index+1) + 24
}
}
}
Rectangle{
anchors.fill: parent
radius: 4
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.06)
}
if(item_mouse.containsMouse || item_check_box.hovered){
return Qt.rgba(1,1,1,0.03)
}
if(item_loader_expand.item && item_loader_expand.item.hovered){
return Qt.rgba(1,1,1,0.03)
}
return Qt.rgba(0,0,0,0)
}else{
if(isCurrent){
return Qt.rgba(0,0,0,0.06)
}
if(item_mouse.containsMouse || item_check_box.hovered){
return Qt.rgba(0,0,0,0.03)
}
if(item_loader_expand.item && item_loader_expand.item.hovered){
return Qt.rgba(0,0,0,0.03)
}
return Qt.rgba(0,0,0,0)
}
}
}
RowLayout{
id:layout_row
height: parent.height
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
spacing: 0
anchors.leftMargin: 14 + control.depthPadding*itemModel.depth
Component{
id:com_icon_btn
FluIconButton{
opacity: itemModel.hasChildren()
onClicked: {
item_container.toggle()
}
contentItem:FluIcon{
rotation: itemModel.isExpanded?0:-90
iconSource:FluentIcons.ChevronDown
iconSize: 16
anchors.centerIn: parent
}
}
}
Loader{
id:item_loader_expand
Layout.preferredWidth: 20
Layout.preferredHeight: 20
sourceComponent: itemModel.hasChildren() ? com_icon_btn : undefined
Layout.alignment: Qt.AlignVCenter
}
FluCheckBox{
id:item_check_box
Layout.preferredWidth: 18
Layout.preferredHeight: 18
Layout.leftMargin: 5
horizontalPadding:0
verticalPadding: 0
checked: itemModel.checked
enableAnimation:false
visible: control.checkable
padding: 0
clickListener: function(){
tree_model.checkRow(rowIndex,!itemModel.checked)
}
Layout.alignment: Qt.AlignVCenter
}
Loader{
property var modelData: itemModel
property var itemMouse: item_mouse
id:item_loader_cell
Layout.leftMargin: 10
Layout.preferredWidth: {
if(item){
return item.width
}
return 0
}
Layout.fillHeight: true
sourceComponent:com_item_text
}
}
}
}
Component{
id:com_item_text
Item{
width: item_text.width
FluText {
id:item_text
text: modelData.title
rightPadding: 14
anchors.centerIn: parent
color:{
if(itemMouse.pressed){
return FluTheme.dark ? FluColors.Grey80 : FluColors.Grey120
}
return FluTheme.dark ? FluColors.White : FluColors.Grey220
}
}
}
}
function selectionModel(){
return tree_model.selectionModel
}
function count(){
return tree_model.dataSourceSize
}
function visibleCount(){
return table_view.count
}
function collapse(rowIndex){
tree_model.collapse(rowIndex)
}
function expand(rowIndex){
tree_model.expand(rowIndex)
}
function allExpand(){
tree_model.allExpand()
}
function allCollapse(){
tree_model.allCollapse()
}
} }

View File

@ -106,7 +106,9 @@ Window {
MouseArea{ MouseArea{
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
popup_loading.visible = false if (cancel){
popup_loading.visible = false
}
} }
} }
ColumnLayout{ ColumnLayout{