Compare commits

...

32 Commits
1.5.5 ... 1.5.7

Author SHA1 Message Date
79a7c97fe8 update 2023-09-12 22:55:42 +08:00
fd30819393 update 2023-09-12 18:45:15 +08:00
05040ec4a9 update 2023-09-11 18:10:50 +08:00
0297445218 update 2023-09-10 17:05:36 +08:00
3990c95489 update 2023-09-10 16:58:23 +08:00
618b21854f Compatible with static build 2023-09-09 20:09:20 +08:00
ef40e3b109 update 2023-09-09 10:19:47 +08:00
f2b67af58a update 2023-09-08 23:01:31 +08:00
c0f15060af update 2023-09-08 22:43:52 +08:00
24f3cb1027 update 2023-09-08 22:33:23 +08:00
4b01fcf2b4 update 2023-09-08 19:14:45 +08:00
752fe8cfba update 2023-09-08 17:39:10 +08:00
e1292966c1 update 2023-09-08 13:54:40 +08:00
249c294926 update 2023-09-08 01:12:20 +08:00
573898149a update 2023-09-08 00:21:28 +08:00
c92d807ec1 update 2023-09-07 18:07:23 +08:00
531bffdf1a update 2023-09-06 23:17:33 +08:00
db47a75f6b update 2023-09-06 22:52:48 +08:00
ad79480345 update 2023-09-06 18:07:51 +08:00
ed5956d824 update 2023-09-06 14:05:29 +08:00
ddee70cdca add breakPointDownload property 2023-09-06 00:22:37 +08:00
d6bbe3a5ec update 2023-09-05 18:38:16 +08:00
d93d6b7c26 update 2023-09-05 16:48:04 +08:00
54be482833 update 2023-09-04 22:49:50 +08:00
f60eaec07c Merge branch 'main' of https://github.com/zhuzichu520/FluentUI 2023-09-04 20:45:56 +08:00
f7b7d30a6f update 2023-09-04 18:37:55 +08:00
ba32c92133 update 2023-09-04 11:29:15 +08:00
08c4f78454 update 2023-09-01 18:38:21 +08:00
e29150ca52 update THIRD_PARTY_COPYRIGHT 2023-09-01 09:37:07 +08:00
1be9103412 update 2023-08-31 18:06:05 +08:00
8583427a49 Merge branch 'main' of https://github.com/zhuzichu520/FluentUI 2023-08-30 18:44:13 +08:00
94a96cf75e update 2023-08-30 18:44:07 +08:00
99 changed files with 5020 additions and 1499 deletions

View File

@ -1,5 +1,6 @@
include(CMakeParseArguments) include(CMakeParseArguments)
find_package(Qt5 REQUIRED COMPONENTS Core) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
function(FindQmlPluginDump) function(FindQmlPluginDump)
get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION) get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION)
@ -35,7 +36,6 @@ function(add_qmlplugin TARGET)
${QMLPLUGIN_SOURCES} ${QMLPLUGIN_SOURCES}
) )
set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib) set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib)
add_custom_target("${TARGET}-qmlfiles" SOURCES ${QMLPLUGIN_QMLFILES})
if(QMLPLUGIN_NO_AUTORCC) if(QMLPLUGIN_NO_AUTORCC)
set_target_properties(${TARGET} PROPERTIES AUTOMOC OFF) set_target_properties(${TARGET} PROPERTIES AUTOMOC OFF)

View File

@ -1,5 +1,3 @@
// 应用程序版本信息
// 请勿修改此头文件,因为这个文件是自动生成的
#ifndef VERSION_H #ifndef VERSION_H
#define VERSION_H #define VERSION_H

View File

@ -263,3 +263,53 @@ zxing-cpp
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
************************************************************************************
ChartJs2QML
MIT License
Copyright (c) 2020 ChartJs2QML contributors (starting with Elypson, Michael A. Voelkel, https://github.com/Elypson)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
************************************************************************************
qml-colorpicker
MIT License
Copyright (c) 2022 Ruslan Shestopalyuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -122,7 +122,7 @@ else()
target_include_directories(example PRIVATE target_include_directories(example PRIVATE
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
) )
target_sources(example PRIVATE resource.qrc) target_sources(example PRIVATE example.qrc)
endif() endif()
#导入component头文件,不然通过QML_NAMED_ELEMENT生成的c++类会找不到头文件报错 #导入component头文件,不然通过QML_NAMED_ELEMENT生成的c++类会找不到头文件报错
@ -130,6 +130,13 @@ target_include_directories(example PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/component ${CMAKE_CURRENT_SOURCE_DIR}/src/component
) )
#如何是静态库则需要手动注册插件导入FluentUI.h头文件
if(FLUENTUI_BUILD_STATIC_LIB)
target_include_directories(example PRIVATE
${CMAKE_SOURCE_DIR}/src
)
endif()
#设置属性 #设置属性
set_target_properties(example PROPERTIES set_target_properties(example PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
@ -139,18 +146,6 @@ set_target_properties(example PROPERTIES
WIN32_EXECUTABLE TRUE WIN32_EXECUTABLE TRUE
) )
#链接库
if (FLUENTUI_BUILD_STATIC_LIB)
target_link_libraries(example PRIVATE
Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::Svg
Qt${QT_VERSION_MAJOR}::Network
fluentui
fluentuiplugin
FramelessHelper::Core
FramelessHelper::Quick
)
else()
target_link_libraries(example PRIVATE target_link_libraries(example PRIVATE
Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::Svg
@ -159,7 +154,6 @@ else()
FramelessHelper::Core FramelessHelper::Core
FramelessHelper::Quick FramelessHelper::Quick
) )
endif()
#安装 #安装
install(TARGETS example install(TARGETS example

View File

@ -126,7 +126,6 @@
<file>qml/component/CustomWindow.qml</file> <file>qml/component/CustomWindow.qml</file>
<file>qml/global/ItemsFooter.qml</file> <file>qml/global/ItemsFooter.qml</file>
<file>qml/global/ItemsOriginal.qml</file> <file>qml/global/ItemsOriginal.qml</file>
<file>qml/global/MainEvent.qml</file>
<file>qml/global/qmldir</file> <file>qml/global/qmldir</file>
<file>qml/page/T_Acrylic.qml</file> <file>qml/page/T_Acrylic.qml</file>
<file>qml/page/T_Awesome.qml</file> <file>qml/page/T_Awesome.qml</file>
@ -186,5 +185,7 @@
<file>res/image/image_1.jpg</file> <file>res/image/image_1.jpg</file>
<file>qml/window/PageWindow.qml</file> <file>qml/window/PageWindow.qml</file>
<file>qml/page/T_StaggeredView.qml</file> <file>qml/page/T_StaggeredView.qml</file>
<file>qml/viewmodel/SettingsViewModel.qml</file>
<file>qml/viewmodel/TextBoxViewModel.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -138,7 +138,9 @@ FluExpander{
"FluTimeline", "FluTimeline",
"FluChart", "FluChart",
"FluRangeSlider", "FluRangeSlider",
"FluStaggeredView" "FluStaggeredView",
"FluProgressButton",
"FluLoadingButton"
]; ];
code = code.replace(/\n/g, "<br>"); code = code.replace(/\n/g, "<br>");
code = code.replace(/ /g, "&nbsp;"); code = code.replace(/ /g, "&nbsp;");

View File

@ -88,7 +88,10 @@ FluObject{
} }
url:"qrc:/example/qml/page/T_Text.qml" url:"qrc:/example/qml/page/T_Text.qml"
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) } onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
onTap:{ navigationView.push(url) } onTap:{
item_text.count = 0
navigationView.push(url)
}
} }
FluPaneItem{ FluPaneItem{
title:"Image" title:"Image"
@ -394,7 +397,7 @@ FluObject{
onTap:{ navigationView.push(url) } onTap:{ navigationView.push(url) }
} }
FluPaneItem{ FluPaneItem{
title:"Screenshot" title:"Screenshot(Todo)"
url:"qrc:/example/qml/page/T_Screenshot.qml" url:"qrc:/example/qml/page/T_Screenshot.qml"
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) } onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
onTap:{ navigationView.push(url) } onTap:{ navigationView.push(url) }

View File

@ -1,9 +0,0 @@
pragma Singleton
import QtQuick
import QtQuick.Controls
import FluentUI
QtObject {
property int displayMode : FluNavigationViewType.Auto
}

View File

@ -1,3 +1,2 @@
singleton ItemsOriginal 1.0 ItemsOriginal.qml singleton ItemsOriginal 1.0 ItemsOriginal.qml
singleton ItemsFooter 1.0 ItemsFooter.qml singleton ItemsFooter 1.0 ItemsFooter.qml
singleton MainEvent 1.0 MainEvent.qml

View File

@ -50,7 +50,7 @@ FluScrollablePage{
FluRectangle{ FluRectangle{
width: 1920/4 width: 1920/4
height: 1200/4 height: 1200/4
radius:[15,15,15,15] radius:[8,8,8,8]
Image { Image {
id:image id:image
asynchronous: true asynchronous: true

View File

@ -161,6 +161,97 @@ FluScrollablePage{
}' }'
} }
Timer{
id:timer_progress
interval: 200
onTriggered: {
btn_progress.progress = (btn_progress.progress + 0.1).toFixed(1)
if(btn_progress.progress==1){
timer_progress.stop()
}else{
timer_progress.start()
}
}
}
FluArea{
Layout.fillWidth: true
height: 68
Layout.topMargin: 20
paddings: 10
FluProgressButton{
id:btn_progress
disabled:progress_button_switch.checked
text:"Progress Button"
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: {
btn_progress.progress = 0
timer_progress.restart()
}
}
FluToggleSwitch{
id:progress_button_switch
anchors{
right: parent.right
verticalCenter: parent.verticalCenter
}
text:"Disabled"
}
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluProgressButton{
text:"Progress Button"
onClicked: {
}
}'
}
FluArea{
Layout.fillWidth: true
height: 68
Layout.topMargin: 20
paddings: 10
FluLoadingButton{
id:btn_loading
loading:loading_button_switch.checked
text:"Loading Button"
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: {
}
}
FluToggleSwitch{
id:loading_button_switch
checked: true
anchors{
right: parent.right
verticalCenter: parent.verticalCenter
}
text:"Loading"
}
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluLoadingButton{
text:"Loading Button"
onClicked: {
}
}'
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true

View File

@ -36,8 +36,15 @@ FluScrollablePage{
FluText{ FluText{
text:"轮播图支持无限轮播无限滑动用ListView实现的组件" text:"轮播图支持无限轮播无限滑动用ListView实现的组件"
} }
Item{
width: 400
height: 300
FluShadow{
radius: 8
}
FluCarousel{ FluCarousel{
radius:[5,5,5,5] anchors.fill: parent
radius:[8,8,8,8]
delegate: Component{ delegate: Component{
Image { Image {
anchors.fill: parent anchors.fill: parent
@ -54,6 +61,7 @@ FluScrollablePage{
} }
} }
} }
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
@ -66,8 +74,15 @@ FluScrollablePage{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
left:parent.left left:parent.left
} }
Item{
width: 400
height: 300
FluShadow{
radius: 8
}
FluCarousel{ FluCarousel{
radius:[15,15,15,15] 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
@ -107,6 +122,8 @@ FluScrollablePage{
} }
} }
} }
}
} }
CodeExpander{ CodeExpander{

View File

@ -28,6 +28,7 @@ FluScrollablePage{
fillMode:Image.PreserveAspectCrop fillMode:Image.PreserveAspectCrop
anchors.fill: parent anchors.fill: parent
verticalAlignment: Qt.AlignTop verticalAlignment: Qt.AlignTop
sourceSize: Qt.size(960,640)
source: "qrc:/example/res/image/bg_home_header.png" source: "qrc:/example/res/image/bg_home_header.png"
} }
Rectangle{ Rectangle{
@ -69,12 +70,12 @@ FluScrollablePage{
width: 220 width: 220
height: 240 height: 240
FluShadow{ FluShadow{
radius:8 radius:5
anchors.fill: item_content anchors.fill: item_content
} }
FluItem{ FluItem{
id:item_content id:item_content
radius: [8,8,8,8] radius: [5,5,5,5]
width: 200 width: 200
height: 220 height: 220
anchors.centerIn: parent anchors.centerIn: parent
@ -88,7 +89,7 @@ FluScrollablePage{
} }
Rectangle{ Rectangle{
anchors.fill: parent anchors.fill: parent
radius: 8 radius: 5
color:{ color:{
if(FluTheme.dark){ if(FluTheme.dark){
if(item_mouse.containsMouse){ if(item_mouse.containsMouse){

View File

@ -10,14 +10,63 @@ import "qrc:///example/qml/component"
FluContentPage{ FluContentPage{
title:"Http" title:"Http"
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
property bool isDownCompleted: false
FluHttp{ FluHttp{
id:http id:http
cacheDir:cacheDirPath
}
FluHttp{
id:http_breakpoint_download
cacheDir:cacheDirPath
breakPointDownload: true
}
FluHttp{
id:http_cache_ifnonecacherequest
cacheMode:FluHttpType.IfNoneCacheRequest
cacheDir:cacheDirPath
}
FluHttp{
id:http_cache_requestfailedreadcache
cacheMode:FluHttpType.RequestFailedReadCache
cacheDir:cacheDirPath
}
FluHttp{
id:http_cache_firstcachethenrequest
cacheMode:FluHttpType.FirstCacheThenRequest
cacheDir:cacheDirPath
}
HttpCallable{
id:callable
onStart: {
showLoading()
}
onFinish: {
hideLoading()
}
onError:
(status,errorString,result)=>{
console.debug(status+";"+errorString+";"+result)
}
onSuccess:
(result)=>{
text_info.text = result
}
onCache:
(result)=>{
text_info.text = result
}
} }
Flickable{ Flickable{
id:layout_flick id:layout_flick
width: 160 width: 200
clip: true clip: true
anchors{ anchors{
top: parent.top top: parent.top
@ -36,21 +85,8 @@ FluContentPage{
implicitHeight: 36 implicitHeight: 36
text: "Get请求" text: "Get请求"
onClicked: { onClicked: {
var callable = {} var request = http.newRequest("https://httpbingo.org/get")
callable.onStart = function(){ http.get(request,callable)
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
http.get("https://httpbingo.org/get",callable)
} }
} }
FluButton{ FluButton{
@ -58,25 +94,13 @@ FluContentPage{
implicitHeight: 36 implicitHeight: 36
text: "Post表单请求" text: "Post表单请求"
onClicked: { onClicked: {
var callable = {} var request = http.newRequest("https://httpbingo.org/post")
callable.onStart = function(){ var params = {}
showLoading() params.custname = "朱子楚"
} params.custtel = "1234567890"
callable.onFinish = function(){ params.custemail = "zhuzichu520@gmail.com"
hideLoading() request.params = params
} http.post(request,callable)
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
param.custemail = "zhuzichu520@gmail.com"
http.post("https://httpbingo.org/post",callable,param)
} }
} }
FluButton{ FluButton{
@ -84,25 +108,13 @@ FluContentPage{
implicitHeight: 36 implicitHeight: 36
text: "Post Json请求" text: "Post Json请求"
onClicked: { onClicked: {
var callable = {} var request = http.newRequest("https://httpbingo.org/post")
callable.onStart = function(){ var params = {}
showLoading() params.custname = "朱子楚"
} params.custtel = "1234567890"
callable.onFinish = function(){ params.custemail = "zhuzichu520@gmail.com"
hideLoading() request.params = params
} http.postJson(request,callable)
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
param.custemail = "zhuzichu520@gmail.com"
http.postJson("https://httpbingo.org/post",callable,param)
} }
} }
FluButton{ FluButton{
@ -110,25 +122,12 @@ FluContentPage{
implicitHeight: 36 implicitHeight: 36
text: "Post String请求" text: "Post String请求"
onClicked: { onClicked: {
var callable = {} var request = http.newRequest("https://httpbingo.org/post")
callable.onStart = function(){ request.params = "我命由我不由天"
showLoading() http.postString(request,callable)
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = "我命由我不由天"
http.postString("https://httpbingo.org/post",callable,param)
} }
} }
FluButton{ FluProgressButton{
id:btn_download id:btn_download
implicitWidth: parent.width implicitWidth: parent.width
implicitHeight: 36 implicitHeight: 36
@ -137,7 +136,90 @@ FluContentPage{
folder_dialog.open() folder_dialog.open()
} }
} }
FluButton{ FluProgressButton{
property bool downloading: false
id:btn_breakpoint_download
implicitWidth: parent.width
implicitHeight: 36
text: {
if(downloading){
return "暂停下载"
}
if(progress === 0){
return "断点下载文件"
}else if(progress === 1){
return "打开文件"
}else{
return "继续下载"
}
}
HttpRequest{
id:request_breakpoint_download
url: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
downloadSavePath: FluTools.getApplicationDirPath()+ "/download/big_buck_bunny.mp4"
}
HttpCallable{
id:callable_breakpoint_download
onStart: {
btn_breakpoint_download.downloading = true
}
onFinish: {
btn_breakpoint_download.downloading = false
}
onError:
(status,errorString,result)=>{
console.debug(status+";"+errorString+";"+result)
}
onSuccess:
(result)=>{
if(!isDownCompleted){
tour.open()
isDownCompleted = true
}
showSuccess(result)
}
onDownloadProgress:
(recv,total)=>{
btn_breakpoint_download.progress = recv/total
}
}
Component.onCompleted: {
progress = http_breakpoint_download.getBreakPointProgress(request_breakpoint_download)
}
onClicked: {
if(downloading){
http_breakpoint_download.cancel()
return
}
if(progress === 1){
FluTools.showFileInFolder(request_breakpoint_download.downloadSavePath)
}else{
http_breakpoint_download.download(request_breakpoint_download,callable_breakpoint_download)
}
}
FluMenu{
id:menu_breakpoint_download
width: 120
FluMenuItem{
text: "删除文件"
onClicked: {
if(FluTools.removeFile(request_breakpoint_download.downloadSavePath)){
btn_breakpoint_download.progress = 0
}
}
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
if(btn_breakpoint_download.progress === 1){
menu_breakpoint_download.popup()
}
}
}
}
FluProgressButton{
id:btn_upload id:btn_upload
implicitWidth: parent.width implicitWidth: parent.width
implicitHeight: 36 implicitHeight: 36
@ -146,81 +228,143 @@ FluContentPage{
file_dialog.open() file_dialog.open()
} }
} }
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "FirstCacheThenRequest缓存"
onClicked: {
var request = http.newRequest("https://httpbingo.org/post")
request.params = {cacheMode:"FirstCacheThenRequest"}
http_cache_firstcachethenrequest.post(request,callable)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "RequestFailedReadCache缓存"
onClicked: {
var request = http.newRequest("https://httpbingo.org/post")
request.params = {cacheMode:"RequestFailedReadCache"}
http_cache_requestfailedreadcache.post(request,callable)
} }
} }
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "IfNoneCacheRequest缓存"
onClicked: {
var request = http.newRequest("https://httpbingo.org/post")
request.params = {cacheMode:"IfNoneCacheRequest"}
http_cache_ifnonecacherequest.post(request,callable)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "打开缓存路径"
onClicked: {
Qt.openUrlExternally("file:///"+cacheDirPath)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "删除缓存"
onClicked: {
console.debug(FluTools.removeDir(cacheDirPath))
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "清空右边数据"
onClicked: {
text_info.text = ""
}
}
}
}
FluTour{
id:tour
steps:[
{title:"友情提示",description: "下载已完成,左击这里可以打开文件所在路径,右击可以弹出菜单删除文件!",target:()=>btn_breakpoint_download}
]
}
HttpCallable{
id:callable_upload
onStart: {
btn_upload.disabled = true
}
onFinish: {
btn_upload.disabled = false
}
onError:
(status,errorString,result)=>{
btn_upload.progress = 0
text_info.text = result
console.debug(result)
}
onSuccess:
(result)=>{
text_info.text = result
}
onUploadProgress:
(sent,total)=>{
btn_upload.progress = sent/total
}
}
FileDialog { FileDialog {
id: file_dialog id: file_dialog
onAccepted: { onAccepted: {
var param = {} var request = http.newRequest("https://httpbingo.org/post")
var params = {}
for(var i=0;i<selectedFiles.length;i++){ for(var i=0;i<selectedFiles.length;i++){
var fileUrl = selectedFiles[i] var fileUrl = selectedFiles[i]
var fileName = FluTools.getFileNameByUrl(fileUrl) var fileName = FluTools.getFileNameByUrl(fileUrl)
var filePath = FluTools.toLocalPath(fileUrl) var filePath = FluTools.toLocalPath(fileUrl)
param[fileName] = filePath params[fileName] = filePath
} }
console.debug(JSON.stringify(param)) request.params = params
var callable = {} http.upload(request,callable_upload)
callable.onStart = function(){
btn_upload.disabled = true
}
callable.onFinish = function(){
btn_upload.disabled = false
btn_upload.text = "上传文件"
layout_upload_file_size.visible = false
text_upload_file_size.text = ""
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString,result){
text_info.text = result
console.debug(result)
}
callable.onUploadProgress = function(sent,total){
var locale = Qt.locale()
var precent = (sent/total * 100).toFixed(0) + "%"
btn_upload.text = "上传中..."+precent
text_upload_file_size.text = "%1/%2".arg(locale.formattedDataSize(sent)).arg(locale.formattedDataSize(total))
layout_upload_file_size.visible = true
}
http.upload("https://httpbingo.org/post",callable,param)
} }
} }
HttpCallable{
id:callable_download
onStart: {
btn_download.progress = 0
btn_download.disabled = true
}
onFinish: {
btn_download.disabled = false
}
onError:
(status,errorString,result)=>{
btn_download.progress = 0
showError(errorString)
console.debug(status+";"+errorString+";"+result)
}
onSuccess:
(result)=>{
showSuccess(result)
}
onDownloadProgress:
(recv,total)=>{
btn_download.progress = recv/total
}
}
FolderDialog { FolderDialog {
id: folder_dialog id: folder_dialog
currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0] currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
onAccepted: { onAccepted: {
var callable = {} var request = http.newRequest("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
callable.onStart = function(){ request.downloadSavePath = FluTools.toLocalPath(currentFolder)+ "/big_buck_bunny.mp4"
btn_download.disabled = true http.download(request,callable_download)
}
callable.onFinish = function(){
btn_download.disabled = false
btn_download.text = "下载文件"
layout_download_file_size.visible = false
text_download_file_size.text = ""
}
callable.onSuccess = function(result){
showSuccess(result)
}
callable.onError = function(status,errorString){
showError(errorString)
}
callable.onDownloadProgress = function(recv,total){
var locale = Qt.locale()
var precent = (recv/total * 100).toFixed(0) + "%"
btn_download.text = "下载中..."+precent
text_download_file_size.text = "%1/%2".arg(locale.formattedDataSize(recv)).arg(locale.formattedDataSize(total))
layout_download_file_size.visible = true
}
var path = FluTools.toLocalPath(currentFolder)+ "/big_buck_bunny.mp4"
http.download("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",callable,path)
} }
} }
FluArea{ FluArea{
anchors{ anchors{
top: layout_flick.top top: layout_flick.top
@ -246,34 +390,4 @@ FluContentPage{
} }
} }
} }
FluRectangle{
id:layout_download_file_size
radius: [4,4,4,4]
height: 36
width: 160
visible: false
x:layout_flick.width
y: 173 - layout_flick.contentY
FluText{
id:text_download_file_size
anchors.centerIn: parent
}
}
FluRectangle{
id:layout_upload_file_size
radius: [4,4,4,4]
height: 36
width: 160
visible: false
x:layout_flick.width
y: 210 - layout_flick.contentY
FluText{
id:text_upload_file_size
anchors.centerIn: parent
}
}
} }

View File

@ -116,7 +116,7 @@ FluScrollablePage{
FluRectangle{ FluRectangle{
width: 1920/5 width: 1920/5
height: 1200/5 height: 1200/5
radius:[15,15,15,15] radius:[8,8,8,8]
Image { Image {
asynchronous: true asynchronous: true
source: "qrc:/example/res/image/banner_1.jpg" source: "qrc:/example/res/image/banner_1.jpg"

View File

@ -30,9 +30,10 @@ FluScrollablePage{
Layout.topMargin: 10 Layout.topMargin: 10
Layout.leftMargin: 4 Layout.leftMargin: 4
Layout.bottomMargin: 4 Layout.bottomMargin: 4
radius: 4
color: FluTheme.dark ? FluColors.Black : FluColors.White color: FluTheme.dark ? FluColors.Black : FluColors.White
FluShadow{ FluShadow{
radius: 0 radius: 4
color: FluTheme.primaryColor.dark color: FluTheme.primaryColor.dark
} }
Image{ Image{

View File

@ -5,11 +5,58 @@ import QtQuick.Controls
import FluentUI import FluentUI
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/viewmodel"
FluScrollablePage{ FluScrollablePage{
title:"Settings" title:"Settings"
SettingsViewModel{
id:viewmodel_settings
}
FluEvent{
id:event_checkupdate_finish
name: "checkUpdateFinish"
onTriggered: {
btn_checkupdate.loading = false
}
}
Component.onCompleted: {
FluEventBus.registerEvent(event_checkupdate_finish)
}
Component.onDestruction: {
FluEventBus.unRegisterEvent(event_checkupdate_finish)
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 60
paddings: 10
Row{
spacing: 20
anchors.verticalCenter: parent.verticalCenter
FluText{
text:"当前版本 v%1".arg(appInfo.version)
font: FluTextStyle.Body
anchors.verticalCenter: parent.verticalCenter
}
FluLoadingButton{
id:btn_checkupdate
text:"检查更新"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
loading = true
FluEventBus.post("checkUpdate")
}
}
}
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
@ -60,10 +107,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}] model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked : MainEvent.displayMode===modelData.mode checked : viewmodel_settings.displayMode===modelData.mode
text:modelData.title text:modelData.title
clickListener:function(){ clickListener:function(){
MainEvent.displayMode = modelData.mode viewmodel_settings.displayMode = modelData.mode
} }
} }
} }

View File

@ -4,12 +4,19 @@ import QtQuick.Layouts
import QtQuick.Window import QtQuick.Window
import FluentUI import FluentUI
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/viewmodel"
FluScrollablePage{ FluScrollablePage{
launchMode: FluPageType.SingleInstance
title:"TextBox" title:"TextBox"
TextBoxViewModel{
id:viewModel
}
Component.onDestruction: {
console.debug("T_TextBox页面销毁了")
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
height: 68 height: 68
@ -20,6 +27,10 @@ FluScrollablePage{
placeholderText: "单行输入框" placeholderText: "单行输入框"
disabled:text_box_switch.checked disabled:text_box_switch.checked
cleanEnabled: true cleanEnabled: true
text:viewModel.text1
onTextChanged: {
viewModel.text1 = text
}
anchors{ anchors{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
left: parent.left left: parent.left
@ -84,6 +95,10 @@ FluScrollablePage{
FluMultilineTextBox{ FluMultilineTextBox{
id:multiine_textbox id:multiine_textbox
placeholderText: "多行输入框" placeholderText: "多行输入框"
text:viewModel.text2
onTextChanged: {
viewModel.text2 = text
}
disabled:text_box_multi_switch.checked disabled:text_box_multi_switch.checked
anchors{ anchors{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter

View File

@ -0,0 +1,13 @@
import QtQuick
import FluentUI
FluViewModel{
objectName: "SettingsViewModel"
property int displayMode
onInitData: {
displayMode = FluNavigationViewType.Auto
}
}

View File

@ -0,0 +1,8 @@
import QtQuick
import FluentUI
FluViewModel {
objectName: "TextBoxView"
property string text1
property string text2
}

View File

@ -7,6 +7,7 @@ import FluentUI
import example import example
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/viewmodel"
CustomWindow { CustomWindow {
@ -20,15 +21,32 @@ CustomWindow {
appBarVisible: false appBarVisible: false
launchMode: FluWindowType.SingleTask launchMode: FluWindowType.SingleTask
SettingsViewModel{
id:viewmodel_settings
}
closeFunc:function(event){ closeFunc:function(event){
dialog_close.open() dialog_close.open()
event.accepted = false event.accepted = false
} }
FluEvent{
id:event_checkupdate
name: "checkUpdate"
onTriggered: {
checkUpdate(false)
}
}
Component.onCompleted: { Component.onCompleted: {
FluTools.setQuitOnLastWindowClosed(false) FluTools.setQuitOnLastWindowClosed(false)
tour.open() tour.open()
checkUpdate() checkUpdate(true)
FluEventBus.registerEvent(event_checkupdate)
}
Component.onDestruction: {
FluEventBus.unRegisterEvent(event_checkupdate)
} }
SystemTrayIcon { SystemTrayIcon {
@ -138,7 +156,7 @@ CustomWindow {
id:loader id:loader
lazy: true lazy: true
anchors.fill: parent anchors.fill: parent
source: "https://zhu-zichu.gitee.io/Qt6_155_LieflatPage.qml" source: "https://zhu-zichu.gitee.io/Qt6_156_LieflatPage.qml"
} }
} }
front: Item{ front: Item{
@ -164,13 +182,13 @@ CustomWindow {
height: parent.height height: parent.height
z:999 z:999
//Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式 //Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式
pageMode: FluNavigationViewType.Stack // pageMode: FluNavigationViewType.Stack
//NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐 //NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐
// pageMode: FluNavigationViewType.NoStack pageMode: FluNavigationViewType.NoStack
items: ItemsOriginal items: ItemsOriginal
footerItems:ItemsFooter footerItems:ItemsFooter
topPadding:FluTools.isMacos() ? 20 : 0 topPadding:FluTools.isMacos() ? 20 : 0
displayMode:MainEvent.displayMode displayMode:viewmodel_settings.displayMode
logo: "qrc:/example/res/image/favicon.ico" logo: "qrc:/example/res/image/favicon.ico"
title:"FluentUI" title:"FluentUI"
onLogoClicked:{ onLogoClicked:{
@ -309,15 +327,18 @@ CustomWindow {
} }
} }
function checkUpdate(){ HttpCallable{
var callable = {} id:callable
callable.onStart = function(){ property bool silent: true
onStart: {
console.debug("satrt check update...") console.debug("satrt check update...")
} }
callable.onFinish = function(){ onFinish: {
console.debug("check update finish") console.debug("check update finish")
FluEventBus.post("checkUpdateFinish");
} }
callable.onSuccess = function(result){ onSuccess:
(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)
@ -325,12 +346,25 @@ CustomWindow {
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()
}else{
if(!silent){
showInfo("当前版本已经是最新版")
} }
} }
callable.onError = function(status,errorString){ }
onError:
(status,errorString)=>{
if(!silent){
showError("网络异常!")
}
console.debug(status+";"+errorString) console.debug(status+";"+errorString)
} }
http.get("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest",callable) }
function checkUpdate(silent){
callable.silent = silent
var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest")
http.get(request,callable);
} }
} }

View File

@ -138,7 +138,9 @@ FluExpander{
"FluTimeline", "FluTimeline",
"FluChart", "FluChart",
"FluRangeSlider", "FluRangeSlider",
"FluStaggeredView" "FluStaggeredView",
"FluProgressButton",
"FluLoadingButton"
]; ];
code = code.replace(/\n/g, "<br>"); code = code.replace(/\n/g, "<br>");
code = code.replace(/ /g, "&nbsp;"); code = code.replace(/ /g, "&nbsp;");

View File

@ -88,7 +88,10 @@ FluObject{
} }
url:"qrc:/example/qml/page/T_Text.qml" url:"qrc:/example/qml/page/T_Text.qml"
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) } onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
onTap:{ navigationView.push(url) } onTap:{
item_text.count = 0
navigationView.push(url)
}
} }
FluPaneItem{ FluPaneItem{
title:"Image" title:"Image"
@ -394,7 +397,7 @@ FluObject{
onTap:{ navigationView.push(url) } onTap:{ navigationView.push(url) }
} }
FluPaneItem{ FluPaneItem{
title:"Screenshot" title:"Screenshot(Todo)"
url:"qrc:/example/qml/page/T_Screenshot.qml" url:"qrc:/example/qml/page/T_Screenshot.qml"
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) } onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
onTap:{ navigationView.push(url) } onTap:{ navigationView.push(url) }

View File

@ -1,9 +0,0 @@
pragma Singleton
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
QtObject {
property int displayMode : FluNavigationViewType.Auto
}

View File

@ -1,3 +1,2 @@
singleton ItemsOriginal 1.0 ItemsOriginal.qml singleton ItemsOriginal 1.0 ItemsOriginal.qml
singleton ItemsFooter 1.0 ItemsFooter.qml singleton ItemsFooter 1.0 ItemsFooter.qml
singleton MainEvent 1.0 MainEvent.qml

View File

@ -51,7 +51,7 @@ FluScrollablePage{
FluRectangle{ FluRectangle{
width: 1920/4 width: 1920/4
height: 1200/4 height: 1200/4
radius:[15,15,15,15] radius:[8,8,8,8]
Image { Image {
id:image id:image
asynchronous: true asynchronous: true

View File

@ -161,6 +161,97 @@ FluScrollablePage{
}' }'
} }
Timer{
id:timer_progress
interval: 200
onTriggered: {
btn_progress.progress = (btn_progress.progress + 0.1).toFixed(1)
if(btn_progress.progress==1){
timer_progress.stop()
}else{
timer_progress.start()
}
}
}
FluArea{
Layout.fillWidth: true
height: 68
Layout.topMargin: 20
paddings: 10
FluProgressButton{
id:btn_progress
disabled:progress_button_switch.checked
text:"Progress Button"
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: {
btn_progress.progress = 0
timer_progress.restart()
}
}
FluToggleSwitch{
id:progress_button_switch
anchors{
right: parent.right
verticalCenter: parent.verticalCenter
}
text:"Disabled"
}
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluProgressButton{
text:"Progress Button"
onClicked: {
}
}'
}
FluArea{
Layout.fillWidth: true
height: 68
Layout.topMargin: 20
paddings: 10
FluLoadingButton{
id:btn_loading
loading:loading_button_switch.checked
text:"Loading Button"
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: {
}
}
FluToggleSwitch{
id:loading_button_switch
checked: true
anchors{
right: parent.right
verticalCenter: parent.verticalCenter
}
text:"Loading"
}
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluLoadingButton{
text:"Loading Button"
onClicked: {
}
}'
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true

View File

@ -37,8 +37,15 @@ FluScrollablePage{
FluText{ FluText{
text:"轮播图支持无限轮播无限滑动用ListView实现的组件" text:"轮播图支持无限轮播无限滑动用ListView实现的组件"
} }
Item{
width: 400
height: 300
FluShadow{
radius: 8
}
FluCarousel{ FluCarousel{
radius:[5,5,5,5] anchors.fill: parent
radius:[8,8,8,8]
delegate: Component{ delegate: Component{
Image { Image {
anchors.fill: parent anchors.fill: parent
@ -55,6 +62,7 @@ FluScrollablePage{
} }
} }
} }
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
@ -67,8 +75,15 @@ FluScrollablePage{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
left:parent.left left:parent.left
} }
Item{
width: 400
height: 300
FluShadow{
radius: 8
}
FluCarousel{ FluCarousel{
radius:[15,15,15,15] 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
@ -108,6 +123,8 @@ FluScrollablePage{
} }
} }
} }
}
} }
CodeExpander{ CodeExpander{

View File

@ -29,6 +29,7 @@ FluScrollablePage{
anchors.fill: parent anchors.fill: parent
asynchronous: true asynchronous: true
verticalAlignment: Qt.AlignTop verticalAlignment: Qt.AlignTop
sourceSize: Qt.size(960,640)
source: "qrc:/example/res/image/bg_home_header.png" source: "qrc:/example/res/image/bg_home_header.png"
} }
Rectangle{ Rectangle{
@ -70,12 +71,12 @@ FluScrollablePage{
width: 220 width: 220
height: 240 height: 240
FluShadow{ FluShadow{
radius:8 radius:5
anchors.fill: item_content anchors.fill: item_content
} }
FluItem{ FluItem{
id:item_content id:item_content
radius: [8,8,8,8] radius: [5,5,5,5]
width: 200 width: 200
height: 220 height: 220
anchors.centerIn: parent anchors.centerIn: parent
@ -89,7 +90,7 @@ FluScrollablePage{
} }
Rectangle{ Rectangle{
anchors.fill: parent anchors.fill: parent
radius: 8 radius: 5
color:{ color:{
if(FluTheme.dark){ if(FluTheme.dark){
if(item_mouse.containsMouse){ if(item_mouse.containsMouse){

View File

@ -11,14 +11,63 @@ import "../component"
FluContentPage{ FluContentPage{
title:"Http" title:"Http"
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
property bool isDownCompleted: false
FluHttp{ FluHttp{
id:http id:http
cacheDir:cacheDirPath
}
FluHttp{
id:http_breakpoint_download
cacheDir:cacheDirPath
breakPointDownload: true
}
FluHttp{
id:http_cache_ifnonecacherequest
cacheMode:FluHttpType.IfNoneCacheRequest
cacheDir:cacheDirPath
}
FluHttp{
id:http_cache_requestfailedreadcache
cacheMode:FluHttpType.RequestFailedReadCache
cacheDir:cacheDirPath
}
FluHttp{
id:http_cache_firstcachethenrequest
cacheMode:FluHttpType.FirstCacheThenRequest
cacheDir:cacheDirPath
}
HttpCallable{
id:callable
onStart: {
showLoading()
}
onFinish: {
hideLoading()
}
onError:
(status,errorString,result)=>{
console.debug(status+";"+errorString+";"+result)
}
onSuccess:
(result)=>{
text_info.text = result
}
onCache:
(result)=>{
text_info.text = result
}
} }
Flickable{ Flickable{
id:layout_flick id:layout_flick
width: 160 width: 200
clip: true clip: true
anchors{ anchors{
top: parent.top top: parent.top
@ -37,21 +86,8 @@ FluContentPage{
implicitHeight: 36 implicitHeight: 36
text: "Get请求" text: "Get请求"
onClicked: { onClicked: {
var callable = {} var request = http.newRequest("https://httpbingo.org/get")
callable.onStart = function(){ http.get(request,callable)
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
http.get("https://httpbingo.org/get",callable)
} }
} }
FluButton{ FluButton{
@ -59,25 +95,13 @@ FluContentPage{
implicitHeight: 36 implicitHeight: 36
text: "Post表单请求" text: "Post表单请求"
onClicked: { onClicked: {
var callable = {} var request = http.newRequest("https://httpbingo.org/post")
callable.onStart = function(){ var params = {}
showLoading() params.custname = "朱子楚"
} params.custtel = "1234567890"
callable.onFinish = function(){ params.custemail = "zhuzichu520@gmail.com"
hideLoading() request.params = params
} http.post(request,callable)
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
param.custemail = "zhuzichu520@gmail.com"
http.post("https://httpbingo.org/post",callable,param)
} }
} }
FluButton{ FluButton{
@ -85,25 +109,13 @@ FluContentPage{
implicitHeight: 36 implicitHeight: 36
text: "Post Json请求" text: "Post Json请求"
onClicked: { onClicked: {
var callable = {} var request = http.newRequest("https://httpbingo.org/post")
callable.onStart = function(){ var params = {}
showLoading() params.custname = "朱子楚"
} params.custtel = "1234567890"
callable.onFinish = function(){ params.custemail = "zhuzichu520@gmail.com"
hideLoading() request.params = params
} http.postJson(request,callable)
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
param.custemail = "zhuzichu520@gmail.com"
http.postJson("https://httpbingo.org/post",callable,param)
} }
} }
FluButton{ FluButton{
@ -111,25 +123,12 @@ FluContentPage{
implicitHeight: 36 implicitHeight: 36
text: "Post String请求" text: "Post String请求"
onClicked: { onClicked: {
var callable = {} var request = http.newRequest("https://httpbingo.org/post")
callable.onStart = function(){ request.params = "我命由我不由天"
showLoading() http.postString(request,callable)
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = "我命由我不由天"
http.postString("https://httpbingo.org/post",callable,param)
} }
} }
FluButton{ FluProgressButton{
id:btn_download id:btn_download
implicitWidth: parent.width implicitWidth: parent.width
implicitHeight: 36 implicitHeight: 36
@ -138,7 +137,90 @@ FluContentPage{
folder_dialog.open() folder_dialog.open()
} }
} }
FluButton{ FluProgressButton{
property bool downloading: false
id:btn_breakpoint_download
implicitWidth: parent.width
implicitHeight: 36
text: {
if(downloading){
return "暂停下载"
}
if(progress === 0){
return "断点下载文件"
}else if(progress === 1){
return "打开文件"
}else{
return "继续下载"
}
}
HttpRequest{
id:request_breakpoint_download
url: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
downloadSavePath: FluTools.getApplicationDirPath()+ "/download/big_buck_bunny.mp4"
}
HttpCallable{
id:callable_breakpoint_download
onStart: {
btn_breakpoint_download.downloading = true
}
onFinish: {
btn_breakpoint_download.downloading = false
}
onError:
(status,errorString,result)=>{
console.debug(status+";"+errorString+";"+result)
}
onSuccess:
(result)=>{
if(!isDownCompleted){
tour.open()
isDownCompleted = true
}
showSuccess(result)
}
onDownloadProgress:
(recv,total)=>{
btn_breakpoint_download.progress = recv/total
}
}
Component.onCompleted: {
progress = http_breakpoint_download.getBreakPointProgress(request_breakpoint_download)
}
onClicked: {
if(downloading){
http_breakpoint_download.cancel()
return
}
if(progress === 1){
FluTools.showFileInFolder(request_breakpoint_download.downloadSavePath)
}else{
http_breakpoint_download.download(request_breakpoint_download,callable_breakpoint_download)
}
}
FluMenu{
id:menu_breakpoint_download
width: 120
FluMenuItem{
text: "删除文件"
onClicked: {
if(FluTools.removeFile(request_breakpoint_download.downloadSavePath)){
btn_breakpoint_download.progress = 0
}
}
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
if(btn_breakpoint_download.progress === 1){
menu_breakpoint_download.popup()
}
}
}
}
FluProgressButton{
id:btn_upload id:btn_upload
implicitWidth: parent.width implicitWidth: parent.width
implicitHeight: 36 implicitHeight: 36
@ -147,81 +229,143 @@ FluContentPage{
file_dialog.open() file_dialog.open()
} }
} }
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "FirstCacheThenRequest缓存"
onClicked: {
var request = http.newRequest("https://httpbingo.org/post")
request.params = {cacheMode:"FirstCacheThenRequest"}
http_cache_firstcachethenrequest.post(request,callable)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "RequestFailedReadCache缓存"
onClicked: {
var request = http.newRequest("https://httpbingo.org/post")
request.params = {cacheMode:"RequestFailedReadCache"}
http_cache_requestfailedreadcache.post(request,callable)
} }
} }
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "IfNoneCacheRequest缓存"
onClicked: {
var request = http.newRequest("https://httpbingo.org/post")
request.params = {cacheMode:"IfNoneCacheRequest"}
http_cache_ifnonecacherequest.post(request,callable)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "打开缓存路径"
onClicked: {
Qt.openUrlExternally("file:///"+cacheDirPath)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "删除缓存"
onClicked: {
console.debug(FluTools.removeDir(cacheDirPath))
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "清空右边数据"
onClicked: {
text_info.text = ""
}
}
}
}
FluTour{
id:tour
steps:[
{title:"友情提示",description: "下载已完成,左击这里可以打开文件所在路径,右击可以弹出菜单删除文件!",target:()=>btn_breakpoint_download}
]
}
HttpCallable{
id:callable_upload
onStart: {
btn_upload.disabled = true
}
onFinish: {
btn_upload.disabled = false
}
onError:
(status,errorString,result)=>{
btn_upload.progress = 0
text_info.text = result
console.debug(result)
}
onSuccess:
(result)=>{
text_info.text = result
}
onUploadProgress:
(sent,total)=>{
btn_upload.progress = sent/total
}
}
FileDialog { FileDialog {
id: file_dialog id: file_dialog
onAccepted: { onAccepted: {
var param = {} var request = http.newRequest("https://httpbingo.org/post")
var params = {}
for(var i=0;i<selectedFiles.length;i++){ for(var i=0;i<selectedFiles.length;i++){
var fileUrl = selectedFiles[i] var fileUrl = selectedFiles[i]
var fileName = FluTools.getFileNameByUrl(fileUrl) var fileName = FluTools.getFileNameByUrl(fileUrl)
var filePath = FluTools.toLocalPath(fileUrl) var filePath = FluTools.toLocalPath(fileUrl)
param[fileName] = filePath params[fileName] = filePath
} }
console.debug(JSON.stringify(param)) request.params = params
var callable = {} http.upload(request,callable_upload)
callable.onStart = function(){
btn_upload.disabled = true
}
callable.onFinish = function(){
btn_upload.disabled = false
btn_upload.text = "上传文件"
layout_upload_file_size.visible = false
text_upload_file_size.text = ""
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString,result){
text_info.text = result
console.debug(result)
}
callable.onUploadProgress = function(sent,total){
var locale = Qt.locale()
var precent = (sent/total * 100).toFixed(0) + "%"
btn_upload.text = "上传中..."+precent
text_upload_file_size.text = "%1/%2".arg(locale.formattedDataSize(sent)).arg(locale.formattedDataSize(total))
layout_upload_file_size.visible = true
}
http.upload("https://httpbingo.org/post",callable,param)
} }
} }
HttpCallable{
id:callable_download
onStart: {
btn_download.progress = 0
btn_download.disabled = true
}
onFinish: {
btn_download.disabled = false
}
onError:
(status,errorString,result)=>{
btn_download.progress = 0
showError(errorString)
console.debug(status+";"+errorString+";"+result)
}
onSuccess:
(result)=>{
showSuccess(result)
}
onDownloadProgress:
(recv,total)=>{
btn_download.progress = recv/total
}
}
FolderDialog { FolderDialog {
id: folder_dialog id: folder_dialog
currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0] currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
onAccepted: { onAccepted: {
var callable = {} var request = http.newRequest("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
callable.onStart = function(){ request.downloadSavePath = FluTools.toLocalPath(currentFolder)+ "/big_buck_bunny.mp4"
btn_download.disabled = true http.download(request,callable_download)
}
callable.onFinish = function(){
btn_download.disabled = false
btn_download.text = "下载文件"
layout_download_file_size.visible = false
text_download_file_size.text = ""
}
callable.onSuccess = function(result){
showSuccess(result)
}
callable.onError = function(status,errorString){
showError(errorString)
}
callable.onDownloadProgress = function(recv,total){
var locale = Qt.locale()
var precent = (recv/total * 100).toFixed(0) + "%"
btn_download.text = "下载中..."+precent
text_download_file_size.text = "%1/%2".arg(locale.formattedDataSize(recv)).arg(locale.formattedDataSize(total))
layout_download_file_size.visible = true
}
var path = FluTools.toLocalPath(currentFolder)+ "/big_buck_bunny.mp4"
http.download("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",callable,path)
} }
} }
FluArea{ FluArea{
anchors{ anchors{
top: layout_flick.top top: layout_flick.top
@ -247,34 +391,4 @@ FluContentPage{
} }
} }
} }
FluRectangle{
id:layout_download_file_size
radius: [4,4,4,4]
height: 36
width: 160
visible: false
x:layout_flick.width
y: 173 - layout_flick.contentY
FluText{
id:text_download_file_size
anchors.centerIn: parent
}
}
FluRectangle{
id:layout_upload_file_size
radius: [4,4,4,4]
height: 36
width: 160
visible: false
x:layout_flick.width
y: 210 - layout_flick.contentY
FluText{
id:text_upload_file_size
anchors.centerIn: parent
}
}
} }

View File

@ -16,9 +16,12 @@ FluScrollablePage{
height: 400 height: 400
paddings: 10 paddings: 10
FluPivot{ FluPivot{
anchors.fill: parent anchors.fill: parent
currentIndex: 2 currentIndex: 2
FluPivotItem{ FluPivotItem{
title:"All" title:"All"
contentItem:FluText{ contentItem:FluText{

View File

@ -117,7 +117,7 @@ FluScrollablePage{
FluRectangle{ FluRectangle{
width: 1920/5 width: 1920/5
height: 1200/5 height: 1200/5
radius:[15,15,15,15] radius:[8,8,8,8]
Image { Image {
asynchronous: true asynchronous: true
source: "qrc:/example/res/image/banner_1.jpg" source: "qrc:/example/res/image/banner_1.jpg"

View File

@ -31,9 +31,10 @@ FluScrollablePage{
Layout.topMargin: 10 Layout.topMargin: 10
Layout.leftMargin: 4 Layout.leftMargin: 4
Layout.bottomMargin: 4 Layout.bottomMargin: 4
radius: 4
color: FluTheme.dark ? FluColors.Black : FluColors.White color: FluTheme.dark ? FluColors.Black : FluColors.White
FluShadow{ FluShadow{
radius: 0 radius: 4
color: FluTheme.primaryColor.dark color: FluTheme.primaryColor.dark
} }
Image{ Image{

View File

@ -5,12 +5,61 @@ import QtQuick.Controls 2.15
import FluentUI 1.0 import FluentUI 1.0
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/viewmodel"
import "../component" import "../component"
import "../viewmodel"
import "../global"
FluScrollablePage{ FluScrollablePage{
title:"Settings" title:"Settings"
SettingsViewModel{
id:viewmodel_settings
}
FluEvent{
id:event_checkupdate_finish
name: "checkUpdateFinish"
onTriggered: {
btn_checkupdate.loading = false
}
}
Component.onCompleted: {
FluEventBus.registerEvent(event_checkupdate_finish)
}
Component.onDestruction: {
FluEventBus.unRegisterEvent(event_checkupdate_finish)
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 60
paddings: 10
Row{
spacing: 20
anchors.verticalCenter: parent.verticalCenter
FluText{
text:"当前版本 v%1".arg(appInfo.version)
font: FluTextStyle.Body
anchors.verticalCenter: parent.verticalCenter
}
FluLoadingButton{
id:btn_checkupdate
text:"检查更新"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
loading = true
FluEventBus.post("checkUpdate")
}
}
}
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
@ -61,10 +110,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}] model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked : MainEvent.displayMode===modelData.mode checked : viewmodel_settings.displayMode===modelData.mode
text:modelData.title text:modelData.title
clickListener:function(){ clickListener:function(){
MainEvent.displayMode = modelData.mode viewmodel_settings.displayMode = modelData.mode
} }
} }
} }

View File

@ -4,13 +4,21 @@ import QtQuick.Layouts 1.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import FluentUI 1.0 import FluentUI 1.0
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/viewmodel"
import "../component" import "../component"
import "../viewmodel"
FluScrollablePage{ FluScrollablePage{
launchMode: FluPageType.SingleInstance
title:"TextBox" title:"TextBox"
TextBoxViewModel{
id:viewModel
}
Component.onDestruction: {
console.debug("T_TextBox页面销毁了")
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
height: 68 height: 68
@ -21,6 +29,10 @@ FluScrollablePage{
placeholderText: "单行输入框" placeholderText: "单行输入框"
disabled:text_box_switch.checked disabled:text_box_switch.checked
cleanEnabled: true cleanEnabled: true
text:viewModel.text1
onTextChanged: {
viewModel.text1 = text
}
anchors{ anchors{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
left: parent.left left: parent.left
@ -85,6 +97,10 @@ FluScrollablePage{
FluMultilineTextBox{ FluMultilineTextBox{
id:multiine_textbox id:multiine_textbox
placeholderText: "多行输入框" placeholderText: "多行输入框"
text:viewModel.text2
onTextChanged: {
viewModel.text2 = text
}
disabled:text_box_multi_switch.checked disabled:text_box_multi_switch.checked
anchors{ anchors{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter

View File

@ -0,0 +1,13 @@
import QtQuick 2.15
import FluentUI 1.0
FluViewModel{
objectName: "SettingsViewModel"
property int displayMode
onInitData: {
displayMode = FluNavigationViewType.Auto
}
}

View File

@ -0,0 +1,8 @@
import QtQuick 2.15
import FluentUI 1.0
FluViewModel {
objectName: "TextBoxView"
property string text1
property string text2
}

View File

@ -6,8 +6,11 @@ import Qt.labs.platform 1.1
import FluentUI 1.0 import FluentUI 1.0
import example 1.0 import example 1.0
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "../component"
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/viewmodel"
import "../component"
import "../viewmodel"
import "../global"
CustomWindow { CustomWindow {
@ -21,15 +24,32 @@ CustomWindow {
appBarVisible: false appBarVisible: false
launchMode: FluWindowType.SingleTask launchMode: FluWindowType.SingleTask
SettingsViewModel{
id:viewmodel_settings
}
closeFunc:function(event){ closeFunc:function(event){
dialog_close.open() dialog_close.open()
event.accepted = false event.accepted = false
} }
FluEvent{
id:event_checkupdate
name: "checkUpdate"
onTriggered: {
checkUpdate(false)
}
}
Component.onCompleted: { Component.onCompleted: {
FluTools.setQuitOnLastWindowClosed(false) FluTools.setQuitOnLastWindowClosed(false)
tour.open() tour.open()
checkUpdate() checkUpdate(true)
FluEventBus.registerEvent(event_checkupdate)
}
Component.onDestruction: {
FluEventBus.unRegisterEvent(event_checkupdate)
} }
SystemTrayIcon { SystemTrayIcon {
@ -139,7 +159,7 @@ CustomWindow {
id:loader id:loader
lazy: true lazy: true
anchors.fill: parent anchors.fill: parent
source: "https://zhu-zichu.gitee.io/Qt5_155_LieflatPage.qml" source: "https://zhu-zichu.gitee.io/Qt5_156_LieflatPage.qml"
} }
} }
front: Item{ front: Item{
@ -165,13 +185,13 @@ CustomWindow {
height: parent.height height: parent.height
z:999 z:999
//Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式 //Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式
pageMode: FluNavigationViewType.Stack // pageMode: FluNavigationViewType.Stack
//NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐 //NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐
// pageMode: FluNavigationViewType.NoStack pageMode: FluNavigationViewType.NoStack
items: ItemsOriginal items: ItemsOriginal
footerItems:ItemsFooter footerItems:ItemsFooter
topPadding:FluTools.isMacos() ? 20 : 0 topPadding:FluTools.isMacos() ? 20 : 0
displayMode:MainEvent.displayMode displayMode:viewmodel_settings.displayMode
logo: "qrc:/example/res/image/favicon.ico" logo: "qrc:/example/res/image/favicon.ico"
title:"FluentUI" title:"FluentUI"
onLogoClicked:{ onLogoClicked:{
@ -310,15 +330,18 @@ CustomWindow {
} }
} }
function checkUpdate(){ HttpCallable{
var callable = {} id:callable
callable.onStart = function(){ property bool silent: true
onStart: {
console.debug("satrt check update...") console.debug("satrt check update...")
} }
callable.onFinish = function(){ onFinish: {
console.debug("check update finish") console.debug("check update finish")
FluEventBus.post("checkUpdateFinish");
} }
callable.onSuccess = function(result){ onSuccess:
(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)
@ -326,12 +349,25 @@ CustomWindow {
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()
}else{
if(!silent){
showInfo("当前版本已经是最新版")
} }
} }
callable.onError = function(status,errorString){ }
onError:
(status,errorString)=>{
if(!silent){
showError("网络异常!")
}
console.debug(status+";"+errorString) console.debug(status+";"+errorString)
} }
http.get("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest",callable) }
function checkUpdate(silent){
callable.silent = silent
var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest")
http.get(request,callable);
} }
} }

View File

@ -8,10 +8,17 @@
#include <QProcess> #include <QProcess>
#include <FramelessHelper/Quick/framelessquickmodule.h> #include <FramelessHelper/Quick/framelessquickmodule.h>
#include <FramelessHelper/Core/private/framelessconfig_p.h> #include <FramelessHelper/Core/private/framelessconfig_p.h>
#include <QtQml/qqmlextensionplugin.h>
#include "AppInfo.h" #include "AppInfo.h"
#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"
#ifdef FLUENTUI_BUILD_STATIC_LIB
#if (QT_VERSION > QT_VERSION_CHECK(6, 2, 0))
Q_IMPORT_QML_PLUGIN(FluentUIPlugin)
#endif
#include <FluentUI.h>
#endif
FRAMELESSHELPER_USE_NAMESPACE FRAMELESSHELPER_USE_NAMESPACE
@ -24,7 +31,6 @@ int main(int argc, char *argv[])
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif #endif
#endif #endif
//将样式设置为Basic不然会导致组件显示异常
qputenv("QT_QUICK_CONTROLS_STYLE","Basic"); qputenv("QT_QUICK_CONTROLS_STYLE","Basic");
FramelessHelper::Quick::initialize(); FramelessHelper::Quick::initialize();
QGuiApplication::setOrganizationName("ZhuZiChu"); QGuiApplication::setOrganizationName("ZhuZiChu");
@ -35,7 +41,7 @@ int main(int argc, char *argv[])
FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow); FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow);
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur); FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur);
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
#ifdef Q_OS_WIN // 此设置仅在Windows下生效 #ifdef Q_OS_WIN
FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder); FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder);
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow,false); FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow,false);
#endif #endif
@ -46,8 +52,9 @@ int main(int argc, char *argv[])
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes(&engine); FramelessHelper::Quick::registerTypes(&engine);
#ifdef FLUENTUI_BUILD_STATIC_LIB #ifdef FLUENTUI_BUILD_STATIC_LIB
engine.addImportPath("qrc:/"); // 让静态资源可以被QML引擎搜索到 FluentUI::getInstance()->registerTypes(&engine);
#endif #endif
qDebug()<<engine.importPathList();
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");

View File

@ -1,16 +1,21 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
if (FLUENTUI_BUILD_STATIC_LIB) if (FLUENTUI_BUILD_STATIC_LIB AND (QT_VERSION VERSION_GREATER_EQUAL "6.2"))
project(fluentui LANGUAGES CXX) project(fluentui LANGUAGES CXX)
else() else()
project(fluentuiplugin LANGUAGES CXX) project(fluentuiplugin LANGUAGES CXX)
endif() endif()
#配置通用编译 #配置通用编译
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(APPLE) if(APPLE)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE) set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif() endif()
if (FLUENTUI_BUILD_STATIC_LIB)
add_definitions(-DFLUENTUI_BUILD_STATIC_LIB)
endif()
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Quick Qml) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Quick Qml)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Quick Qml) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Quick Qml)
@ -32,6 +37,9 @@ endforeach(filepath)
if(QT_VERSION VERSION_GREATER_EQUAL "6.2") if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
#删除fluentuiplugin.cpp与fluentuiplugin.h这些只要Qt5使用Qt6不需要 #删除fluentuiplugin.cpp与fluentuiplugin.h这些只要Qt5使用Qt6不需要
list(REMOVE_ITEM sources_files fluentuiplugin.h fluentuiplugin.cpp) list(REMOVE_ITEM sources_files fluentuiplugin.h fluentuiplugin.cpp)
if (NOT FLUENTUI_BUILD_STATIC_LIB)
list(REMOVE_ITEM sources_files FluentUI.h FluentUI.cpp)
endif()
#遍历所有qml文件 #遍历所有qml文件
file(GLOB_RECURSE QML_PATHS *.qml) file(GLOB_RECURSE QML_PATHS *.qml)
@ -83,6 +91,9 @@ if(WIN32)
endif() endif()
if(QT_VERSION VERSION_GREATER_EQUAL "6.2") if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
if(FLUENTUI_BUILD_STATIC_LIB)
set(FLUENTUI_QML_PLUGIN_DIRECTORY ${CMAKE_BINARY_DIR}/FluentUI)
endif()
qt_add_library(${PROJECT_NAME} ${LIB_TYPE}) qt_add_library(${PROJECT_NAME} ${LIB_TYPE})
qt_add_qml_module(${PROJECT_NAME} qt_add_qml_module(${PROJECT_NAME}
PLUGIN_TARGET ${PLUGIN_TARGET_NAME} PLUGIN_TARGET ${PLUGIN_TARGET_NAME}
@ -94,14 +105,14 @@ if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH} SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH}
QML_FILES ${qml_files} QML_FILES ${qml_files}
RESOURCES ${resource_files} RESOURCES ${resource_files}
RESOURCE_PREFIX "/" RESOURCE_PREFIX "/qt/qml"
) )
else() else()
include(QmlPlugin) include(QmlPlugin)
add_qmlplugin(${PROJECT_NAME} add_qmlplugin(${PROJECT_NAME}
URI "FluentUI" URI "FluentUI"
VERSION 1.0 VERSION 1.0
SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH} resource.qrc SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH} Qt5/imports/fluentui.qrc
QMLFILES ${qml_files} QMLFILES ${qml_files}
QMLDIR imports/FluentUI QMLDIR imports/FluentUI
BINARY_DIR ${FLUENTUI_QML_PLUGIN_DIRECTORY} BINARY_DIR ${FLUENTUI_QML_PLUGIN_DIRECTORY}
@ -109,6 +120,9 @@ else()
) )
endif() endif()
#去掉mingw生成的动态库libxxx前缀lib不去掉前缀会导致 module "FluentUI" plugin "fluentuiplugin" not found
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
#链接库 #链接库
target_link_libraries(${PROJECT_NAME} PUBLIC target_link_libraries(${PROJECT_NAME} PUBLIC
Qt${QT_VERSION_MAJOR}::CorePrivate Qt${QT_VERSION_MAJOR}::CorePrivate

View File

@ -4,6 +4,27 @@
#include <QObject> #include <QObject>
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
namespace FluViewModelType {
Q_NAMESPACE
enum Scope {
Window = 0x0000,
Application = 0x0001
};
Q_ENUM_NS(Scope)
QML_NAMED_ELEMENT(FluViewModelType)
}
namespace FluHttpType {
Q_NAMESPACE
enum CacheMode {
NoCache = 0x0000,
RequestFailedReadCache = 0x0001,
IfNoneCacheRequest = 0x0002,
FirstCacheThenRequest = 0x0004,
};
Q_ENUM_NS(CacheMode)
QML_NAMED_ELEMENT(FluHttpType)
}
namespace FluScreenshotType { namespace FluScreenshotType {
Q_NAMESPACE Q_NAMESPACE

View File

@ -18,28 +18,12 @@
class FluApp : public QObject class FluApp : public QObject
{ {
Q_OBJECT Q_OBJECT
/**
* @brief initialRoute 初始路由
*/
Q_PROPERTY_AUTO(QString,initialRoute); Q_PROPERTY_AUTO(QString,initialRoute);
/**
* @brief routes 路由表
*/
Q_PROPERTY_AUTO(QJsonObject,routes); Q_PROPERTY_AUTO(QJsonObject,routes);
/**
* @brief http拦截器
*/
Q_PROPERTY_AUTO(FluHttpInterceptor*,httpInterceptor); Q_PROPERTY_AUTO(FluHttpInterceptor*,httpInterceptor);
QML_NAMED_ELEMENT(FluApp) QML_NAMED_ELEMENT(FluApp)
QML_SINGLETON QML_SINGLETON
private: private:
/**
* @brief FluApp 将默认构造函数设置为私有则qml创建单例就会走create工厂方法创建单例
* @param parent
*/
explicit FluApp(QObject *parent = nullptr); explicit FluApp(QObject *parent = nullptr);
public: public:
~FluApp(); ~FluApp();
@ -48,50 +32,16 @@ public:
return getInstance(); return getInstance();
} }
static FluApp *getInstance(); static FluApp *getInstance();
/**
* @brief run
*/
Q_INVOKABLE void run(); Q_INVOKABLE void run();
/**
* @brief navigate
* @param route
* @param argument
* @param fluRegister
*/
Q_INVOKABLE void navigate(const QString& route,const QJsonObject& argument = {},FluRegister* fluRegister = nullptr); Q_INVOKABLE void navigate(const QString& route,const QJsonObject& argument = {},FluRegister* fluRegister = nullptr);
/**
* @brief init
* @param window
*/
Q_INVOKABLE void init(QQuickWindow *window); Q_INVOKABLE void init(QQuickWindow *window);
/**
* @brief awesomelist
* @param keyword
* @return
*/
Q_INVOKABLE QJsonArray awesomelist(const QString& keyword = ""); Q_INVOKABLE QJsonArray awesomelist(const QString& keyword = "");
/**
* @brief closeApp
*/
Q_INVOKABLE void closeApp(); Q_INVOKABLE void closeApp();
Q_INVOKABLE void deleteWindow(QQuickWindow* window); Q_INVOKABLE void deleteWindow(QQuickWindow* window);
public: public:
/**
* @brief wnds
*/
QMap<quint64, QQuickWindow*> wnds; QMap<quint64, QQuickWindow*> wnds;
private: private:
static FluApp* m_instance; static FluApp* m_instance;
/**
* @brief appWindow
*/
QWindow *appWindow; QWindow *appWindow;
}; };

View File

@ -8,7 +8,10 @@
FluCaptcha::FluCaptcha(QQuickItem *parent) FluCaptcha::FluCaptcha(QQuickItem *parent)
: QQuickPaintedItem(parent) : QQuickPaintedItem(parent)
{ {
font(QFont("楷体",25,QFont::Bold,true)); QFont fontStype;
fontStype.setPixelSize(28);
fontStype.setBold(true);
font(fontStype);
setWidth(180); setWidth(180);
setHeight(80); setHeight(80);
refresh(); refresh();

40
src/FluEventBus.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "FluEventBus.h"
FluEventBus* FluEventBus::m_instance = nullptr;
FluEvent::FluEvent(QObject *parent)
: QObject{parent}
{
}
FluEventBus *FluEventBus::getInstance()
{
if(FluEventBus::m_instance == nullptr){
FluEventBus::m_instance = new FluEventBus;
}
return FluEventBus::m_instance;
}
FluEventBus::FluEventBus(QObject *parent)
: QObject{parent}
{
}
void FluEventBus::registerEvent(FluEvent* event){
eventData.append(event);
}
void FluEventBus::unRegisterEvent(FluEvent* event){
eventData.removeOne(event);
}
void FluEventBus::post(const QString& name,const QMap<QString, QVariant>& data){
foreach (auto event, eventData) {
if(event->name()==name){
Q_EMIT event->triggered(data);
}
}
}

38
src/FluEventBus.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef FLUEVENTBUS_H
#define FLUEVENTBUS_H
#include <QObject>
#include <QtQml/qqml.h>
#include "stdafx.h"
class FluEvent : public QObject{
Q_OBJECT
Q_PROPERTY_AUTO(QString,name);
QML_NAMED_ELEMENT(FluEvent)
public:
explicit FluEvent(QObject *parent = nullptr);
Q_SIGNAL void triggered(QMap<QString, QVariant> data);
};
class FluEventBus : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(FluEventBus)
QML_SINGLETON
private:
static FluEventBus* m_instance;
explicit FluEventBus(QObject *parent = nullptr);
public:
static FluEventBus *getInstance();
static FluEventBus *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
{
return getInstance();
}
Q_INVOKABLE void registerEvent(FluEvent* event);
Q_INVOKABLE void unRegisterEvent(FluEvent* event);
Q_INVOKABLE void post(const QString& name,const QMap<QString, QVariant>& params = {});
private:
QList<FluEvent*> eventData;
};
#endif // FLUEVENTBUS_H

View File

@ -5,15 +5,69 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QUrlQuery> #include <QUrlQuery>
#include <QHttpMultiPart> #include <QHttpMultiPart>
#include <QGuiApplication>
#include <QJsonDocument> #include <QJsonDocument>
#include "MainThread.h" #include <QStandardPaths>
#include <QTextStream>
#include <QDir>
#include "Def.h"
#include "FluApp.h" #include "FluApp.h"
#include "FluTools.h"
HttpRequest::HttpRequest(QObject *parent)
: QObject{parent}
{
}
QMap<QString, QVariant> HttpRequest::toMap(){
QVariant _params;
bool isPostString = method() == "postString";
if(params().isNull()){
if(isPostString){
_params = "";
}else{
_params = QMap<QString,QVariant>();
}
}else{
_params = params();
}
QVariant _headers;
if(headers().isNull()){
_headers = QMap<QString,QVariant>();
}else{
_params = params();
}
QMap<QString, QVariant> request = {
{"url",url()},
{"headers",_headers.toMap()},
{"method",method()},
{"downloadSavePath",downloadSavePath()}
};
if(isPostString){
request.insert("params",_params.toString());
}else{
request.insert("params",_params.toMap());
}
return request;
}
QString HttpRequest::httpId(){
return FluTools::getInstance()->sha256(QJsonDocument::fromVariant(QVariant(toMap())).toJson(QJsonDocument::Compact));
}
HttpCallable::HttpCallable(QObject *parent)
: QObject{parent}
{
}
FluHttp::FluHttp(QObject *parent) FluHttp::FluHttp(QObject *parent)
: QObject{parent} : QObject{parent}
{ {
retry(3); retry(3);
timeout(15000); timeout(15000);
cacheMode(FluHttpType::CacheMode::NoCache);
cacheDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)+"/httpcache");
breakPointDownload(false);
} }
FluHttp::~FluHttp(){ FluHttp::~FluHttp(){
@ -21,27 +75,36 @@ FluHttp::~FluHttp(){
} }
void FluHttp::cancel(){ void FluHttp::cancel(){
foreach (QPointer<QNetworkReply> item, _cache) { foreach (QPointer<QNetworkReply> item, _cacheReply) {
if(item){ if(item){
item->abort(); item->abort();
} }
} }
} }
void FluHttp::handleReply(QNetworkReply* reply){ void FluHttp::post(HttpRequest* r,HttpCallable* c){
_cache.append(reply); auto request = QPointer(r);
} auto callable = QPointer(c);
request->method("post");
void FluHttp::post(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){ auto requestMap = request->toMap();
QMap<QString, QVariant> data = invokeIntercept(params,headers,"post").toMap(); auto httpId = request->httpId();
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
QThreadPool::globalInstance()->start([=](){ QThreadPool::globalInstance()->start([=](){
onStart(callable); onStart(callable);
for (int i = 0; i < retry(); ++i) { if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(httpId)){
onCache(callable,readCache(httpId));
onFinish(callable,request);
return;
}
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest && cacheExists(httpId)){
onCache(callable,readCache(httpId));
}
QNetworkAccessManager manager; QNetworkAccessManager manager;
manager.setTransferTimeout(timeout()); manager.setTransferTimeout(timeout());
QUrl _url(url); for (int i = 0; i < retry(); ++i) {
QNetworkRequest request(_url); QUrl url(request->url());
addHeaders(&request,data["headers"].toMap()); QNetworkRequest req(url);
addHeaders(&req,data["headers"].toMap());
QHttpMultiPart multiPart(QHttpMultiPart::FormDataType); QHttpMultiPart multiPart(QHttpMultiPart::FormDataType);
for (const auto& each : data["params"].toMap().toStdMap()) for (const auto& each : data["params"].toMap().toStdMap())
{ {
@ -54,198 +117,291 @@ void FluHttp::post(QString url,QJSValue callable,QMap<QString, QVariant> params,
multiPart.append(part); multiPart.append(part);
} }
QEventLoop loop; QEventLoop loop;
QNetworkReply* reply = manager.post(request,&multiPart); QNetworkReply* reply = manager.post(req,&multiPart);
_cache.append(reply); _cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){ connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();});
loop.quit(); connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();});
});
loop.exec(); loop.exec();
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; bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if (isSuccess) { if (isSuccess) {
handleCache(httpId,result);
onSuccess(callable,result); onSuccess(callable,result);
break; break;
}else{ }else{
if(i == retry()-1){ if(i == retry()-1){
if(_cacheMode == FluHttpType::CacheMode::RequestFailedReadCache && cacheExists(httpId)){
onCache(callable,readCache(httpId));
}
onError(callable,status,errorString,result); onError(callable,status,errorString,result);
} }
} }
} }
onFinish(callable); onFinish(callable,request);
}); });
} }
void FluHttp::postString(QString url,QJSValue callable,QString params,QMap<QString, QVariant> headers){ void FluHttp::postString(HttpRequest* r,HttpCallable* c){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"postString").toMap(); auto request = QPointer(r);
auto callable = QPointer(c);
request->method("postString");
auto requestMap = request->toMap();
auto httpId = request->httpId();
QString params = request->params().toString();
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
QThreadPool::globalInstance()->start([=](){ QThreadPool::globalInstance()->start([=](){
onStart(callable); onStart(callable);
for (int i = 0; i < retry(); ++i) { if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(httpId)){
QNetworkAccessManager manager; onCache(callable,readCache(httpId));
manager.setTransferTimeout(timeout()); onFinish(callable,request);
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QString contentType = QString("text/plain;charset=utf-8");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop;
QNetworkReply* reply = manager.post(request,params.toUtf8());
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString,result);
}
}
}
onFinish(callable);
});
}
void FluHttp::postJson(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"postJson").toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QString contentType = QString("application/json;charset=utf-8");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop;
QNetworkReply* reply = manager.post(request,QJsonDocument::fromVariant(data["params"]).toJson());
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString,result);
}
}
}
onFinish(callable);
});
}
void FluHttp::get(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"get").toMap();
QThreadPool::globalInstance()->start([=](){
for (int i = 0; i < retry(); ++i) {
onStart(callable);
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
addQueryParam(&_url,data["params"].toMap());
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QEventLoop loop;
QNetworkReply* reply = manager.get(request);
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString,result);
}
}
}
onFinish(callable);
});
}
void FluHttp::download(QString url,QJSValue callable,QString filePath,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"download").toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
QNetworkAccessManager manager;
QUrl _url(url);
addQueryParam(&_url,data["params"].toMap());
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QSharedPointer<QFile> file(new QFile(filePath));
QIODevice::OpenMode mode = QIODevice::WriteOnly|QIODevice::Truncate;
if (!file->open(mode))
{
onError(callable,-1,QString("Url: %1 %2 Non-Writable").arg(request.url().toString(),file->fileName()),"");
onFinish(callable);
return; return;
} }
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest && cacheExists(httpId)){
onCache(callable,readCache(httpId));
}
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
for (int i = 0; i < retry(); ++i) {
QUrl url(request->url());
QNetworkRequest req(url);
addHeaders(&req,data["headers"].toMap());
QString contentType = QString("text/plain;charset=utf-8");
req.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop; QEventLoop loop;
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){ QNetworkReply* reply = manager.post(req,params.toUtf8());
loop.quit(); _cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();});
connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
handleCache(httpId,result);
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
if(_cacheMode == FluHttpType::CacheMode::RequestFailedReadCache && cacheExists(httpId)){
onCache(callable,readCache(httpId));
}
onError(callable,status,errorString,result);
}
}
}
onFinish(callable,request);
}); });
QPointer<QNetworkReply> reply = manager.get(request); }
_cache.append(reply);
connect(reply,&QNetworkReply::downloadProgress,this,[=](qint64 bytesReceived, qint64 bytesTotal){ void FluHttp::postJson(HttpRequest* r,HttpCallable* c){
onDownloadProgress(callable,bytesReceived,bytesTotal); auto request = QPointer(r);
auto callable = QPointer(c);
request->method("postJson");
auto requestMap = request->toMap();
auto httpId = request->httpId();
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(httpId)){
onCache(callable,readCache(httpId));
onFinish(callable,request);
return;
}
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest && cacheExists(httpId)){
onCache(callable,readCache(httpId));
}
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
for (int i = 0; i < retry(); ++i) {
QUrl url(request->url());
QNetworkRequest req(url);
addHeaders(&req,data["headers"].toMap());
QString contentType = QString("application/json;charset=utf-8");
req.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop;
QNetworkReply* reply = manager.post(req,QJsonDocument::fromVariant(data["params"]).toJson());
_cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();});
connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
handleCache(httpId,result);
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
if(_cacheMode == FluHttpType::CacheMode::RequestFailedReadCache && cacheExists(httpId)){
onCache(callable,readCache(httpId));
}
onError(callable,status,errorString,result);
}
}
}
onFinish(callable,request);
});
}
void FluHttp::get(HttpRequest* r,HttpCallable* c){
auto request = QPointer(r);
auto callable = QPointer(c);
request->method("get");
auto requestMap = request->toMap();
auto httpId = request->httpId();
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest && cacheExists(httpId)){
onCache(callable,readCache(httpId));
}
if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(httpId)){
onCache(callable,readCache(httpId));
onFinish(callable,request);
return;
}
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
for (int i = 0; i < retry(); ++i) {
QUrl url(request->url());
addQueryParam(&url,data["params"].toMap());
QNetworkRequest req(url);
addHeaders(&req,data["headers"].toMap());
QEventLoop loop;
QNetworkReply* reply = manager.get(req);
_cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();});
connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();});
loop.exec();
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
QString result = QString::fromUtf8(reply->readAll());
if (isSuccess) {
handleCache(httpId,result);
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
if(_cacheMode == FluHttpType::CacheMode::RequestFailedReadCache && cacheExists(httpId)){
onCache(callable,readCache(httpId));
}
onError(callable,status,errorString,result);
}
}
reply->deleteLater();
reply = nullptr;
}
onFinish(callable,request);
});
}
void FluHttp::download(HttpRequest* r,HttpCallable* c){
auto request = QPointer(r);
auto callable = QPointer(c);
request->method("download");
auto requestMap = request->toMap();
auto httpId = request->httpId();
auto savePath = request->downloadSavePath();
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
QNetworkAccessManager manager;
QUrl url(request->url());
addQueryParam(&url,data["params"].toMap());
QNetworkRequest req(url);
addHeaders(&req,data["headers"].toMap());
QSharedPointer<QFile> file(new QFile(savePath));
QDir dir = QFileInfo(savePath).path();
if (!dir.exists(dir.path())){
dir.mkpath(dir.path());
}
QEventLoop loop;
connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();});
connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();});
qint64 seek = 0;
auto filePath = getCacheFilePath(httpId);
QSharedPointer<QFile> fileCache(new QFile(filePath));
if(fileCache->exists() && file->exists() && _breakPointDownload){
QJsonObject cacheInfo = QJsonDocument::fromJson(readCache(httpId).toUtf8()).object();
qint64 fileSize = cacheInfo.value("fileSize").toDouble();
qint64 contentLength = cacheInfo.value("contentLength").toDouble();
if(fileSize == contentLength && file->size() == contentLength){
onDownloadProgress(callable,fileSize,contentLength);
onSuccess(callable,savePath);
onFinish(callable,request);
return;
}
if(fileSize==file->size()){
req.setRawHeader("Range", QString("bytes=%1-").arg(fileSize).toUtf8());
seek = fileSize;
file->open(QIODevice::WriteOnly|QIODevice::Append);
}else{
file->open(QIODevice::WriteOnly|QIODevice::Truncate);
}
}else{
file->open(QIODevice::WriteOnly|QIODevice::Truncate);
}
QNetworkReply* reply = manager.get(req);
_cacheReply.append(reply);
if (!fileCache->open(QIODevice::WriteOnly|QIODevice::Truncate))
{
qDebug()<<"FileCache Error";
}
connect(reply,&QNetworkReply::readyRead,reply,[reply,file,fileCache,requestMap,callable,seek,this]{
if (!reply || !file || reply->error() != QNetworkReply::NoError)
{
return;
}
QMap<QString, QVariant> downMap = requestMap;
qint64 contentLength = reply->header(QNetworkRequest::ContentLengthHeader).toLongLong()+seek;
downMap.insert("contentLength",contentLength);
QString eTag = reply->header(QNetworkRequest::ETagHeader).toString();
downMap.insert("eTag",eTag);
file->write(reply->readAll());
file->flush();
downMap.insert("fileSize",file->size());
fileCache->resize(0);
fileCache->write(FluTools::getInstance()->toBase64(QJsonDocument::fromVariant(QVariant(downMap)).toJson()).toUtf8());
fileCache->flush();
onDownloadProgress(callable,file->size(),contentLength);
}); });
loop.exec(); loop.exec();
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
file->write(reply->readAll()); onSuccess(callable,savePath);
onSuccess(callable,filePath);
}else{ }else{
onError(callable,reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),reply->errorString(),""); onError(callable,reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),reply->errorString(),"");
} }
_cache.removeOne(reply);
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
onFinish(callable); onFinish(callable,request);
}); });
} }
void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){ void FluHttp::upload(HttpRequest* request,HttpCallable* callable){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"upload").toMap(); request->method("upload");
auto requestMap = request->toMap();
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
QThreadPool::globalInstance()->start([=](){ QThreadPool::globalInstance()->start([=](){
onStart(callable); onStart(callable);
QNetworkAccessManager manager; QNetworkAccessManager manager;
manager.setTransferTimeout(timeout()); manager.setTransferTimeout(timeout());
QUrl _url(url); QUrl url(request->url());
QNetworkRequest request(_url); QNetworkRequest req(url);
addHeaders(&request,data["headers"].toMap()); addHeaders(&req,data["headers"].toMap());
QHttpMultiPart multiPart(QHttpMultiPart::FormDataType); QHttpMultiPart multiPart(QHttpMultiPart::FormDataType);
qDebug()<<data["params"].toMap();
for (const auto& each : data["params"].toMap().toStdMap()) for (const auto& each : data["params"].toMap().toStdMap())
{ {
const QString& key = each.first; const QString& key = each.first;
@ -261,12 +417,11 @@ void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> param
multiPart.append(part); multiPart.append(part);
} }
QEventLoop loop; QEventLoop loop;
QNetworkReply* reply = manager.post(request,&multiPart); QNetworkReply* reply = manager.post(req,&multiPart);
_cache.append(reply); _cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){ connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();});
loop.quit(); connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();});
}); connect(reply,&QNetworkReply::uploadProgress,reply,[=](qint64 bytesSent, qint64 bytesTotal){
connect(reply,&QNetworkReply::uploadProgress,this,[=](qint64 bytesSent, qint64 bytesTotal){
onUploadProgress(callable,bytesSent,bytesTotal); onUploadProgress(callable,bytesSent,bytesTotal);
}); });
loop.exec(); loop.exec();
@ -274,7 +429,6 @@ void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> param
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; bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if (isSuccess) { if (isSuccess) {
@ -282,21 +436,16 @@ void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> param
}else{ }else{
onError(callable,status,errorString,result); onError(callable,status,errorString,result);
} }
onFinish(callable); onFinish(callable,request);
}); });
} }
QVariant FluHttp::invokeIntercept(const QVariant& params,const QVariant& headers,const QString& method){ QVariant FluHttp::invokeIntercept(QMap<QString, QVariant> request){
QMap<QString, QVariant> requet = {
{"params",params},
{"headers",headers},
{"method",method}
};
if(!FluApp::getInstance()->httpInterceptor()){ if(!FluApp::getInstance()->httpInterceptor()){
return requet; return request;
} }
QVariant target; QVariant target;
QMetaObject::invokeMethod(FluApp::getInstance()->httpInterceptor(), "onIntercept",Q_RETURN_ARG(QVariant,target),Q_ARG(QVariant, requet)); QMetaObject::invokeMethod(FluApp::getInstance()->httpInterceptor(), "onIntercept",Q_RETURN_ARG(QVariant,target),Q_ARG(QVariant, request));
return target; return target;
} }
@ -320,54 +469,117 @@ void FluHttp::addHeaders(QNetworkRequest* request,const QMap<QString, QVariant>&
} }
} }
void FluHttp::onStart(const QJSValue& callable){ QString FluHttp::readCache(const QString& httpId){
MainThread::post([=](){ auto filePath = getCacheFilePath(httpId);
QJSValue onStart = callable.property("onStart"); QString result;
onStart.call(); QFile file(filePath);
}); if(!file.exists()){
return result;
}
if (file.open(QIODevice::ReadOnly)) {
QTextStream stream(&file);
result = FluTools::getInstance()->fromBase64(stream.readAll().toUtf8());
}
return result;
} }
void FluHttp::onFinish(const QJSValue& callable){ bool FluHttp::cacheExists(const QString& httpId){
MainThread::post([=](){ return QFile(getCacheFilePath(httpId)).exists();
QJSValue onFinish = callable.property("onFinish");
onFinish.call();
});
} }
void FluHttp::onError(const QJSValue& callable,int status,QString errorString,QString result){ QString FluHttp::getCacheFilePath(const QString& httpId){
MainThread::post([=](){ QDir dir = _cacheDir;
QJSValue onError = callable.property("onError"); if (!dir.exists(_cacheDir)){
QJSValueList args; dir.mkpath(_cacheDir);
args<<status<<errorString<<result; }
onError.call(args); auto filePath = _cacheDir+"/"+httpId;
}); return filePath;
} }
void FluHttp::onSuccess(const QJSValue& callable,QString result){ void FluHttp::handleCache(const QString& httpId,const QString& result){
MainThread::post([=](){ if(_cacheMode==FluHttpType::CacheMode::NoCache){
QJSValueList args; return;
args<<result; }
QJSValue onSuccess = callable.property("onSuccess"); auto filePath = getCacheFilePath(httpId);
onSuccess.call(args); QSharedPointer<QFile> file(new QFile(filePath));
}); QIODevice::OpenMode mode = QIODevice::WriteOnly|QIODevice::Truncate;
if (!file->open(mode))
{
return;
}
file->write(FluTools::getInstance()->toBase64(result).toUtf8());
} }
void FluHttp::onDownloadProgress(const QJSValue& callable,qint64 recv, qint64 total){ qreal FluHttp::getBreakPointProgress(HttpRequest* request){
MainThread::post([=](){ request->method("download");
QJSValueList args; auto httpId = request->httpId();
args<<static_cast<double>(recv); QSharedPointer<QFile> file(new QFile(request->downloadSavePath()));
args<<static_cast<double>(total); auto filePath = getCacheFilePath(httpId);
QJSValue onDownloadProgress = callable.property("onDownloadProgress"); QSharedPointer<QFile> fileCache(new QFile(filePath));
onDownloadProgress.call(args); if(fileCache->exists() && file->exists() && _breakPointDownload){
}); QJsonObject cacheInfo = QJsonDocument::fromJson(readCache(httpId).toUtf8()).object();
double fileSize = cacheInfo.value("fileSize").toDouble();
double contentLength = cacheInfo.value("contentLength").toDouble();
if(fileSize == contentLength && file->size() == contentLength){
return 1;
}
if(fileSize==file->size()){
return fileSize/contentLength;
}else{
return 0;
}
}else{
return 0;
}
} }
void FluHttp::onUploadProgress(const QJSValue& callable,qint64 sent, qint64 total){ HttpRequest* FluHttp::newRequest(QString url){
MainThread::post([=](){ HttpRequest* request = new HttpRequest(this);
QJSValueList args; request->url(url);
args<<static_cast<double>(sent); return request;
args<<static_cast<double>(total); }
QJSValue onUploadProgress = callable.property("onUploadProgress");
onUploadProgress.call(args); void FluHttp::onStart(QPointer<HttpCallable> callable){
}); if(callable){
Q_EMIT callable->start();
}
}
void FluHttp::onFinish(QPointer<HttpCallable> callable,QPointer<HttpRequest> request){
if(callable){
Q_EMIT callable->finish();
}
if(request&&request->parent()->inherits("FluHttp")){
request->deleteLater();
}
}
void FluHttp::onError(QPointer<HttpCallable> callable,int status,QString errorString,QString result){
if(callable){
Q_EMIT callable->error(status,errorString,result);
}
}
void FluHttp::onSuccess(QPointer<HttpCallable> callable,QString result){
if(callable){
Q_EMIT callable->success(result);
}
}
void FluHttp::onCache(QPointer<HttpCallable> callable,QString result){
if(callable){
Q_EMIT callable->cache(result);
}
}
void FluHttp::onDownloadProgress(QPointer<HttpCallable> callable,qint64 recv,qint64 total){
if(callable){
Q_EMIT callable->downloadProgress(recv,total);
}
}
void FluHttp::onUploadProgress(QPointer<HttpCallable> callable,qint64 sent,qint64 total){
if(callable){
Q_EMIT callable->uploadProgress(sent,total);
}
} }

View File

@ -7,36 +7,72 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include "stdafx.h" #include "stdafx.h"
class HttpRequest : public QObject{
Q_OBJECT
Q_PROPERTY_AUTO(QString,url);
Q_PROPERTY_AUTO(QVariant,params);
Q_PROPERTY_AUTO(QVariant,headers);
Q_PROPERTY_AUTO(QString,method);
Q_PROPERTY_AUTO(QString,downloadSavePath);
QML_NAMED_ELEMENT(HttpRequest)
public:
explicit HttpRequest(QObject *parent = nullptr);
QMap<QString, QVariant> toMap();
Q_INVOKABLE QString httpId();
};
class HttpCallable : public QObject{
Q_OBJECT
QML_NAMED_ELEMENT(HttpCallable)
public:
explicit HttpCallable(QObject *parent = nullptr);
Q_SIGNAL void start();
Q_SIGNAL void finish();
Q_SIGNAL void error(int status,QString errorString,QString result);
Q_SIGNAL void success(QString result);
Q_SIGNAL void cache(QString result);
Q_SIGNAL void downloadProgress(qint64 recv, qint64 total);
Q_SIGNAL void uploadProgress(qint64 sent, qint64 total);
};
class FluHttp : public QObject class FluHttp : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY_AUTO(int,retry); Q_PROPERTY_AUTO(int,retry);
Q_PROPERTY_AUTO(int,timeout) Q_PROPERTY_AUTO(int,timeout)
Q_PROPERTY_AUTO(int,cacheMode);
Q_PROPERTY_AUTO(QString,cacheDir);
Q_PROPERTY_AUTO(bool,breakPointDownload);
QML_NAMED_ELEMENT(FluHttp) QML_NAMED_ELEMENT(FluHttp)
private: private:
QVariant invokeIntercept(const QVariant& params,const QVariant& headers,const QString& method); QVariant invokeIntercept(QMap<QString, QVariant> request);
void handleReply(QNetworkReply* reply);
void addQueryParam(QUrl* url,const QMap<QString, QVariant>& params); void addQueryParam(QUrl* url,const QMap<QString, QVariant>& params);
void addHeaders(QNetworkRequest* request,const QMap<QString, QVariant>& params); void addHeaders(QNetworkRequest* request,const QMap<QString, QVariant>& params);
void onStart(const QJSValue& callable); void handleCache(const QString& httpId, const QString& result);
void onFinish(const QJSValue& callable); QString readCache(const QString& httpId);
void onError(const QJSValue& callable,int status,QString errorString,QString result); bool cacheExists(const QString& httpId);
void onSuccess(const QJSValue& callable,QString result); QString getCacheFilePath(const QString& httpId);
void onDownloadProgress(const QJSValue& callable,qint64 recv, qint64 total); void onStart(QPointer<HttpCallable> callable);
void onUploadProgress(const QJSValue& callable,qint64 recv, qint64 total); void onFinish(QPointer<HttpCallable> callable,QPointer<HttpRequest> request);
void onError(QPointer<HttpCallable> callable,int status,QString errorString,QString result);
void onSuccess(QPointer<HttpCallable> callable,QString result);
void onCache(QPointer<HttpCallable> callable,QString result);
void onDownloadProgress(QPointer<HttpCallable> callable,qint64 recv,qint64 total);
void onUploadProgress(QPointer<HttpCallable> callable,qint64 sent,qint64 total);
public: public:
explicit FluHttp(QObject *parent = nullptr); explicit FluHttp(QObject *parent = nullptr);
~FluHttp(); ~FluHttp();
//神坑!!! 如果参数使用QVariantMap会有问题在6.4.3版本中QML一调用就会编译失败。所以改用QMap<QString, QVariant> Q_INVOKABLE HttpRequest* newRequest(QString url = "");
Q_INVOKABLE void get(QString url,QJSValue callable,QMap<QString, QVariant> params= {},QMap<QString, QVariant> headers = {}); Q_INVOKABLE void get(HttpRequest* request,HttpCallable* callable);
Q_INVOKABLE void post(QString url,QJSValue callable,QMap<QString, QVariant> params= {},QMap<QString, QVariant> headers = {}); Q_INVOKABLE void post(HttpRequest* request,HttpCallable* callable);
Q_INVOKABLE void postString(QString url,QJSValue callable,QString params = "",QMap<QString, QVariant> headers = {}); Q_INVOKABLE void postString(HttpRequest* request,HttpCallable* callable);
Q_INVOKABLE void postJson(QString url,QJSValue callable,QMap<QString, QVariant> params = {},QMap<QString, QVariant> headers = {}); Q_INVOKABLE void postJson(HttpRequest* request,HttpCallable* callable);
Q_INVOKABLE void download(QString url,QJSValue callable,QString filePath,QMap<QString, QVariant> params = {},QMap<QString, QVariant> headers = {}); Q_INVOKABLE void download(HttpRequest* request,HttpCallable* callable);
Q_INVOKABLE void upload(QString url,QJSValue callable,QMap<QString, QVariant> params = {},QMap<QString, QVariant> headers = {}); Q_INVOKABLE void upload(HttpRequest* request,HttpCallable* callable);
Q_INVOKABLE qreal getBreakPointProgress(HttpRequest* request);
Q_INVOKABLE void cancel(); Q_INVOKABLE void cancel();
private: private:
QList<QPointer<QNetworkReply>> _cache; QList<QPointer<QNetworkReply>> _cacheReply;
}; };
#endif // FLUHTTP_H #endif // FLUHTTP_H

View File

@ -17,25 +17,9 @@ class FluRegister : public QObject
Q_PROPERTY_AUTO(QString,path); Q_PROPERTY_AUTO(QString,path);
public: public:
explicit FluRegister(QObject *parent = nullptr); explicit FluRegister(QObject *parent = nullptr);
/**
* @brief launch 窗口跳转
* @param argument 跳转携带参数
*/
Q_INVOKABLE void launch(const QJsonObject& argument = {}); Q_INVOKABLE void launch(const QJsonObject& argument = {});
/**
* @brief onResult 将结果数据回传到上一个窗口
* @param data 结果数据
*/
Q_INVOKABLE void onResult(const QJsonObject& data = {}); Q_INVOKABLE void onResult(const QJsonObject& data = {});
/**
* @brief result 收到结果数据的信号
* @param data 结果数据
*/
Q_SIGNAL void result(const QJsonObject& data); Q_SIGNAL void result(const QJsonObject& data);
}; };
#endif // FLUREGISTER_H #endif // FLUREGISTER_H

View File

@ -31,7 +31,7 @@ FluTheme::FluTheme(QObject *parent)
}); });
primaryColor(FluColors::getInstance()->Blue()); primaryColor(FluColors::getInstance()->Blue());
nativeText(false); nativeText(false);
enableAnimation(false); enableAnimation(true);
darkMode(FluThemeType::DarkMode::Light); darkMode(FluThemeType::DarkMode::Light);
_systemDark = systemDark(); _systemDark = systemDark();
qApp->installEventFilter(this); qApp->installEventFilter(this);

View File

@ -12,31 +12,11 @@
class FluTheme : public QObject class FluTheme : public QObject
{ {
Q_OBJECT Q_OBJECT
/**
* @brief dark 改变窗口夜间样式只读属性可以通过darkMode切换
*/
Q_PROPERTY(bool dark READ dark NOTIFY darkChanged) Q_PROPERTY(bool dark READ dark NOTIFY darkChanged)
/**
* @brief primaryColor 主题颜色
*/
Q_PROPERTY_AUTO(FluColorSet*,primaryColor) Q_PROPERTY_AUTO(FluColorSet*,primaryColor)
/**
* @brief darkMode 夜间模式支持System=0、Light=1、Dark=2
*/
Q_PROPERTY_AUTO(int,darkMode); Q_PROPERTY_AUTO(int,darkMode);
/**
* @brief nativeText 本地渲染文本
*/
Q_PROPERTY_AUTO(bool,nativeText); Q_PROPERTY_AUTO(bool,nativeText);
/**
* @brief 是否开启动画效果
*/
Q_PROPERTY_AUTO(bool,enableAnimation); Q_PROPERTY_AUTO(bool,enableAnimation);
QML_NAMED_ELEMENT(FluTheme) QML_NAMED_ELEMENT(FluTheme)
QML_SINGLETON QML_SINGLETON
private: private:

View File

@ -6,6 +6,9 @@
#include <QScreen> #include <QScreen>
#include <QColor> #include <QColor>
#include <QFileInfo> #include <QFileInfo>
#include <QProcess>
#include <QDir>
#include <QCryptographicHash>
#include <QTextDocument> #include <QTextDocument>
FluTools* FluTools::m_instance = nullptr; FluTools* FluTools::m_instance = nullptr;
@ -128,3 +131,48 @@ QUrl FluTools::getUrlByFilePath(const QString& path){
QColor FluTools::colorAlpha(const QColor& color,qreal alpha){ QColor FluTools::colorAlpha(const QColor& color,qreal alpha){
return QColor(color.red(),color.green(),color.blue(),255*alpha); return QColor(color.red(),color.green(),color.blue(),255*alpha);
} }
QString FluTools::md5(QString text)
{
return QCryptographicHash::hash(text.toUtf8(), QCryptographicHash::Md5).toHex();
}
QString FluTools::toBase64(QString text)
{
return text.toUtf8().toBase64();
}
QString FluTools::fromBase64(QString text)
{
return QByteArray::fromBase64(text.toUtf8());
}
bool FluTools::removeDir(QString dirPath){
QDir qDir(dirPath);
return qDir.removeRecursively();
}
bool FluTools::removeFile(QString filePath){
QFile file(filePath);
return file.remove();
}
QString FluTools::sha256(QString text){
return QCryptographicHash::hash(text.toUtf8(), QCryptographicHash::Sha256).toHex();
}
void FluTools::showFileInFolder(QString path){
#if defined(Q_OS_WIN)
QProcess::startDetached("explorer.exe", {"/select,", QDir::toNativeSeparators(path)});
#endif
#if defined(Q_OS_LINUX)
QFileInfo fileInfo(path);
auto process = "xdg-open";
auto arguments = { fileInfo.absoluteDir().absolutePath() };
QProcess::startDetached(process, arguments);
#endif
#if defined(Q_OS_MACOS)
QProcess::execute("/usr/bin/osascript", {"-e", "tell application \"Finder\" to reveal POSIX file \"" + path + "\""});
QProcess::execute("/usr/bin/osascript", {"-e", "tell application \"Finder\" to activate"});
#endif
}

View File

@ -25,125 +25,58 @@ public:
} }
static FluTools *getInstance(); static FluTools *getInstance();
/**
* @brief qtMajor Qt Major版本
* @return
*/
Q_INVOKABLE int qtMajor(); Q_INVOKABLE int qtMajor();
/**
* @brief qtMajor Qt Minor版本
* @return
*/
Q_INVOKABLE int qtMinor(); Q_INVOKABLE int qtMinor();
/**
* @brief isMacos 是否是Macos系统
* @return
*/
Q_INVOKABLE bool isMacos(); Q_INVOKABLE bool isMacos();
/**
* @brief isLinux 是否是Linux系统
* @return
*/
Q_INVOKABLE bool isLinux(); Q_INVOKABLE bool isLinux();
/**
* @brief isWin 是否是Windows系统
* @return
*/
Q_INVOKABLE bool isWin(); Q_INVOKABLE bool isWin();
/**
* @brief clipText 将字符串添加到剪切板
* @param text
*/
Q_INVOKABLE void clipText(const QString& text); Q_INVOKABLE void clipText(const QString& text);
/**
* @brief uuid 获取uuid
* @return
*/
Q_INVOKABLE QString uuid(); Q_INVOKABLE QString uuid();
/**
* @brief readFile 读取文件内容
* @param fileName
* @return
*/
Q_INVOKABLE QString readFile(const QString& fileName); Q_INVOKABLE QString readFile(const QString& fileName);
/**
* @brief setQuitOnLastWindowClosed 设置关闭最后一个窗口是否退出程序
* @param val
*/
Q_INVOKABLE void setQuitOnLastWindowClosed(bool val); Q_INVOKABLE void setQuitOnLastWindowClosed(bool val);
/**
* @brief setOverrideCursor 设置全局鼠标样式
* @param shape
*/
Q_INVOKABLE void setOverrideCursor(Qt::CursorShape shape); Q_INVOKABLE void setOverrideCursor(Qt::CursorShape shape);
/**
* @brief restoreOverrideCursor 还原全局鼠标样式
*/
Q_INVOKABLE void restoreOverrideCursor(); Q_INVOKABLE void restoreOverrideCursor();
/**
* @brief html2PlantText 将html转换成纯文本
* @param html
*/
Q_INVOKABLE QString html2PlantText(const QString& html); Q_INVOKABLE QString html2PlantText(const QString& html);
/**
* @brief toLocalPath 获取文件路径可以去掉windows系统下的file:///macos下的file://
* @param url
* @return 返回文件路径
*/
Q_INVOKABLE QString toLocalPath(const QUrl& url); Q_INVOKABLE QString toLocalPath(const QUrl& url);
/**
* @brief deleteItem 销毁Item对象
* @param p
*/
Q_INVOKABLE void deleteItem(QObject *p); Q_INVOKABLE void deleteItem(QObject *p);
/**
* @brief getFileNameByUrl
* @param url
* @return
*/
Q_INVOKABLE QString getFileNameByUrl(const QUrl& url); Q_INVOKABLE QString getFileNameByUrl(const QUrl& url);
/**
* @brief getVirtualGeometry
* @return
*/
Q_INVOKABLE QRect getVirtualGeometry(); Q_INVOKABLE QRect getVirtualGeometry();
/**
* @brief getApplicationDirPath
* @return
*/
Q_INVOKABLE QString getApplicationDirPath(); Q_INVOKABLE QString getApplicationDirPath();
/**
* @brief getUrlByFilePath
* @param path
* @return
*/
Q_INVOKABLE QUrl getUrlByFilePath(const QString& path); Q_INVOKABLE QUrl getUrlByFilePath(const QString& path);
/**
* @brief colorAlpha
* @param color
* @param alpha
* @return
*/
Q_INVOKABLE QColor colorAlpha(const QColor&,qreal alpha); Q_INVOKABLE QColor colorAlpha(const QColor&,qreal alpha);
Q_INVOKABLE QString md5(QString text);
Q_INVOKABLE QString sha256(QString text);
Q_INVOKABLE QString toBase64(QString text);
Q_INVOKABLE QString fromBase64(QString text);
Q_INVOKABLE bool removeDir(QString dirPath);
Q_INVOKABLE bool removeFile(QString filePath);
Q_INVOKABLE void showFileInFolder(QString path);
}; };
#endif // FLUTOOLS_H #endif // FLUTOOLS_H

137
src/FluViewModel.cpp Normal file
View File

@ -0,0 +1,137 @@
#include "FluViewModel.h"
#include <QQuickItem>
#include "Def.h"
ViewModelManager* ViewModelManager::m_instance = nullptr;
ViewModelManager *ViewModelManager::getInstance()
{
if(ViewModelManager::m_instance == nullptr){
ViewModelManager::m_instance = new ViewModelManager;
}
return ViewModelManager::m_instance;
}
Model::Model(QObject *parent)
: QObject{parent}
{
}
Model::~Model()
{
}
ViewModelManager::ViewModelManager(QObject *parent)
: QObject{parent}
{
}
void ViewModelManager::insertViewModel(FluViewModel* value){
m_viewmodel.append(value);
}
void ViewModelManager::deleteViewModel(FluViewModel* value){
m_viewmodel.removeOne(value);
}
QObject* ViewModelManager::getModel(const QString& key){
return m_data.value(key);
}
void ViewModelManager::insert(const QString& key,QObject* value){
m_data.insert(key,value);
}
bool ViewModelManager::exist(const QString& key){
return m_data.contains(key);
}
void ViewModelManager::refreshViewModel(FluViewModel* viewModel,QString key,QVariant value){
foreach (auto item, m_viewmodel) {
if(item->getKey() == viewModel->getKey()){
item->setProperty(key.toStdString().c_str(),value);
}
}
}
PropertyObserver::PropertyObserver(QString name,QObject* model,QObject *parent)
: QObject{parent}
{
_name = name;
_model = model;
_property = QQmlProperty(parent,_name);
_property.connectNotifySignal(this,SLOT(_propertyChange()));
}
PropertyObserver::~PropertyObserver(){
}
void PropertyObserver::_propertyChange(){
auto value = _property.read();
_model->setProperty(_name.toStdString().c_str(),value);
ViewModelManager::getInstance()->refreshViewModel((FluViewModel*)parent(),_name,value);
}
FluViewModel::FluViewModel(QObject *parent)
: QObject{parent}
{
ViewModelManager::getInstance()->insertViewModel(this);
scope(FluViewModelType::Scope::Window);
}
FluViewModel::~FluViewModel(){
ViewModelManager::getInstance()->deleteViewModel(this);
}
void FluViewModel::classBegin()
{
}
void FluViewModel::componentComplete()
{
auto o = parent();
while (nullptr != o) {
_window = o;
o = o->parent();
}
const QMetaObject* obj = metaObject();
if(_scope == FluViewModelType::Scope::Window){
_key = property("objectName_").toString()+QString::number(reinterpret_cast<qulonglong>(_window), 16);
}else{
_key = property("objectName").toString();
}
QObject * model;
if(!ViewModelManager::getInstance()->exist(_key)){
if(_scope == FluViewModelType::Scope::Window){
model = new Model(_window);
}else{
model = new Model();
}
Q_EMIT initData();
for (int i = 0; i < obj->propertyCount(); ++i) {
const QMetaProperty property = obj->property(i);
QString propertyName = property.name();
auto value = property.read(this);
model->setProperty(propertyName.toStdString().c_str(),value);
new PropertyObserver(propertyName,model,this);
}
ViewModelManager::getInstance()->insert(_key,model);
}else{
model = ViewModelManager::getInstance()->getModel(_key);
for (int i = 0; i < obj->propertyCount(); ++i) {
const QMetaProperty property = obj->property(i);
QString propertyName = property.name();
new PropertyObserver(propertyName,model,this);
}
}
foreach (auto key, model->dynamicPropertyNames()) {
setProperty(key,model->property(key));
}
}
QString FluViewModel::getKey(){
return _key;
}

68
src/FluViewModel.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef FLUVIEWMODEL_H
#define FLUVIEWMODEL_H
#include <QQuickItem>
#include <QtQml/qqml.h>
#include <QQuickWindow>
#include <QQmlProperty>
#include "stdafx.h"
class Model : public QObject{
Q_OBJECT
public:
explicit Model(QObject *parent = nullptr);
~Model();
};
class FluViewModel : public QObject, public QQmlParserStatus
{
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY_AUTO(int,scope);
Q_PROPERTY_AUTO(QObject*,target);
QML_NAMED_ELEMENT(FluViewModel)
public:
explicit FluViewModel(QObject *parent = nullptr);
~FluViewModel();
void classBegin() override;
void componentComplete() override;
Q_SIGNAL void initData();
QString getKey();
private:
QObject* _window = nullptr;
QString _key;
};
class PropertyObserver: public QObject{
Q_OBJECT
public:
explicit PropertyObserver(QString name,QObject* model,QObject *parent = nullptr);
~PropertyObserver();
private:
Q_SLOT void _propertyChange();
private:
QString _name;
QQmlProperty _property;
QObject* _model;
};
class ViewModelManager:public QObject{
Q_OBJECT
private:
explicit ViewModelManager(QObject *parent = nullptr);
public:
static ViewModelManager *getInstance();
bool exist(const QString& key);
void insert(const QString& key,QObject* value);
QObject* getModel(const QString& key);
void insertViewModel(FluViewModel* value);
void deleteViewModel(FluViewModel* value);
void refreshViewModel(FluViewModel* viewModel,QString key,QVariant value);
private:
static ViewModelManager* m_instance;
QMap<QString,QObject*> m_data;
QList<FluViewModel*> m_viewmodel;
};
#endif // FLUVIEWMODEL_H

190
src/FluentUI.cpp Normal file
View File

@ -0,0 +1,190 @@
#include "FluentUI.h"
#include <QGuiApplication>
#include "WindowHelper.h"
#include "Def.h"
#include "FluApp.h"
#include "FluColors.h"
#include "FluTheme.h"
#include "FluTools.h"
#include "FluTextStyle.h"
#include "FluHttp.h"
#include "FluHttpInterceptor.h"
#include "FluWatermark.h"
#include "FluCaptcha.h"
#include "FluEventBus.h"
#include "FluViewModel.h"
#include "Screenshot.h"
#include "QRCode.h"
int major = 1;
int minor = 0;
auto uri = "FluentUI";
FluentUI* FluentUI::m_instance = nullptr;
FluentUI *FluentUI::getInstance()
{
if(FluentUI::m_instance == nullptr){
FluentUI::m_instance = new FluentUI;
}
return FluentUI::m_instance;
}
void FluentUI::registerTypes(QQmlEngine *engine){
initializeEngine(engine,uri);
registerTypes(uri);
}
void FluentUI::registerTypes(const char *uri){
#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 0))
Q_INIT_RESOURCE(fluentui);
#endif
qmlRegisterType<WindowHelper>(uri,major,minor,"WindowHelper");
qmlRegisterType<QRCode>(uri,major,minor,"QRCode");
qmlRegisterType<FluCaptcha>(uri,major,minor,"FluCaptcha");
qmlRegisterType<FluWatermark>(uri,major,minor,"FluWatermark");
qmlRegisterType<ScreenshotBackground>(uri,major,minor,"ScreenshotBackground");
qmlRegisterType<Screenshot>(uri,major,minor,"Screenshot");
qmlRegisterType<FluColorSet>(uri,major,minor,"FluColorSet");
qmlRegisterType<FluHttpInterceptor>(uri,major,minor,"FluHttpInterceptor");
qmlRegisterType<FluHttp>(uri,major,minor,"FluHttp");
qmlRegisterType<HttpCallable>(uri,major,minor,"HttpCallable");
qmlRegisterType<HttpRequest>(uri,major,minor,"HttpRequest");
qmlRegisterType<FluEvent>(uri,major,minor,"FluEvent");
qmlRegisterType<FluViewModel>(uri,major,minor,"FluViewModel");
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/ColorSlider.qml"),uri,major,minor,"ColorSlider");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/Content/NumberBox.qml"),uri,major,minor,"NumberBox");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/Content/PanelBorder.qml"),uri,major,minor,"PanelBorder");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/ColorPicker/Content/SBPicker.qml"),uri,major,minor,"SBPicker");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluAcrylic.qml"),uri,major,minor,"FluAcrylic");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluAppBar.qml"),uri,major,minor,"FluAppBar");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluArea.qml"),uri,major,minor,"FluArea");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluAutoSuggestBox.qml"),uri,major,minor,"FluAutoSuggestBox");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluBadge.qml"),uri,major,minor,"FluBadge");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluBreadcrumbBar.qml"),uri,major,minor,"FluBreadcrumbBar");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluButton.qml"),uri,major,minor,"FluButton");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluCalendarPicker.qml"),uri,major,minor,"FluCalendarPicker");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluCalendarView.qml"),uri,major,minor,"FluCalendarView");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluCarousel.qml"),uri,major,minor,"FluCarousel");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluChart.qml"),uri,major,minor,"FluChart");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluCheckBox.qml"),uri,major,minor,"FluCheckBox");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluColorPicker.qml"),uri,major,minor,"FluColorPicker");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluColorView.qml"),uri,major,minor,"FluColorView");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluComboBox.qml"),uri,major,minor,"FluComboBox");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluContentDialog.qml"),uri,major,minor,"FluContentDialog");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluContentPage.qml"),uri,major,minor,"FluContentPage");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluControl.qml"),uri,major,minor,"FluControl");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluCopyableText.qml"),uri,major,minor,"FluCopyableText");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluDatePicker.qml"),uri,major,minor,"FluDatePicker");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluDivider.qml"),uri,major,minor,"FluDivider");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluDropDownButton.qml"),uri,major,minor,"FluDropDownButton");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluExpander.qml"),uri,major,minor,"FluExpander");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluFilledButton.qml"),uri,major,minor,"FluFilledButton");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluFlipView.qml"),uri,major,minor,"FluFlipView");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluFocusRectangle.qml"),uri,major,minor,"FluFocusRectangle");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluIcon.qml"),uri,major,minor,"FluIcon");
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/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/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/FluMenuBarItem.qml"),uri,major,minor,"FluMenuBarItem");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluMenuItem.qml"),uri,major,minor,"FluMenuItem");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluMenuSeparator.qml"),uri,major,minor,"FluMenuSeparator");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluMultilineTextBox.qml"),uri,major,minor,"FluMultilineTextBox");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluNavigationView.qml"),uri,major,minor,"FluNavigationView");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluObject.qml"),uri,major,minor,"FluObject");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPage.qml"),uri,major,minor,"FluPage");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPagination.qml"),uri,major,minor,"FluPagination");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPaneItem.qml"),uri,major,minor,"FluPaneItem");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPaneItemEmpty.qml"),uri,major,minor,"FluPaneItemEmpty");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPaneItemExpander.qml"),uri,major,minor,"FluPaneItemExpander");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPaneItemHeader.qml"),uri,major,minor,"FluPaneItemHeader");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPaneItemSeparator.qml"),uri,major,minor,"FluPaneItemSeparator");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPasswordBox.qml"),uri,major,minor,"FluPasswordBox");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPivot.qml"),uri,major,minor,"FluPivot");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPivotItem.qml"),uri,major,minor,"FluPivotItem");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluPopup.qml"),uri,major,minor,"FluPopup");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluProgressBar.qml"),uri,major,minor,"FluProgressBar");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluProgressRing.qml"),uri,major,minor,"FluProgressRing");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluQRCode.qml"),uri,major,minor,"FluQRCode");
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/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/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/FluScrollIndicator.qml"),uri,major,minor,"FluScrollIndicator");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluScrollablePage.qml"),uri,major,minor,"FluScrollablePage");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluShadow.qml"),uri,major,minor,"FluShadow");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluSlider.qml"),uri,major,minor,"FluSlider");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluSpinBox.qml"),uri,major,minor,"FluSpinBox");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluStatusView.qml"),uri,major,minor,"FluStatusView");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTabView.qml"),uri,major,minor,"FluTabView");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTableModelColumn.qml"),uri,major,minor,"FluTableModelColumn");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTableView.qml"),uri,major,minor,"FluTableView");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluText.qml"),uri,major,minor,"FluText");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTextBox.qml"),uri,major,minor,"FluTextBox");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTextBoxBackground.qml"),uri,major,minor,"FluTextBoxBackground");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTextBoxMenu.qml"),uri,major,minor,"FluTextBoxMenu");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTextButton.qml"),uri,major,minor,"FluTextButton");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTimePicker.qml"),uri,major,minor,"FluTimePicker");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTimeline.qml"),uri,major,minor,"FluTimeline");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluToggleButton.qml"),uri,major,minor,"FluToggleButton");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluToggleSwitch.qml"),uri,major,minor,"FluToggleSwitch");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTooltip.qml"),uri,major,minor,"FluTooltip");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTour.qml"),uri,major,minor,"FluTour");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluTreeView.qml"),uri,major,minor,"FluTreeView");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluWindow.qml"),uri,major,minor,"FluWindow");
qmlRegisterType(QUrl("qrc:/qt/qml/FluentUI/Controls/FluRangeSlider.qml"),uri,major,minor,"FluRangeSlider");
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/FluLoadingButton.qml"),uri,major,minor,"FluLoadingButton");
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(FluThemeType::staticMetaObject, uri,major,minor,"FluThemeType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluPageType::staticMetaObject, uri,major,minor,"FluPageType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluWindowType::staticMetaObject, uri,major,minor,"FluWindowType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluTreeViewType::staticMetaObject, uri,major,minor,"FluTreeViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluStatusViewType::staticMetaObject, uri,major,minor,"FluStatusViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluContentDialogType::staticMetaObject, uri,major,minor,"FluContentDialogType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluTimePickerType::staticMetaObject, uri,major,minor,"FluTimePickerType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluCalendarViewType::staticMetaObject, uri,major,minor,"FluCalendarViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluTabViewType::staticMetaObject, uri,major,minor,"FluTabViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluNavigationViewType::staticMetaObject, uri,major,minor,"FluNavigationViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluTimelineType::staticMetaObject, uri,major,minor,"FluTimelineType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluScreenshotType::staticMetaObject, uri,major,minor,"FluScreenshotType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluViewModelType::staticMetaObject, uri,major,minor,"FluViewModelType", "Access to enums & flags only");
qmlRegisterModule(uri,major,minor);
}
void FluentUI::initializeEngine(QQmlEngine *engine, const char *uri){
#ifdef Q_OS_WIN
QFont font;
font.setFamily("Microsoft YaHei");
QGuiApplication::setFont(font);
#endif
FluApp* app = FluApp::getInstance();
engine->rootContext()->setContextProperty("FluApp",app);
FluColors* colors = FluColors::getInstance();
engine->rootContext()->setContextProperty("FluColors",colors);
FluTheme* theme = FluTheme::getInstance();
engine->rootContext()->setContextProperty("FluTheme",theme);
FluTools* tools = FluTools::getInstance();
engine->rootContext()->setContextProperty("FluTools",tools);
FluTextStyle* textStyle = FluTextStyle::getInstance();
engine->rootContext()->setContextProperty("FluTextStyle",textStyle);
FluEventBus* eventBus = FluEventBus::getInstance();
engine->rootContext()->setContextProperty("FluEventBus",eventBus);
engine->addImportPath("qrc:/qt/qml");
}

19
src/FluentUI.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef FLUENTUI_H
#define FLUENTUI_H
#include <QObject>
#include <QQmlEngine>
class FluentUI : public QObject
{
Q_OBJECT
public:
static FluentUI *getInstance();
Q_DECL_EXPORT void registerTypes(QQmlEngine *engine);
void registerTypes(const char *uri);
void initializeEngine(QQmlEngine *engine, const char *uri);
private:
static FluentUI* m_instance;
};
#endif // FLUENTUI_H

View File

@ -23,7 +23,6 @@ Rectangle {
radius: 4 radius: 4
border.width: 1 border.width: 1
border.color: dividerColor border.color: dividerColor
Component.onCompleted: { Component.onCompleted: {
if(current){ if(current){
const date = current const date = current
@ -33,7 +32,6 @@ Rectangle {
text_date.text = year+"-"+(month+1)+"-"+day text_date.text = year+"-"+(month+1)+"-"+day
} }
} }
MouseArea{ MouseArea{
id:mouse_area id:mouse_area
hoverEnabled: true hoverEnabled: true

View File

@ -7,6 +7,7 @@ Item {
property var date: new Date() property var date: new Date()
property var currentDate : new Date() property var currentDate : new Date()
property var toDay: new Date() property var toDay: new Date()
property int radius: 5
signal dateClicked(var date) signal dateClicked(var date)
id:control id:control
width: 280 width: 280
@ -214,10 +215,7 @@ Item {
} }
FluArea{ FluArea{
anchors.fill: parent anchors.fill: parent
radius: 5 radius: control.radius
FluShadow{
radius: 5
}
Rectangle{ Rectangle{
id:layout_divider id:layout_divider
height: 1 height: 1

View File

@ -10,7 +10,7 @@ Canvas {
property var chartOptions property var chartOptions
property double chartAnimationProgress: 0.1 property double chartAnimationProgress: 0.1
property int animationEasingType: Easing.InOutExpo property int animationEasingType: Easing.InOutExpo
property double animationDuration: 500 property double animationDuration: 0
property var memorizedContext property var memorizedContext
property var memorizedData property var memorizedData
property var memorizedOptions property var memorizedOptions
@ -89,7 +89,7 @@ Canvas {
control.requestPaint(); control.requestPaint();
} }
onPaint: { onPaint: {
if(control.getContext('2d') !== null && memorizedContext !== control.getContext('2d') || memorizedData !== control.chartData || memorizedOptions !== control.chartOptions) { if(control.getContext('2d') !== null && memorizedContext !== control.getContext('2d') || memorizedData !== control.chartData || memorizedOptions !== control.chartOptions) {
var ctx = control.getContext('2d'); var ctx = control.getContext('2d');
jsChart = Chart.build(ctx, { jsChart = Chart.build(ctx, {

View File

@ -4,15 +4,14 @@ import FluentUI 1.0
import "ColorPicker" import "ColorPicker"
Item { Item {
id:control
property alias colorValue: color_picker.colorValue property alias colorValue: color_picker.colorValue
property int radius: 5
width: color_picker.width+10 width: color_picker.width+10
height: color_picker.height height: color_picker.height
FluArea{ FluArea{
anchors.fill: parent anchors.fill: parent
radius: 5 radius: control.radius
FluShadow{
radius: 5
}
ColorPicker{ ColorPicker{
id:color_picker id:color_picker
} }

View File

@ -0,0 +1,40 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
FluButton {
property bool loading: false
id: control
disabled: loading
contentItem: Row{
spacing: 6
FluText {
text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font: control.font
color: control.textColor
anchors.verticalCenter: parent.verticalCenter
}
Item{
width: control.loading ? 16 : 0
height: 16
anchors.verticalCenter: parent.verticalCenter
visible: width!==0
clip: true
Behavior on width {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
FluProgressRing{
width: 16
height: 16
strokeWidth:3
anchors.centerIn: parent
}
}
}
}

View File

@ -124,7 +124,6 @@ Item {
Component{ Component{
id:com_panel_item_header id:com_panel_item_header
Item{ Item{
clip: true
height: { height: {
if(model.parent){ if(model.parent){
return model.parent.isExpand ? 30 : 0 return model.parent.isExpand ? 30 : 0
@ -154,7 +153,6 @@ Item {
Item{ Item{
height: 38 height: 38
width: layout_list.width width: layout_list.width
clip: true
FluControl{ FluControl{
id:item_control id:item_control
anchors{ anchors{
@ -378,7 +376,6 @@ Item {
duration: 83 duration: 83
} }
} }
clip: true
height: { height: {
if(model.parent){ if(model.parent){
return model.parent.isExpand ? 38 : 0 return model.parent.isExpand ? 38 : 0
@ -409,6 +406,31 @@ Item {
Drag.hotSpot.x: item_control.width / 2 Drag.hotSpot.x: item_control.width / 2
Drag.hotSpot.y: item_control.height / 2 Drag.hotSpot.y: item_control.height / 2
Drag.dragType: Drag.Automatic Drag.dragType: Drag.Automatic
onClicked:{
if(type === 0){
if(model.onTapListener){
model.onTapListener()
}else{
nav_list.currentIndex = _idx
layout_footer.currentIndex = -1
model.tap()
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}else{
if(model.onTapListener){
model.onTapListener()
}else{
nav_list.currentIndex = nav_list.count-layout_footer.count+_idx
layout_footer.currentIndex = _idx
model.tap()
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}
}
MouseArea{ MouseArea{
id:item_mouse id:item_mouse
anchors.fill: parent anchors.fill: parent
@ -433,29 +455,7 @@ Item {
loader_item_menu.item.popup(); loader_item_menu.item.popup();
} }
}else{ }else{
if(type === 0){ item_control.clicked()
if(model.onTapListener){
model.onTapListener()
}else{
nav_list.currentIndex = _idx
layout_footer.currentIndex = -1
model.tap()
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}else{
if(model.onTapListener){
model.onTapListener()
}else{
nav_list.currentIndex = nav_list.count-layout_footer.count+_idx
layout_footer.currentIndex = _idx
model.tap()
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}
} }
} }
} }
@ -752,7 +752,12 @@ Item {
id:nav_stack2 id:nav_stack2
anchors.fill: nav_stack anchors.fill: nav_stack
clip: true clip: true
visible: FluPageType.SingleInstance === nav_stack.currentItem.launchMode visible: {
if(!nav_stack.currentItem){
return false
}
return FluPageType.SingleInstance === nav_stack.currentItem.launchMode
}
} }
function navStack(){ function navStack(){
return nav_stack return nav_stack

View File

@ -2,30 +2,32 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import FluentUI 1.0 import FluentUI 1.0
Item { Page {
default property alias content: d.children default property alias content: d.children
property alias currentIndex: nav_list.currentIndex property alias currentIndex: nav_list.currentIndex
property color normalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120 property color textNormalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120
property color hoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black property color textHoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black
property int textSize: 28
property bool textBold: true
property int textSpacing: 10
property int headerSpacing: 20
property int headerHeight: 40
id:control id:control
width: 400 width: 400
height: 300 height: 300
implicitHeight: height implicitHeight: height
implicitWidth: width implicitWidth: width
MouseArea{
anchors.fill: parent
preventStealing: true
}
FluObject{ FluObject{
id:d id:d
property int tabY: control.headerHeight/2+control.textSize/2 + 3
} }
ListView{ background:Item{}
header:ListView{
id:nav_list id:nav_list
height: 40 implicitHeight: control.headerHeight
width: control.width implicitWidth: control.width
model:d.children model:d.children
clip: true spacing: control.headerSpacing
spacing: 20
interactive: false interactive: false
orientation: ListView.Horizontal orientation: ListView.Horizontal
highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0 highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0
@ -36,7 +38,7 @@ Item {
radius: 1.5 radius: 1.5
color: FluTheme.primaryColor.dark color: FluTheme.primaryColor.dark
width: nav_list.currentItem ? nav_list.currentItem.width : 0 width: nav_list.currentItem ? nav_list.currentItem.width : 0
y:37 y:d.tabY
Behavior on width { Behavior on width {
enabled: FluTheme.enableAnimation enabled: FluTheme.enableAnimation
NumberAnimation{ NumberAnimation{
@ -50,18 +52,25 @@ Item {
id:item_button id:item_button
width: item_title.width width: item_title.width
height: nav_list.height height: nav_list.height
focusPolicy:Qt.TabFocus
background:Item{ background:Item{
FluFocusRectangle{
anchors.margins: -4
visible: item_button.activeFocus
radius:4
}
} }
contentItem: Item{ contentItem: Item{
FluText { FluText {
id:item_title id:item_title
font: FluTextStyle.Title
text: modelData.title text: modelData.title
anchors.centerIn: parent anchors.centerIn: parent
font.pixelSize: control.textSize
font.bold: control.textBold
color: { color: {
if(item_button.hovered) if(item_button.hovered)
return hoverColor return textHoverColor
return normalColor return textNormalColor
} }
} }
} }
@ -72,13 +81,7 @@ Item {
} }
Item{ Item{
id:container id:container
anchors{ anchors.fill: parent
top: nav_list.bottom
topMargin: 10
left: parent.left
right: parent.right
bottom: parent.bottom
}
Repeater{ Repeater{
model:d.children model:d.children
Loader{ Loader{

View File

@ -0,0 +1,134 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import FluentUI 1.0
Button {
property real progress
property bool disabled: false
property string contentDescription: ""
QtObject{
id:d
property bool checked: rect_back.height == background.height
}
property color normalColor: {
if(d.checked){
return FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
}else{
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(254/255,254/255,254/255,1)
}
}
property color hoverColor: {
if(d.checked){
return FluTheme.dark ? Qt.darker(normalColor,1.1) : Qt.lighter(normalColor,1.1)
}else{
return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
}
}
property color disableColor: {
if(d.checked){
return FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
}else{
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
}
}
property color pressedColor: FluTheme.dark ? Qt.darker(normalColor,1.2) : Qt.lighter(normalColor,1.2)
Accessible.role: Accessible.Button
Accessible.name: control.text
Accessible.description: contentDescription
Accessible.onPressAction: control.clicked()
focusPolicy:Qt.TabFocus
id: control
enabled: !disabled
horizontalPadding:12
background: FluItem{
implicitWidth: 28
implicitHeight: 28
radius: [4,4,4,4]
Rectangle{
anchors.fill: parent
border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
border.width: d.checked ? 0 : 1
radius: 4
color:{
if(!enabled){
return disableColor
}
if(d.checked){
if(pressed){
return pressedColor
}
}
return hovered ? hoverColor :normalColor
}
}
Rectangle{
id:rect_back
width: parent.width * control.progress
height: control.progress === 1 ? background.height : 3
visible: !d.checked
color: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
anchors.bottom: parent.bottom
Behavior on height{
enabled: control.progress !== 1
SequentialAnimation {
PauseAnimation {
duration: FluTheme.enableAnimation ? 167 : 0
}
NumberAnimation{
duration: FluTheme.enableAnimation ? 167 : 0
from: 3
to: background.height
}
}
}
Behavior on width{
NumberAnimation{
duration: 167
}
}
}
FluFocusRectangle{
visible: control.activeFocus
radius:4
}
}
contentItem: FluText {
text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: {
if(d.checked){
if(FluTheme.dark){
if(!enabled){
return Qt.rgba(173/255,173/255,173/255,1)
}
return Qt.rgba(0,0,0,1)
}else{
return Qt.rgba(1,1,1,1)
}
}else{
if(FluTheme.dark){
if(!enabled){
return Qt.rgba(131/255,131/255,131/255,1)
}
if(!d.checked){
if(pressed){
return Qt.rgba(162/255,162/255,162/255,1)
}
}
return Qt.rgba(1,1,1,1)
}else{
if(!enabled){
return Qt.rgba(160/255,160/255,160/255,1)
}
if(!d.checked){
if(pressed){
return Qt.rgba(96/255,96/255,96/255,1)
}
}
return Qt.rgba(0,0,0,1)
}
}
}
}
}

View File

@ -26,7 +26,7 @@ Item{
Component{ Component{
id:com_screen id:com_screen
Window{ Window{
property bool isZeroPos: screenshot.start == Qt.point(0,0) && screenshot.end == Qt.point(0,0) property bool isZeroPos: screenshot.start.x === 0 && screenshot.start.y === 0 && screenshot.end.x === 0 && screenshot.end.y === 0
id:window_screen id:window_screen
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
x:-1 x:-1
@ -41,7 +41,7 @@ Item{
} }
} }
Component.onCompleted: { Component.onCompleted: {
setGeometry(0,0,screenshot_background.width,screenshot_background.height) setGeometry(0,0,screenshot_background.width,screenshot_background.height+1)
} }
ScreenshotBackground{ ScreenshotBackground{
id:screenshot_background id:screenshot_background
@ -123,7 +123,7 @@ Item{
MouseArea{ MouseArea{
property point clickPos: Qt.point(0,0) property point clickPos: Qt.point(0,0)
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.SizeAllCursor cursorShape: d.isEdit ? Qt.ArrowCursor : Qt.SizeAllCursor
onPressed: onPressed:
(mouse)=>{ (mouse)=>{
clickPos = Qt.point(mouse.x, mouse.y) clickPos = Qt.point(mouse.x, mouse.y)
@ -477,7 +477,7 @@ Item{
width: 100 width: 100
height: 40 height: 40
visible: { visible: {
if(screenshot.start === Qt.point(0,0) && screenshot.end === Qt.point(0,0)){ if(isZeroPos){
return false return false
} }
if(d.enablePosition){ if(d.enablePosition){

View File

@ -13,18 +13,115 @@ T.ScrollBar {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding) implicitContentHeight + topPadding + bottomPadding)
padding: 2
visible: control.policy !== T.ScrollBar.AlwaysOff visible: control.policy !== T.ScrollBar.AlwaysOff
minimumSize: Math.max(orientation === Qt.Horizontal ? height / width : width / height,0.3) minimumSize: Math.max(orientation === Qt.Horizontal ? height / width : width / height,0.3)
QtObject{
id:d
property int minLine : 2
property int maxLine : 6
}
verticalPadding : vertical ? 15 : 3
horizontalPadding : horizontal ? 15 : 3
background: Rectangle{
id:back_rect
radius: 5
color:FluTheme.dark ? Qt.rgba(44/255,44/255,44/255,1) : Qt.rgba(255/255,255/255,255/255,1)
opacity:{
if(vertical){
return d.maxLine === Number(rect_bar.width)
}
return d.maxLine === Number(rect_bar.height)
}
Behavior on opacity {
NumberAnimation{
duration: 50
}
}
}
FluIconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
visible: control.horizontal
opacity: back_rect.opacity
anchors{
left: parent.left
leftMargin: 2
verticalCenter: parent.verticalCenter
}
iconColor: control.color
iconSource: FluentIcons.CaretLeftSolid8
onClicked: {
control.decrease()
}
}
FluIconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
iconColor: control.color
opacity: back_rect.opacity
anchors{
right: parent.right
rightMargin: 2
verticalCenter: parent.verticalCenter
}
visible: control.horizontal
iconSource: FluentIcons.CaretRightSolid8
onClicked: {
control.increase()
}
}
FluIconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
iconColor: control.color
opacity: back_rect.opacity
anchors{
top: parent.top
topMargin: 2
horizontalCenter: parent.horizontalCenter
}
visible: control.vertical
iconSource: FluentIcons.CaretUpSolid8
onClicked: {
control.decrease()
}
}
FluIconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
iconColor: control.color
opacity: back_rect.opacity
anchors{
bottom: parent.bottom
bottomMargin: 2
horizontalCenter: parent.horizontalCenter
}
visible: control.vertical
iconSource: FluentIcons.CaretDownSolid8
onClicked: {
control.increase()
}
}
contentItem: Item { contentItem: Item {
property bool collapsed: (control.policy === T.ScrollBar.AlwaysOn || (control.active && control.size < 1.0)) property bool collapsed: (control.policy === T.ScrollBar.AlwaysOn || (control.active && control.size < 1.0))
implicitWidth: control.interactive ? 6 : 2 implicitWidth: control.interactive ? d.maxLine : d.minLine
implicitHeight: control.interactive ? 6 : 2 implicitHeight: control.interactive ? d.maxLine : d.minLine
Rectangle{ Rectangle{
id:rect_bar id:rect_bar
width: vertical ? 2 : parent.width width: vertical ? d.minLine : parent.width
height: horizontal ? 2 : parent.height height: horizontal ? d.minLine : parent.height
color:{ color:{
if(control.pressed){ if(control.pressed){
return control.pressedColor return control.pressedColor
@ -44,8 +141,8 @@ T.ScrollBar {
when: contentItem.collapsed when: contentItem.collapsed
PropertyChanges { PropertyChanges {
target: rect_bar target: rect_bar
width: vertical ? 6 : parent.width width: vertical ? d.maxLine : parent.width
height: horizontal ? 6 : parent.height height: horizontal ? d.maxLine : parent.height
} }
} }
,State{ ,State{
@ -53,8 +150,8 @@ T.ScrollBar {
when: !contentItem.collapsed when: !contentItem.collapsed
PropertyChanges { PropertyChanges {
target: rect_bar target: rect_bar
width: vertical ? 2 : parent.width width: vertical ? d.minLine : parent.width
height: horizontal ? 2 : parent.height height: horizontal ? d.minLine : parent.height
} }
} }
] ]
@ -73,6 +170,8 @@ T.ScrollBar {
} }
,Transition { ,Transition {
to: "show" to: "show"
SequentialAnimation {
PauseAnimation { duration: 450 }
NumberAnimation { NumberAnimation {
target: rect_bar target: rect_bar
properties: vertical ? "width" : "height" properties: vertical ? "width" : "height"
@ -80,6 +179,7 @@ T.ScrollBar {
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
}
] ]
} }
} }

View File

@ -3,64 +3,22 @@ import QtQuick.Controls 2.15
import FluentUI 1.0 import FluentUI 1.0
Item { Item {
//高性能阴影比DropShadow阴影性能高出数倍
property color color: FluTheme.dark ? "#FFFFFF" : "#999999" property color color: FluTheme.dark ? "#FFFFFF" : "#999999"
property int elevation: 6
property int radius: 4 property int radius: 4
id:control id:control
anchors.fill: parent anchors.fill: parent
anchors.margins: -4 Repeater{
model: elevation
Rectangle{ Rectangle{
width: control.width anchors.fill: parent
height: control.height
anchors.centerIn: parent
color: "#00000000" color: "#00000000"
opacity: 0.02 opacity: 0.01 * (elevation-index+1)
border.width: 1 anchors.margins: -index
radius: control.radius radius: control.radius+index
border.width: index
border.color: control.color border.color: control.color
} }
Rectangle{
width: control.width - 2
height: control.height - 2
anchors.centerIn: parent
color: "#00000000"
opacity: 0.04
border.width: 1
radius: control.radius
border.color: control.color
} }
Rectangle{
width: control.width - 4
height: control.height - 4
anchors.centerIn: parent
color: "#00000000"
opacity: 0.06
border.width: 1
radius: control.radius
border.color: control.color
}
Rectangle{
width: control.width - 6
height: control.height - 6
anchors.centerIn: parent
color: "#00000000"
opacity: 0.08
border.width: 1
radius: control.radius
border.color: control.color
}
Rectangle{
width: control.width - 8
height: control.height - 8
anchors.centerIn: parent
opacity: 0.1
radius: control.radius
color: "#00000000"
border.width: 1
border.color: control.color
}
} }

View File

@ -67,6 +67,7 @@ Rectangle {
Component{ Component{
id:com_edit id:com_edit
FluTextBox{ FluTextBox{
id:text_box
text: display text: display
readOnly: true === columnSource[column].readOnly readOnly: true === columnSource[column].readOnly
Component.onCompleted: { Component.onCompleted: {
@ -75,7 +76,7 @@ Rectangle {
} }
onCommit: { onCommit: {
if(!readOnly){ if(!readOnly){
display = text display = text_box.text
} }
tableView.closeEditor() tableView.closeEditor()
} }

View File

@ -31,7 +31,7 @@ FluMenu{
text:cutText text:cutText
focus: false focus: false
padding: 0 padding: 0
height: visible ? 34 : 0 height: visible ? 36 : 0
visible: inputItem.selectedText !== "" && !inputItem.readOnly visible: inputItem.selectedText !== "" && !inputItem.readOnly
onClicked: { onClicked: {
inputItem.cut() inputItem.cut()
@ -43,7 +43,7 @@ FluMenu{
text:copyText text:copyText
focus: false focus: false
padding: 0 padding: 0
height: visible ? 34 : 0 height: visible ? 36 : 0
visible: inputItem.selectedText !== "" visible: inputItem.selectedText !== ""
onClicked: { onClicked: {
inputItem.copy() inputItem.copy()
@ -55,8 +55,8 @@ FluMenu{
text:pasteText text:pasteText
focus: false focus: false
padding: 0 padding: 0
height: visible ? 34 : 0 visible: !inputItem.readOnly
visible: inputItem.canPaste height: visible ? 36 : 0
onClicked: { onClicked: {
inputItem.paste() inputItem.paste()
menu.close() menu.close()
@ -67,7 +67,7 @@ FluMenu{
text:selectAllText text:selectAllText
focus: false focus: false
padding: 0 padding: 0
height: visible ? 34 : 0 height: visible ? 36 : 0
visible: inputItem.text !== "" visible: inputItem.text !== ""
onClicked: { onClicked: {
inputItem.selectAll() inputItem.selectAll()

View File

@ -52,7 +52,7 @@ Button {
border.width: checked ? 0 : 1 border.width: checked ? 0 : 1
FluFocusRectangle{ FluFocusRectangle{
visible: control.activeFocus visible: control.activeFocus
radius:8 radius:4
} }
color:{ color:{
if(!enabled){ if(!enabled){

View File

@ -27,28 +27,31 @@ Button {
Accessible.name: control.text Accessible.name: control.text
Accessible.description: contentDescription Accessible.description: contentDescription
Accessible.onPressAction: control.clicked() Accessible.onPressAction: control.clicked()
height: 20
enabled: !disabled enabled: !disabled
implicitHeight: height
focusPolicy:Qt.TabFocus focusPolicy:Qt.TabFocus
onClicked: clickListener() onClicked: clickListener()
padding: 0
horizontalPadding: 0
onCheckableChanged: { onCheckableChanged: {
if(checkable){ if(checkable){
checkable = false checkable = false
} }
} }
contentItem: Item{} background : Item{
background : RowLayout{ implicitHeight: 20
implicitWidth: 40
}
contentItem: RowLayout{
spacing: control.textSpacing spacing: control.textSpacing
layoutDirection:control.textRight ? Qt.LeftToRight : Qt.RightToLeft layoutDirection:control.textRight ? Qt.LeftToRight : Qt.RightToLeft
Rectangle { Rectangle {
id:control_backgound id:control_backgound
width: 40 width: background.width
height: control.height height: background.height
radius: height / 2 radius: height / 2
FluFocusRectangle{ FluFocusRectangle{
visible: control.activeFocus visible: control.activeFocus
radius: 20 radius: parent.radius
} }
color: { color: {
if(!enabled){ if(!enabled){
@ -73,10 +76,10 @@ Button {
return borderNormalColor return borderNormalColor
} }
Rectangle { Rectangle {
width: 20 width: parent.height
x:checked ? control_backgound.width-width : 0 x:checked ? control_backgound.width-width : 0
height: 20 height: width
radius: 10 radius: width/2
scale: hovered&enabled ? 7/10 : 6/10 scale: hovered&enabled ? 7/10 : 6/10
color: { color: {
if(!enabled){ if(!enabled){

View File

@ -96,24 +96,22 @@ Popup{
ctx.globalCompositeOperation = 'destination-out' ctx.globalCompositeOperation = 'destination-out'
ctx.fillStyle = 'black' ctx.fillStyle = 'black'
var rect = Qt.rect(d.pos.x-control.targetMargins,d.pos.y-control.targetMargins, d.target.width+control.targetMargins*2, d.target.height+control.targetMargins*2) var rect = Qt.rect(d.pos.x-control.targetMargins,d.pos.y-control.targetMargins, d.target.width+control.targetMargins*2, d.target.height+control.targetMargins*2)
ctx.fillRect(rect.x,rect.y,rect.width,rect.height) drawRoundedRect(rect,2,ctx)
ctx.restore() ctx.restore()
} }
//Todo
function drawRoundedRect(rect, r, ctx) { function drawRoundedRect(rect, r, ctx) {
var ptA = Qt.point(rect.x + r, rect.y) ctx.beginPath();
var ptB = Qt.point(rect.x + rect.width, rect.y) ctx.moveTo(rect.x + r, rect.y);
var ptC = Qt.point(rect.x + rect.width, rect.y + rect.height) ctx.lineTo(rect.x + rect.width - r, rect.y);
var ptD = Qt.point(rect.x, rect.y + rect.height) ctx.arcTo(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + r, r);
var ptE = Qt.point(rect.x, rect.y) ctx.lineTo(rect.x + rect.width, rect.y + rect.height - r);
ctx.beginPath() ctx.arcTo(rect.x + rect.width, rect.y + rect.height, rect.x + rect.width - r, rect.y + rect.height, r);
ctx.moveTo(ptA.x, ptA.y) ctx.lineTo(rect.x + r, rect.y + rect.height);
ctx.arcTo(ptB.x, ptB.y, ptC.x, ptC.y, r) ctx.arcTo(rect.x, rect.y + rect.height, rect.x, rect.y + rect.height - r, r);
ctx.arcTo(ptC.x, ptC.y, ptD.x, ptD.y, r) ctx.lineTo(rect.x, rect.y + r);
ctx.arcTo(ptD.x, ptD.y, ptE.x, ptE.y, r) ctx.arcTo(rect.x, rect.y, rect.x + r, rect.y, r);
ctx.arcTo(ptE.x, ptE.y, ptA.x, ptA.y, r) ctx.closePath();
ctx.fill() ctx.fill()
ctx.closePath()
} }
} }
FluArea{ FluArea{

View File

@ -69,6 +69,8 @@ Window {
id:popup_loading id:popup_loading
modal:true modal:true
focus: true focus: true
width: window.width
height: window.height
anchors.centerIn: Overlay.overlay anchors.centerIn: Overlay.overlay
closePolicy: { closePolicy: {
if(cancel){ if(cancel){
@ -76,17 +78,38 @@ Window {
} }
return Popup.NoAutoClose return Popup.NoAutoClose
} }
Overlay.modal: Rectangle { Overlay.modal: Item {}
color: "#44000000"
}
onVisibleChanged: { onVisibleChanged: {
if(!visible){ if(!visible){
loader_loading.sourceComponent = undefined loader_loading.sourceComponent = undefined
} }
} }
padding: 0
opacity: 0
visible:true visible:true
background: Item{} Behavior on opacity {
SequentialAnimation {
PauseAnimation {
duration: 88
}
NumberAnimation{
duration: 167
}
}
}
Component.onCompleted: {
opacity = 1
}
background: Rectangle{
color:"#44000000"
}
contentItem: Item{ contentItem: Item{
MouseArea{
anchors.fill: parent
onClicked: {
popup_loading.visible = false
}
}
ColumnLayout{ ColumnLayout{
spacing: 8 spacing: 8
anchors.centerIn: parent anchors.centerIn: parent

File diff suppressed because it is too large Load Diff

View File

@ -94,4 +94,6 @@ FluTreeView 1.0 Controls/FluTreeView.qml
FluWindow 1.0 Controls/FluWindow.qml FluWindow 1.0 Controls/FluWindow.qml
FluRangeSlider 1.0 Controls/FluRangeSlider.qml FluRangeSlider 1.0 Controls/FluRangeSlider.qml
FluStaggeredView 1.0 Controls/FluStaggeredView.qml FluStaggeredView 1.0 Controls/FluStaggeredView.qml
FluProgressButton 1.0 Controls/FluProgressButton.qml
FluLoadingButton 1.0 Controls/FluLoadingButton.qml
plugin fluentuiplugin plugin fluentuiplugin

View File

@ -0,0 +1,100 @@
<RCC>
<qresource prefix="/qt/qml">
<file>FluentUI/JS/Chart.js</file>
<file>FluentUI/Image/noise.png</file>
<file>FluentUI/Font/Segoe_Fluent_Icons.ttf</file>
<file>FluentUI/Controls/FluAcrylic.qml</file>
<file>FluentUI/Controls/FluAppBar.qml</file>
<file>FluentUI/Controls/FluArea.qml</file>
<file>FluentUI/Controls/FluAutoSuggestBox.qml</file>
<file>FluentUI/Controls/FluBadge.qml</file>
<file>FluentUI/Controls/FluBreadcrumbBar.qml</file>
<file>FluentUI/Controls/FluButton.qml</file>
<file>FluentUI/Controls/FluCalendarPicker.qml</file>
<file>FluentUI/Controls/FluCalendarView.qml</file>
<file>FluentUI/Controls/FluCarousel.qml</file>
<file>FluentUI/Controls/FluChart.qml</file>
<file>FluentUI/Controls/FluCheckBox.qml</file>
<file>FluentUI/Controls/FluColorPicker.qml</file>
<file>FluentUI/Controls/FluColorView.qml</file>
<file>FluentUI/Controls/FluComboBox.qml</file>
<file>FluentUI/Controls/FluContentDialog.qml</file>
<file>FluentUI/Controls/FluContentPage.qml</file>
<file>FluentUI/Controls/FluControl.qml</file>
<file>FluentUI/Controls/FluCopyableText.qml</file>
<file>FluentUI/Controls/FluDatePicker.qml</file>
<file>FluentUI/Controls/FluDivider.qml</file>
<file>FluentUI/Controls/FluDropDownButton.qml</file>
<file>FluentUI/Controls/FluExpander.qml</file>
<file>FluentUI/Controls/FluFilledButton.qml</file>
<file>FluentUI/Controls/FluFlipView.qml</file>
<file>FluentUI/Controls/FluFocusRectangle.qml</file>
<file>FluentUI/Controls/FluIcon.qml</file>
<file>FluentUI/Controls/FluIconButton.qml</file>
<file>FluentUI/Controls/FluImage.qml</file>
<file>FluentUI/Controls/FluInfoBar.qml</file>
<file>FluentUI/Controls/FluItem.qml</file>
<file>FluentUI/Controls/FluItemDelegate.qml</file>
<file>FluentUI/Controls/FluMenu.qml</file>
<file>FluentUI/Controls/FluMenuBar.qml</file>
<file>FluentUI/Controls/FluMenuBarItem.qml</file>
<file>FluentUI/Controls/FluMenuItem.qml</file>
<file>FluentUI/Controls/FluMenuSeparator.qml</file>
<file>FluentUI/Controls/FluMultilineTextBox.qml</file>
<file>FluentUI/Controls/FluNavigationView.qml</file>
<file>FluentUI/Controls/FluObject.qml</file>
<file>FluentUI/Controls/FluPage.qml</file>
<file>FluentUI/Controls/FluPagination.qml</file>
<file>FluentUI/Controls/FluPaneItem.qml</file>
<file>FluentUI/Controls/FluPaneItemEmpty.qml</file>
<file>FluentUI/Controls/FluPaneItemExpander.qml</file>
<file>FluentUI/Controls/FluPaneItemHeader.qml</file>
<file>FluentUI/Controls/FluPaneItemSeparator.qml</file>
<file>FluentUI/Controls/FluPasswordBox.qml</file>
<file>FluentUI/Controls/FluPivot.qml</file>
<file>FluentUI/Controls/FluPivotItem.qml</file>
<file>FluentUI/Controls/FluPopup.qml</file>
<file>FluentUI/Controls/FluProgressBar.qml</file>
<file>FluentUI/Controls/FluProgressButton.qml</file>
<file>FluentUI/Controls/FluProgressRing.qml</file>
<file>FluentUI/Controls/FluQRCode.qml</file>
<file>FluentUI/Controls/FluRadioButton.qml</file>
<file>FluentUI/Controls/FluRadioButtons.qml</file>
<file>FluentUI/Controls/FluRangeSlider.qml</file>
<file>FluentUI/Controls/FluRatingControl.qml</file>
<file>FluentUI/Controls/FluRectangle.qml</file>
<file>FluentUI/Controls/FluRemoteLoader.qml</file>
<file>FluentUI/Controls/FluScreenshot.qml</file>
<file>FluentUI/Controls/FluScrollablePage.qml</file>
<file>FluentUI/Controls/FluScrollBar.qml</file>
<file>FluentUI/Controls/FluScrollIndicator.qml</file>
<file>FluentUI/Controls/FluShadow.qml</file>
<file>FluentUI/Controls/FluSlider.qml</file>
<file>FluentUI/Controls/FluSpinBox.qml</file>
<file>FluentUI/Controls/FluStaggeredView.qml</file>
<file>FluentUI/Controls/FluStatusView.qml</file>
<file>FluentUI/Controls/FluTableModelColumn.qml</file>
<file>FluentUI/Controls/FluTableView.qml</file>
<file>FluentUI/Controls/FluTabView.qml</file>
<file>FluentUI/Controls/FluText.qml</file>
<file>FluentUI/Controls/FluTextBox.qml</file>
<file>FluentUI/Controls/FluTextBoxBackground.qml</file>
<file>FluentUI/Controls/FluTextBoxMenu.qml</file>
<file>FluentUI/Controls/FluTextButton.qml</file>
<file>FluentUI/Controls/FluTimeline.qml</file>
<file>FluentUI/Controls/FluTimePicker.qml</file>
<file>FluentUI/Controls/FluToggleButton.qml</file>
<file>FluentUI/Controls/FluToggleSwitch.qml</file>
<file>FluentUI/Controls/FluTooltip.qml</file>
<file>FluentUI/Controls/FluTour.qml</file>
<file>FluentUI/Controls/FluTreeView.qml</file>
<file>FluentUI/Controls/FluWindow.qml</file>
<file>FluentUI/Controls/ColorPicker/ColorPicker.qml</file>
<file>FluentUI/Controls/ColorPicker/Content/Checkerboard.qml</file>
<file>FluentUI/Controls/ColorPicker/Content/ColorSlider.qml</file>
<file>FluentUI/Controls/ColorPicker/Content/NumberBox.qml</file>
<file>FluentUI/Controls/ColorPicker/Content/PanelBorder.qml</file>
<file>FluentUI/Controls/ColorPicker/Content/SBPicker.qml</file>
<file>FluentUI/Controls/FluLoadingButton.qml</file>
</qresource>
</RCC>

View File

@ -23,7 +23,6 @@ Rectangle {
radius: 4 radius: 4
border.width: 1 border.width: 1
border.color: dividerColor border.color: dividerColor
Component.onCompleted: { Component.onCompleted: {
if(current){ if(current){
const date = current const date = current
@ -33,7 +32,6 @@ Rectangle {
text_date.text = year+"-"+(month+1)+"-"+day text_date.text = year+"-"+(month+1)+"-"+day
} }
} }
MouseArea{ MouseArea{
id:mouse_area id:mouse_area
hoverEnabled: true hoverEnabled: true

View File

@ -7,6 +7,7 @@ Item {
property var date: new Date() property var date: new Date()
property var currentDate : new Date() property var currentDate : new Date()
property var toDay: new Date() property var toDay: new Date()
property int radius: 5
signal dateClicked(var date) signal dateClicked(var date)
id:control id:control
width: 280 width: 280
@ -214,10 +215,7 @@ Item {
} }
FluArea{ FluArea{
anchors.fill: parent anchors.fill: parent
radius: 5 radius: control.radius
FluShadow{
radius: 5
}
Rectangle{ Rectangle{
id:layout_divider id:layout_divider
height: 1 height: 1

View File

@ -1,4 +1,4 @@
import QtQuick 2.13 import QtQuick
import "./../JS/Chart.js" as Chart import "./../JS/Chart.js" as Chart
Canvas { Canvas {
@ -10,7 +10,7 @@ Canvas {
property var chartOptions property var chartOptions
property double chartAnimationProgress: 0.1 property double chartAnimationProgress: 0.1
property int animationEasingType: Easing.InOutExpo property int animationEasingType: Easing.InOutExpo
property double animationDuration: 500 property double animationDuration: 0
property var memorizedContext property var memorizedContext
property var memorizedData property var memorizedData
property var memorizedOptions property var memorizedOptions

View File

@ -4,15 +4,14 @@ import FluentUI
import "ColorPicker" import "ColorPicker"
Item { Item {
id:control
property alias colorValue: color_picker.colorValue property alias colorValue: color_picker.colorValue
property int radius: 5
width: color_picker.width+10 width: color_picker.width+10
height: color_picker.height height: color_picker.height
FluArea{ FluArea{
anchors.fill: parent anchors.fill: parent
radius: 5 radius: control.radius
FluShadow{
radius: 5
}
ColorPicker{ ColorPicker{
id:color_picker id:color_picker
} }

View File

@ -0,0 +1,41 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import FluentUI
FluButton {
property bool loading: false
id: control
disabled: loading
contentItem: Row{
spacing: 6
FluText {
text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font: control.font
color: control.textColor
anchors.verticalCenter: parent.verticalCenter
}
Item{
width: control.loading ? 16 : 0
height: 16
anchors.verticalCenter: parent.verticalCenter
visible: width!==0
clip: true
Behavior on width {
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
}
}
FluProgressRing{
width: 16
height: 16
strokeWidth:3
anchors.centerIn: parent
}
}
}
}

View File

@ -125,7 +125,6 @@ Item {
Component{ Component{
id:com_panel_item_header id:com_panel_item_header
Item{ Item{
clip: true
height: { height: {
if(model.parent){ if(model.parent){
return model.parent.isExpand ? 30 : 0 return model.parent.isExpand ? 30 : 0
@ -155,7 +154,6 @@ Item {
Item{ Item{
height: 38 height: 38
width: layout_list.width width: layout_list.width
clip: true
FluControl{ FluControl{
id:item_control id:item_control
anchors{ anchors{
@ -379,7 +377,6 @@ Item {
duration: 83 duration: 83
} }
} }
clip: true
height: { height: {
if(model.parent){ if(model.parent){
return model.parent.isExpand ? 38 : 0 return model.parent.isExpand ? 38 : 0
@ -410,6 +407,31 @@ Item {
Drag.hotSpot.x: item_control.width / 2 Drag.hotSpot.x: item_control.width / 2
Drag.hotSpot.y: item_control.height / 2 Drag.hotSpot.y: item_control.height / 2
Drag.dragType: Drag.Automatic Drag.dragType: Drag.Automatic
onClicked:{
if(type === 0){
if(model.onTapListener){
model.onTapListener()
}else{
nav_list.currentIndex = _idx
layout_footer.currentIndex = -1
model.tap()
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}else{
if(model.onTapListener){
model.onTapListener()
}else{
nav_list.currentIndex = nav_list.count-layout_footer.count+_idx
layout_footer.currentIndex = _idx
model.tap()
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}
}
MouseArea{ MouseArea{
id:item_mouse id:item_mouse
anchors.fill: parent anchors.fill: parent
@ -434,29 +456,7 @@ Item {
loader_item_menu.item.popup(); loader_item_menu.item.popup();
} }
}else{ }else{
if(type === 0){ item_control.clicked()
if(model.onTapListener){
model.onTapListener()
}else{
nav_list.currentIndex = _idx
layout_footer.currentIndex = -1
model.tap()
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}else{
if(model.onTapListener){
model.onTapListener()
}else{
nav_list.currentIndex = nav_list.count-layout_footer.count+_idx
layout_footer.currentIndex = _idx
model.tap()
if(d.isMinimal || d.isCompact){
d.enableNavigationPanel = false
}
}
}
} }
} }
} }
@ -753,7 +753,12 @@ Item {
id:nav_stack2 id:nav_stack2
anchors.fill: nav_stack anchors.fill: nav_stack
clip: true clip: true
visible: FluPageType.SingleInstance === nav_stack.currentItem.launchMode visible: {
if(!nav_stack.currentItem){
return false
}
return FluPageType.SingleInstance === nav_stack.currentItem.launchMode
}
} }
function navStack(){ function navStack(){
return nav_stack return nav_stack

View File

@ -2,30 +2,32 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import FluentUI import FluentUI
Item { Page {
default property alias content: d.children default property alias content: d.children
property alias currentIndex: nav_list.currentIndex property alias currentIndex: nav_list.currentIndex
property color normalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120 property color textNormalColor: FluTheme.dark ? FluColors.Grey120 : FluColors.Grey120
property color hoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black property color textHoverColor: FluTheme.dark ? FluColors.Grey10 : FluColors.Black
property int textSize: 28
property bool textBold: true
property int textSpacing: 10
property int headerSpacing: 20
property int headerHeight: 40
id:control id:control
width: 400 width: 400
height: 300 height: 300
implicitHeight: height implicitHeight: height
implicitWidth: width implicitWidth: width
MouseArea{
anchors.fill: parent
preventStealing: true
}
FluObject{ FluObject{
id:d id:d
property int tabY: control.headerHeight/2+control.textSize/2 + 3
} }
ListView{ background:Item{}
header:ListView{
id:nav_list id:nav_list
height: 40 implicitHeight: control.headerHeight
width: control.width implicitWidth: control.width
model:d.children model:d.children
clip: true spacing: control.headerSpacing
spacing: 20
interactive: false interactive: false
orientation: ListView.Horizontal orientation: ListView.Horizontal
highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0 highlightMoveDuration: FluTheme.enableAnimation ? 167 : 0
@ -36,7 +38,7 @@ Item {
radius: 1.5 radius: 1.5
color: FluTheme.primaryColor.dark color: FluTheme.primaryColor.dark
width: nav_list.currentItem ? nav_list.currentItem.width : 0 width: nav_list.currentItem ? nav_list.currentItem.width : 0
y:37 y:d.tabY
Behavior on width { Behavior on width {
enabled: FluTheme.enableAnimation enabled: FluTheme.enableAnimation
NumberAnimation{ NumberAnimation{
@ -50,18 +52,25 @@ Item {
id:item_button id:item_button
width: item_title.width width: item_title.width
height: nav_list.height height: nav_list.height
focusPolicy:Qt.TabFocus
background:Item{ background:Item{
FluFocusRectangle{
anchors.margins: -4
visible: item_button.activeFocus
radius:4
}
} }
contentItem: Item{ contentItem: Item{
FluText { FluText {
id:item_title id:item_title
font: FluTextStyle.Title
text: modelData.title text: modelData.title
anchors.centerIn: parent anchors.centerIn: parent
font.pixelSize: control.textSize
font.bold: control.textBold
color: { color: {
if(item_button.hovered) if(item_button.hovered)
return hoverColor return textHoverColor
return normalColor return textNormalColor
} }
} }
} }
@ -72,13 +81,7 @@ Item {
} }
Item{ Item{
id:container id:container
anchors{ anchors.fill: parent
top: nav_list.bottom
topMargin: 10
left: parent.left
right: parent.right
bottom: parent.bottom
}
Repeater{ Repeater{
model:d.children model:d.children
Loader{ Loader{

View File

@ -0,0 +1,135 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import FluentUI
Button {
property real progress
property bool disabled: false
property string contentDescription: ""
QtObject{
id:d
property bool checked: rect_back.height == background.height
}
property color normalColor: {
if(d.checked){
return FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
}else{
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(254/255,254/255,254/255,1)
}
}
property color hoverColor: {
if(d.checked){
return FluTheme.dark ? Qt.darker(normalColor,1.1) : Qt.lighter(normalColor,1.1)
}else{
return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
}
}
property color disableColor: {
if(d.checked){
return FluTheme.dark ? Qt.rgba(82/255,82/255,82/255,1) : Qt.rgba(199/255,199/255,199/255,1)
}else{
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
}
}
property color pressedColor: FluTheme.dark ? Qt.darker(normalColor,1.2) : Qt.lighter(normalColor,1.2)
Accessible.role: Accessible.Button
Accessible.name: control.text
Accessible.description: contentDescription
Accessible.onPressAction: control.clicked()
focusPolicy:Qt.TabFocus
id: control
enabled: !disabled
horizontalPadding:12
background: FluItem{
implicitWidth: 28
implicitHeight: 28
radius: [4,4,4,4]
Rectangle{
anchors.fill: parent
border.color: FluTheme.dark ? "#505050" : "#DFDFDF"
border.width: d.checked ? 0 : 1
radius: 4
color:{
if(!enabled){
return disableColor
}
if(d.checked){
if(pressed){
return pressedColor
}
}
return hovered ? hoverColor :normalColor
}
}
Rectangle{
id:rect_back
width: parent.width * control.progress
height: control.progress === 1 ? background.height : 3
visible: !d.checked
color: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
anchors.bottom: parent.bottom
Behavior on height{
enabled: control.progress === 1
SequentialAnimation {
PauseAnimation {
duration: FluTheme.enableAnimation ? 167 : 0
}
NumberAnimation{
duration: FluTheme.enableAnimation ? 167 : 0
from: 3
to: background.height
}
}
}
Behavior on width{
NumberAnimation{
duration: 167
}
}
}
FluFocusRectangle{
visible: control.activeFocus
radius:4
}
}
contentItem: FluText {
text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: {
if(d.checked){
if(FluTheme.dark){
if(!enabled){
return Qt.rgba(173/255,173/255,173/255,1)
}
return Qt.rgba(0,0,0,1)
}else{
return Qt.rgba(1,1,1,1)
}
}else{
if(FluTheme.dark){
if(!enabled){
return Qt.rgba(131/255,131/255,131/255,1)
}
if(!d.checked){
if(pressed){
return Qt.rgba(162/255,162/255,162/255,1)
}
}
return Qt.rgba(1,1,1,1)
}else{
if(!enabled){
return Qt.rgba(160/255,160/255,160/255,1)
}
if(!d.checked){
if(pressed){
return Qt.rgba(96/255,96/255,96/255,1)
}
}
return Qt.rgba(0,0,0,1)
}
}
}
}
}

View File

@ -26,7 +26,7 @@ Item{
Component{ Component{
id:com_screen id:com_screen
Window{ Window{
property bool isZeroPos: screenshot.start === Qt.point(0,0) && screenshot.end === Qt.point(0,0) property bool isZeroPos: screenshot.start.x === 0 && screenshot.start.y === 0 && screenshot.end.x === 0 && screenshot.end.y === 0
id:window_screen id:window_screen
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint
x:-1 x:-1
@ -41,7 +41,7 @@ Item{
} }
} }
Component.onCompleted: { Component.onCompleted: {
setGeometry(0,0,screenshot_background.width,screenshot_background.height) setGeometry(0,0,screenshot_background.width,screenshot_background.height+1)
} }
ScreenshotBackground{ ScreenshotBackground{
id:screenshot_background id:screenshot_background
@ -123,7 +123,7 @@ Item{
MouseArea{ MouseArea{
property point clickPos: Qt.point(0,0) property point clickPos: Qt.point(0,0)
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.SizeAllCursor cursorShape: d.isEdit ? Qt.ArrowCursor : Qt.SizeAllCursor
onPressed: onPressed:
(mouse)=>{ (mouse)=>{
clickPos = Qt.point(mouse.x, mouse.y) clickPos = Qt.point(mouse.x, mouse.y)
@ -477,7 +477,7 @@ Item{
width: 100 width: 100
height: 40 height: 40
visible: { visible: {
if(screenshot.start === Qt.point(0,0) && screenshot.end === Qt.point(0,0)){ if(isZeroPos){
return false return false
} }
if(d.enablePosition){ if(d.enablePosition){

View File

@ -14,18 +14,115 @@ T.ScrollBar {
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding) implicitContentHeight + topPadding + bottomPadding)
padding: 2
visible: control.policy !== T.ScrollBar.AlwaysOff visible: control.policy !== T.ScrollBar.AlwaysOff
minimumSize: Math.max(orientation === Qt.Horizontal ? height / width : width / height,0.3) minimumSize: Math.max(orientation === Qt.Horizontal ? height / width : width / height,0.3)
QtObject{
id:d
property int minLine : 2
property int maxLine : 6
}
verticalPadding : vertical ? 15 : 3
horizontalPadding : horizontal ? 15 : 3
background: Rectangle{
id:back_rect
radius: 5
color:FluTheme.dark ? Qt.rgba(44/255,44/255,44/255,1) : Qt.rgba(255/255,255/255,255/255,1)
opacity:{
if(vertical){
return d.maxLine === Number(rect_bar.width)
}
return d.maxLine === Number(rect_bar.height)
}
Behavior on opacity {
NumberAnimation{
duration: 50
}
}
}
FluIconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
visible: control.horizontal
opacity: back_rect.opacity
anchors{
left: parent.left
leftMargin: 2
verticalCenter: parent.verticalCenter
}
iconColor: control.color
iconSource: FluentIcons.CaretLeftSolid8
onClicked: {
control.decrease()
}
}
FluIconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
iconColor: control.color
opacity: back_rect.opacity
anchors{
right: parent.right
rightMargin: 2
verticalCenter: parent.verticalCenter
}
visible: control.horizontal
iconSource: FluentIcons.CaretRightSolid8
onClicked: {
control.increase()
}
}
FluIconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
iconColor: control.color
opacity: back_rect.opacity
anchors{
top: parent.top
topMargin: 2
horizontalCenter: parent.horizontalCenter
}
visible: control.vertical
iconSource: FluentIcons.CaretUpSolid8
onClicked: {
control.decrease()
}
}
FluIconButton{
width: 12
height: 12
iconSize: 8
verticalPadding: 0
horizontalPadding: 0
iconColor: control.color
opacity: back_rect.opacity
anchors{
bottom: parent.bottom
bottomMargin: 2
horizontalCenter: parent.horizontalCenter
}
visible: control.vertical
iconSource: FluentIcons.CaretDownSolid8
onClicked: {
control.increase()
}
}
contentItem: Item { contentItem: Item {
property bool collapsed: (control.policy === T.ScrollBar.AlwaysOn || (control.active && control.size < 1.0)) property bool collapsed: (control.policy === T.ScrollBar.AlwaysOn || (control.active && control.size < 1.0))
implicitWidth: control.interactive ? 6 : 2 implicitWidth: control.interactive ? d.maxLine : d.minLine
implicitHeight: control.interactive ? 6 : 2 implicitHeight: control.interactive ? d.maxLine : d.minLine
Rectangle{ Rectangle{
id:rect_bar id:rect_bar
width: vertical ? 2 : parent.width width: vertical ? d.minLine : parent.width
height: horizontal ? 2 : parent.height height: horizontal ? d.minLine : parent.height
color:{ color:{
if(control.pressed){ if(control.pressed){
return control.pressedColor return control.pressedColor
@ -45,8 +142,8 @@ T.ScrollBar {
when: contentItem.collapsed when: contentItem.collapsed
PropertyChanges { PropertyChanges {
target: rect_bar target: rect_bar
width: vertical ? 6 : parent.width width: vertical ? d.maxLine : parent.width
height: horizontal ? 6 : parent.height height: horizontal ? d.maxLine : parent.height
} }
} }
,State{ ,State{
@ -54,8 +151,8 @@ T.ScrollBar {
when: !contentItem.collapsed when: !contentItem.collapsed
PropertyChanges { PropertyChanges {
target: rect_bar target: rect_bar
width: vertical ? 2 : parent.width width: vertical ? d.minLine : parent.width
height: horizontal ? 2 : parent.height height: horizontal ? d.minLine : parent.height
} }
} }
] ]
@ -74,6 +171,8 @@ T.ScrollBar {
} }
,Transition { ,Transition {
to: "show" to: "show"
SequentialAnimation {
PauseAnimation { duration: 450 }
NumberAnimation { NumberAnimation {
target: rect_bar target: rect_bar
properties: vertical ? "width" : "height" properties: vertical ? "width" : "height"
@ -81,6 +180,7 @@ T.ScrollBar {
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
}
] ]
} }
} }

View File

@ -3,64 +3,22 @@ import QtQuick.Controls
import FluentUI import FluentUI
Item { Item {
//高性能阴影比DropShadow阴影性能高出数倍
property color color: FluTheme.dark ? "#FFFFFF" : "#999999" property color color: FluTheme.dark ? "#FFFFFF" : "#999999"
property int elevation: 6
property int radius: 4 property int radius: 4
id:control id:control
anchors.fill: parent anchors.fill: parent
anchors.margins: -4 Repeater{
model: elevation
Rectangle{ Rectangle{
width: control.width anchors.fill: parent
height: control.height
anchors.centerIn: parent
color: "#00000000" color: "#00000000"
opacity: 0.02 opacity: 0.01 * (elevation-index+1)
border.width: 1 anchors.margins: -index
radius: control.radius radius: control.radius+index
border.width: index
border.color: control.color border.color: control.color
} }
Rectangle{
width: control.width - 2
height: control.height - 2
anchors.centerIn: parent
color: "#00000000"
opacity: 0.04
border.width: 1
radius: control.radius
border.color: control.color
} }
Rectangle{
width: control.width - 4
height: control.height - 4
anchors.centerIn: parent
color: "#00000000"
opacity: 0.06
border.width: 1
radius: control.radius
border.color: control.color
}
Rectangle{
width: control.width - 6
height: control.height - 6
anchors.centerIn: parent
color: "#00000000"
opacity: 0.08
border.width: 1
radius: control.radius
border.color: control.color
}
Rectangle{
width: control.width - 8
height: control.height - 8
anchors.centerIn: parent
opacity: 0.1
radius: control.radius
color: "#00000000"
border.width: 1
border.color: control.color
}
} }

View File

@ -69,6 +69,7 @@ Rectangle {
Component{ Component{
id:com_edit id:com_edit
FluTextBox{ FluTextBox{
id:text_box
text: display text: display
readOnly: true === columnSource[column].readOnly readOnly: true === columnSource[column].readOnly
Component.onCompleted: { Component.onCompleted: {
@ -77,7 +78,7 @@ Rectangle {
} }
onCommit: { onCommit: {
if(!readOnly){ if(!readOnly){
display = text display = text_box.text
} }
tableView.closeEditor() tableView.closeEditor()
} }

View File

@ -31,7 +31,7 @@ FluMenu{
text:cutText text:cutText
focus: false focus: false
padding: 0 padding: 0
height: visible ? 34 : 0 height: visible ? 36 : 0
visible: inputItem.selectedText !== "" && !inputItem.readOnly visible: inputItem.selectedText !== "" && !inputItem.readOnly
onClicked: { onClicked: {
inputItem.cut() inputItem.cut()
@ -43,7 +43,7 @@ FluMenu{
text:copyText text:copyText
focus: false focus: false
padding: 0 padding: 0
height: visible ? 34 : 0 height: visible ? 36 : 0
visible: inputItem.selectedText !== "" visible: inputItem.selectedText !== ""
onClicked: { onClicked: {
inputItem.copy() inputItem.copy()
@ -55,8 +55,8 @@ FluMenu{
text:pasteText text:pasteText
focus: false focus: false
padding: 0 padding: 0
height: visible ? 34 : 0 visible: !inputItem.readOnly
visible: inputItem.canPaste height: visible ? 36 : 0
onClicked: { onClicked: {
inputItem.paste() inputItem.paste()
menu.close() menu.close()
@ -67,7 +67,7 @@ FluMenu{
text:selectAllText text:selectAllText
focus: false focus: false
padding: 0 padding: 0
height: visible ? 34 : 0 height: visible ? 36 : 0
visible: inputItem.text !== "" visible: inputItem.text !== ""
onClicked: { onClicked: {
inputItem.selectAll() inputItem.selectAll()

View File

@ -48,7 +48,7 @@ Button {
border.width: checked ? 0 : 1 border.width: checked ? 0 : 1
FluFocusRectangle{ FluFocusRectangle{
visible: control.activeFocus visible: control.activeFocus
radius:8 radius:4
} }
color:{ color:{
if(!enabled){ if(!enabled){

View File

@ -28,23 +28,31 @@ Button {
Accessible.name: control.text Accessible.name: control.text
Accessible.description: contentDescription Accessible.description: contentDescription
Accessible.onPressAction: control.clicked() Accessible.onPressAction: control.clicked()
height: 20
enabled: !disabled enabled: !disabled
implicitHeight: height
focusPolicy:Qt.TabFocus focusPolicy:Qt.TabFocus
onClicked: clickListener() onClicked: clickListener()
contentItem: Item{} padding: 0
background : RowLayout{ horizontalPadding: 0
onCheckableChanged: {
if(checkable){
checkable = false
}
}
background : Item{
implicitHeight: 20
implicitWidth: 40
}
contentItem: RowLayout{
spacing: control.textSpacing spacing: control.textSpacing
layoutDirection:control.textRight ? Qt.LeftToRight : Qt.RightToLeft layoutDirection:control.textRight ? Qt.LeftToRight : Qt.RightToLeft
Rectangle { Rectangle {
id:control_backgound id:control_backgound
width: 40 width: background.width
height: control.height height: background.height
radius: height / 2 radius: height / 2
FluFocusRectangle{ FluFocusRectangle{
visible: control.activeFocus visible: control.activeFocus
radius: 20 radius: parent.radius
} }
color: { color: {
if(!enabled){ if(!enabled){
@ -69,10 +77,10 @@ Button {
return borderNormalColor return borderNormalColor
} }
Rectangle { Rectangle {
width: 20 width: parent.height
x:checked ? control_backgound.width-width : 0 x:checked ? control_backgound.width-width : 0
height: 20 height: width
radius: 10 radius: width/2
scale: hovered&enabled ? 7/10 : 6/10 scale: hovered&enabled ? 7/10 : 6/10
color: { color: {
if(!enabled){ if(!enabled){

View File

@ -89,19 +89,18 @@ Popup{
ctx.restore() ctx.restore()
} }
function drawRoundedRect(rect, r, ctx) { function drawRoundedRect(rect, r, ctx) {
var ptA = Qt.point(rect.x + r, rect.y) ctx.beginPath();
var ptB = Qt.point(rect.x + rect.width, rect.y) ctx.moveTo(rect.x + r, rect.y);
var ptC = Qt.point(rect.x + rect.width, rect.y + rect.height) ctx.lineTo(rect.x + rect.width - r, rect.y);
var ptD = Qt.point(rect.x, rect.y + rect.height) ctx.arcTo(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + r, r);
var ptE = Qt.point(rect.x, rect.y) ctx.lineTo(rect.x + rect.width, rect.y + rect.height - r);
ctx.beginPath() ctx.arcTo(rect.x + rect.width, rect.y + rect.height, rect.x + rect.width - r, rect.y + rect.height, r);
ctx.moveTo(ptA.x, ptA.y) ctx.lineTo(rect.x + r, rect.y + rect.height);
ctx.arcTo(ptB.x, ptB.y, ptC.x, ptC.y, r) ctx.arcTo(rect.x, rect.y + rect.height, rect.x, rect.y + rect.height - r, r);
ctx.arcTo(ptC.x, ptC.y, ptD.x, ptD.y, r) ctx.lineTo(rect.x, rect.y + r);
ctx.arcTo(ptD.x, ptD.y, ptE.x, ptE.y, r) ctx.arcTo(rect.x, rect.y, rect.x + r, rect.y, r);
ctx.arcTo(ptE.x, ptE.y, ptA.x, ptA.y, r) ctx.closePath();
ctx.fill() ctx.fill()
ctx.closePath()
} }
} }
FluArea{ FluArea{

View File

@ -68,6 +68,8 @@ Window {
id:popup_loading id:popup_loading
modal:true modal:true
focus: true focus: true
width: window.width
height: window.height
anchors.centerIn: Overlay.overlay anchors.centerIn: Overlay.overlay
closePolicy: { closePolicy: {
if(cancel){ if(cancel){
@ -75,17 +77,38 @@ Window {
} }
return Popup.NoAutoClose return Popup.NoAutoClose
} }
Overlay.modal: Rectangle { Overlay.modal: Item {}
color: "#44000000"
}
onVisibleChanged: { onVisibleChanged: {
if(!visible){ if(!visible){
loader_loading.sourceComponent = undefined loader_loading.sourceComponent = undefined
} }
} }
padding: 0
opacity: 0
visible:true visible:true
background: Item{} Behavior on opacity {
SequentialAnimation {
PauseAnimation {
duration: 88
}
NumberAnimation{
duration: 167
}
}
}
Component.onCompleted: {
opacity = 1
}
background: Rectangle{
color:"#44000000"
}
contentItem: Item{ contentItem: Item{
MouseArea{
anchors.fill: parent
onClicked: {
popup_loading.visible = false
}
}
ColumnLayout{ ColumnLayout{
spacing: 8 spacing: 8
anchors.centerIn: parent anchors.centerIn: parent

View File

@ -9,10 +9,11 @@
#include "Def.h" #include "Def.h"
Screenshot::Screenshot(QQuickItem* parent) : QQuickPaintedItem(parent) Screenshot::Screenshot(QQuickItem* parent) : QQuickPaintedItem(parent)
{ {
_desktopGeometry = qApp->primaryScreen()->virtualGeometry(); _desktopGeometry = qApp->primaryScreen()->virtualGeometry();
maskColor(QColor(0,0,0,80)); maskColor(QColor(0,0,0,150));
start(QPoint(0,0)); start(QPoint(0,0));
end(QPoint(0,0)); end(QPoint(0,0));
connect(this,&Screenshot::startChanged,this,[=]{update();}); connect(this,&Screenshot::startChanged,this,[=]{update();});
@ -53,6 +54,7 @@ void ScreenshotBackground::paint(QPainter* painter)
} }
void ScreenshotBackground::capture(const QPoint& start,const QPoint& end){ void ScreenshotBackground::capture(const QPoint& start,const QPoint& end){
update();
auto pixelRatio = qApp->primaryScreen()->devicePixelRatio(); auto pixelRatio = qApp->primaryScreen()->devicePixelRatio();
auto x = qMin(start.x(),end.x()) * pixelRatio; auto x = qMin(start.x(),end.x()) * pixelRatio;
auto y = qMin(start.y(),end.y()) * pixelRatio; auto y = qMin(start.y(),end.y()) * pixelRatio;
@ -72,4 +74,3 @@ void ScreenshotBackground::capture(const QPoint& start,const QPoint& end){
Q_EMIT captrueToFileCompleted(QUrl::fromLocalFile(filePath)); Q_EMIT captrueToFileCompleted(QUrl::fromLocalFile(filePath));
} }
} }

View File

@ -6,6 +6,7 @@
#include <QPainter> #include <QPainter>
#include <QQuickItemGrabResult> #include <QQuickItemGrabResult>
#include "stdafx.h" #include "stdafx.h"
#include <qmath.h>
class ScreenshotBackground : public QQuickPaintedItem class ScreenshotBackground : public QQuickPaintedItem
{ {

View File

@ -1,87 +1,19 @@
#include "fluentuiplugin.h" #include "fluentuiplugin.h"
#include <QtQml/QQmlExtensionPlugin>
#include <QGuiApplication>
#include <qdebug.h>
#include "WindowHelper.h"
#include "Def.h"
#include "FluApp.h"
#include "FluColors.h"
#include "FluTheme.h"
#include "FluTools.h"
#include "FluTextStyle.h"
#include "FluHttp.h"
#include "FluHttpInterceptor.h"
#include "FluWatermark.h"
#include "FluCaptcha.h"
#include "Screenshot.h"
#include "QRCode.h"
int major = 1; #include "FluentUI.h"
int minor = 0;
static FluentUIPlugin instance;
FluentUIPlugin::FluentUIPlugin() FluentUIPlugin::FluentUIPlugin()
{ {
#ifdef FLUENTUI_BUILD_STATIC_LIB
Q_INIT_RESOURCE(fluentui);
#endif
} }
void FluentUIPlugin::registerTypes(const char *uri) void FluentUIPlugin::registerTypes(const char *uri)
{ {
qmlRegisterType<WindowHelper>(uri,major,minor,"WindowHelper"); FluentUI::getInstance()->registerTypes(uri);
qmlRegisterType<QRCode>(uri,major,minor,"QRCode");
qmlRegisterType<FluCaptcha>(uri,major,minor,"FluCaptcha");
qmlRegisterType<FluWatermark>(uri,major,minor,"FluWatermark");
qmlRegisterType<ScreenshotBackground>(uri,major,minor,"ScreenshotBackground");
qmlRegisterType<Screenshot>(uri,major,minor,"Screenshot");
qmlRegisterType<FluColorSet>(uri,major,minor,"FluColorSet");
qmlRegisterType<FluHttpInterceptor>(uri,major,minor,"FluHttpInterceptor");
qmlRegisterType<FluHttp>(uri,major,minor,"FluHttp");
qmlRegisterUncreatableMetaObject(Fluent_Awesome::staticMetaObject, uri,major,minor,"FluentIcons", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluThemeType::staticMetaObject, uri,major,minor,"FluThemeType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluPageType::staticMetaObject, uri,major,minor,"FluPageType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluWindowType::staticMetaObject, uri,major,minor,"FluWindowType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluTreeViewType::staticMetaObject, uri,major,minor,"FluTreeViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluStatusViewType::staticMetaObject, uri,major,minor,"FluStatusViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluContentDialogType::staticMetaObject, uri,major,minor,"FluContentDialogType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluTimePickerType::staticMetaObject, uri,major,minor,"FluTimePickerType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluCalendarViewType::staticMetaObject, uri,major,minor,"FluCalendarViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluTabViewType::staticMetaObject, uri,major,minor,"FluTabViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluNavigationViewType::staticMetaObject, uri,major,minor,"FluNavigationViewType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluTimelineType::staticMetaObject, uri,major,minor,"FluTimelineType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluScreenshotType::staticMetaObject, uri,major,minor,"FluScreenshotType", "Access to enums & flags only");
} }
#ifdef FLUENTUI_BUILD_STATIC_LIB
void FluentUIPlugin::registerTypes()
{
instance()->registerTypes("FluentUI");
}
FluentUIPlugin* FluentUIPlugin::instance()
{
static FluentUIPlugin instance;
return &instance;
}
#endif
void FluentUIPlugin::initializeEngine(QQmlEngine *engine, const char *uri) void FluentUIPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
{ {
Q_UNUSED(uri) Q_UNUSED(uri)
#ifdef Q_OS_WIN FluentUI::getInstance()->initializeEngine(engine,uri);
QFont font;
font.setFamily("Microsoft YaHei");
QGuiApplication::setFont(font);
#endif
FluApp* app = FluApp::getInstance();
engine->rootContext()->setContextProperty("FluApp",app);
FluColors* colors = FluColors::getInstance();
engine->rootContext()->setContextProperty("FluColors",colors);
FluTheme* theme = FluTheme::getInstance();
engine->rootContext()->setContextProperty("FluTheme",theme);
FluTools* tools = FluTools::getInstance();
engine->rootContext()->setContextProperty("FluTools",tools);
FluTextStyle* textStyle = FluTextStyle::getInstance();
engine->rootContext()->setContextProperty("FluTextStyle",textStyle);
engine->addImportPath("qrc:/FluentUI/imports/");
} }

View File

@ -7,14 +7,9 @@ class FluentUIPlugin : public QQmlExtensionPlugin
{ {
Q_OBJECT Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public: public:
FluentUIPlugin(); FluentUIPlugin();
void registerTypes(const char *uri) Q_DECL_OVERRIDE; void registerTypes(const char *uri) Q_DECL_OVERRIDE;
#ifdef FLUENTUI_BUILD_STATIC_LIB
static void registerTypes();
static FluentUIPlugin* instance();
#endif
void initializeEngine(QQmlEngine *engine, const char *uri) Q_DECL_OVERRIDE; void initializeEngine(QQmlEngine *engine, const char *uri) Q_DECL_OVERRIDE;
}; };

View File

@ -1,98 +0,0 @@
<RCC>
<qresource prefix="/FluentUI">
<file>Qt5/imports/FluentUI/JS/Chart.js</file>
<file>Qt5/imports/FluentUI/Image/noise.png</file>
<file>Qt5/imports/FluentUI/Controls/FluAcrylic.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluAppBar.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluArea.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluAutoSuggestBox.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluBadge.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluBreadcrumbBar.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluButton.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluCalendarPicker.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluCalendarView.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluCarousel.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluChart.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluCheckBox.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluColorPicker.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluColorView.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluComboBox.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluContentDialog.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluContentPage.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluControl.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluCopyableText.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluDatePicker.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluDivider.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluDropDownButton.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluExpander.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluFilledButton.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluFlipView.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluFocusRectangle.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluIcon.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluIconButton.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluImage.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluInfoBar.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluItem.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluItemDelegate.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluMenu.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluMenuBar.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluMenuBarItem.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluMenuItem.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluMenuSeparator.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluMultilineTextBox.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluNavigationView.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluObject.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPage.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPagination.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPaneItem.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPaneItemEmpty.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPaneItemExpander.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPaneItemHeader.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPaneItemSeparator.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPasswordBox.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPivot.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPivotItem.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluPopup.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluProgressBar.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluProgressRing.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluQRCode.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluRadioButton.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluRadioButtons.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluRatingControl.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluRectangle.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluRemoteLoader.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluScreenshot.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluScrollablePage.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluScrollBar.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluScrollIndicator.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluShadow.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluSlider.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluSpinBox.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluStatusView.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTableModelColumn.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTableView.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTabView.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluText.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTextBox.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTextBoxBackground.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTextBoxMenu.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTextButton.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTimeline.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTimePicker.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluToggleButton.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluToggleSwitch.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTooltip.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTour.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluTreeView.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluWindow.qml</file>
<file>Qt5/imports/FluentUI/Font/Segoe_Fluent_Icons.ttf</file>
<file>Qt5/imports/FluentUI/Controls/ColorPicker/ColorPicker.qml</file>
<file>Qt5/imports/FluentUI/Controls/ColorPicker/Content/Checkerboard.qml</file>
<file>Qt5/imports/FluentUI/Controls/ColorPicker/Content/ColorSlider.qml</file>
<file>Qt5/imports/FluentUI/Controls/ColorPicker/Content/NumberBox.qml</file>
<file>Qt5/imports/FluentUI/Controls/ColorPicker/Content/PanelBorder.qml</file>
<file>Qt5/imports/FluentUI/Controls/ColorPicker/Content/SBPicker.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluRangeSlider.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluStaggeredView.qml</file>
</qresource>
</RCC>