Compare commits

...

20 Commits
1.5.5 ... 1.5.6

Author SHA1 Message Date
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
37 changed files with 1713 additions and 684 deletions

View File

@ -263,3 +263,28 @@ 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.

View File

@ -138,7 +138,8 @@ FluExpander{
"FluTimeline", "FluTimeline",
"FluChart", "FluChart",
"FluRangeSlider", "FluRangeSlider",
"FluStaggeredView" "FluStaggeredView",
"FluProgressButton"
]; ];
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

@ -161,6 +161,58 @@ 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{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true

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{

View File

@ -10,14 +10,62 @@ import "qrc:///example/qml/component"
FluContentPage{ FluContentPage{
title:"Http" title:"Http"
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
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 +84,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 +93,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 +107,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 +121,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 +135,86 @@ 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)=>{
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 +223,136 @@ 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 = ""
}
}
}
}
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 +378,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

@ -138,7 +138,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{
@ -309,15 +309,16 @@ CustomWindow {
} }
} }
function checkUpdate(){ HttpCallable{
var callable = {} id:callable
callable.onStart = function(){ 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")
} }
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)
@ -327,10 +328,15 @@ CustomWindow {
dialog_update.open() dialog_update.open()
} }
} }
callable.onError = function(status,errorString){ onError:
(status,errorString)=>{
console.debug(status+";"+errorString) console.debug(status+";"+errorString)
} }
http.get("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest",callable) }
function checkUpdate(){
var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest")
http.get(request,callable);
} }
} }

View File

@ -138,7 +138,8 @@ FluExpander{
"FluTimeline", "FluTimeline",
"FluChart", "FluChart",
"FluRangeSlider", "FluRangeSlider",
"FluStaggeredView" "FluStaggeredView",
"FluProgressButton"
]; ];
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

@ -161,6 +161,58 @@ 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{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true

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{

View File

@ -11,14 +11,62 @@ import "../component"
FluContentPage{ FluContentPage{
title:"Http" title:"Http"
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
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 +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{
@ -59,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{
@ -85,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{
@ -111,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
@ -138,7 +136,86 @@ 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)=>{
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 +224,136 @@ 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 = ""
}
}
}
}
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 +379,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

@ -139,7 +139,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/Qt6_156_LieflatPage.qml"
} }
} }
front: Item{ front: Item{
@ -310,15 +310,16 @@ CustomWindow {
} }
} }
function checkUpdate(){ HttpCallable{
var callable = {} id:callable
callable.onStart = function(){ 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")
} }
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)
@ -328,10 +329,15 @@ CustomWindow {
dialog_update.open() dialog_update.open()
} }
} }
callable.onError = function(status,errorString){ onError:
(status,errorString)=>{
console.debug(status+";"+errorString) console.debug(status+";"+errorString)
} }
http.get("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest",callable) }
function checkUpdate(){
var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest")
http.get(request,callable);
} }
} }

View File

@ -4,6 +4,24 @@
#include <QObject> #include <QObject>
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
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

@ -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,34 @@ 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* request,HttpCallable* callable){
_cache.append(reply); request->method("post");
} auto requestMap = request->toMap();
auto httpId = request->httpId();
void FluHttp::post(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){ QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
QMap<QString, QVariant> data = invokeIntercept(params,headers,"post").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 +115,283 @@ 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* request,HttpCallable* callable){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"postString").toMap(); 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* request,HttpCallable* callable){
onDownloadProgress(callable,bytesReceived,bytesTotal); 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* request,HttpCallable* callable){
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* request,HttpCallable* callable){
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 +407,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 +419,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 +426,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 +459,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,HttpRequest* request){
if(callable){
Q_EMIT callable->finish();
}
if(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,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

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

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

@ -144,6 +144,55 @@ public:
*/ */
Q_INVOKABLE QColor colorAlpha(const QColor&,qreal alpha); Q_INVOKABLE QColor colorAlpha(const QColor&,qreal alpha);
/**
* @brief md5
* @param text
* @return
*/
Q_INVOKABLE QString md5(QString text);
/**
* @brief sha256
* @param text
* @return
*/
Q_INVOKABLE QString sha256(QString text);
/**
* @brief toBase64
* @param text
* @return
*/
Q_INVOKABLE QString toBase64(QString text);
/**
* @brief fromBase64
* @param text
* @return
*/
Q_INVOKABLE QString fromBase64(QString text);
/**
* @brief removeDir
* @param dirPath
* @return
*/
Q_INVOKABLE bool removeDir(QString dirPath);
/**
* @brief removeFile
* @param filePath
* @return
*/
Q_INVOKABLE bool removeFile(QString filePath);
/**
* @brief showFileInFolder
* @param path
*/
Q_INVOKABLE void showFileInFolder(QString path);
}; };
#endif // FLUTOOLS_H #endif // FLUTOOLS_H

View File

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

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

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

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

View File

@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only. // It is used for QML tooling purposes only.
// //
// This file was auto-generated by: // This file was auto-generated by:
// 'qmlplugindump -nonrelocatable FluentUI 1.0 D:/QtProjects/build-FluentUI-Desktop_Qt_5_15_2_MSVC2019_64bit-Release/src' // 'qmlplugindump -nonrelocatable FluentUI 1.0 D:\QtProjects\build-FluentUI-Desktop_Qt_5_15_2_MSVC2019_64bit-Release\src'
Module { Module {
dependencies: ["QtQuick 2.0"] dependencies: ["QtQuick 2.0"]
@ -70,116 +70,49 @@ Module {
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
Property { name: "retry"; type: "int" } Property { name: "retry"; type: "int" }
Property { name: "timeout"; type: "int" } Property { name: "timeout"; type: "int" }
Property { name: "cacheMode"; type: "int" }
Property { name: "cacheDir"; type: "string" }
Property { name: "breakPointDownload"; type: "bool" }
Method { Method {
name: "get" name: "newRequest"
type: "HttpRequest*"
Parameter { name: "url"; type: "string" } Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
Parameter { name: "params"; type: "QVariantMap" }
Parameter { name: "headers"; type: "QVariantMap" }
} }
Method { name: "newRequest"; type: "HttpRequest*" }
Method { Method {
name: "get" name: "get"
Parameter { name: "url"; type: "string" } Parameter { name: "request"; type: "HttpRequest"; isPointer: true }
Parameter { name: "callable"; type: "QJSValue" } Parameter { name: "callable"; type: "HttpCallable"; isPointer: true }
Parameter { name: "params"; type: "QVariantMap" }
}
Method {
name: "get"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
} }
Method { Method {
name: "post" name: "post"
Parameter { name: "url"; type: "string" } Parameter { name: "request"; type: "HttpRequest"; isPointer: true }
Parameter { name: "callable"; type: "QJSValue" } Parameter { name: "callable"; type: "HttpCallable"; isPointer: true }
Parameter { name: "params"; type: "QVariantMap" }
Parameter { name: "headers"; type: "QVariantMap" }
}
Method {
name: "post"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
Parameter { name: "params"; type: "QVariantMap" }
}
Method {
name: "post"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
} }
Method { Method {
name: "postString" name: "postString"
Parameter { name: "url"; type: "string" } Parameter { name: "request"; type: "HttpRequest"; isPointer: true }
Parameter { name: "callable"; type: "QJSValue" } Parameter { name: "callable"; type: "HttpCallable"; isPointer: true }
Parameter { name: "params"; type: "string" }
Parameter { name: "headers"; type: "QVariantMap" }
}
Method {
name: "postString"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
Parameter { name: "params"; type: "string" }
}
Method {
name: "postString"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
} }
Method { Method {
name: "postJson" name: "postJson"
Parameter { name: "url"; type: "string" } Parameter { name: "request"; type: "HttpRequest"; isPointer: true }
Parameter { name: "callable"; type: "QJSValue" } Parameter { name: "callable"; type: "HttpCallable"; isPointer: true }
Parameter { name: "params"; type: "QVariantMap" }
Parameter { name: "headers"; type: "QVariantMap" }
}
Method {
name: "postJson"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
Parameter { name: "params"; type: "QVariantMap" }
}
Method {
name: "postJson"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
} }
Method { Method {
name: "download" name: "download"
Parameter { name: "url"; type: "string" } Parameter { name: "request"; type: "HttpRequest"; isPointer: true }
Parameter { name: "callable"; type: "QJSValue" } Parameter { name: "callable"; type: "HttpCallable"; isPointer: true }
Parameter { name: "filePath"; type: "string" }
Parameter { name: "params"; type: "QVariantMap" }
Parameter { name: "headers"; type: "QVariantMap" }
}
Method {
name: "download"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
Parameter { name: "filePath"; type: "string" }
Parameter { name: "params"; type: "QVariantMap" }
}
Method {
name: "download"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
Parameter { name: "filePath"; type: "string" }
} }
Method { Method {
name: "upload" name: "upload"
Parameter { name: "url"; type: "string" } Parameter { name: "request"; type: "HttpRequest"; isPointer: true }
Parameter { name: "callable"; type: "QJSValue" } Parameter { name: "callable"; type: "HttpCallable"; isPointer: true }
Parameter { name: "params"; type: "QVariantMap" }
Parameter { name: "headers"; type: "QVariantMap" }
} }
Method { Method {
name: "upload" name: "getBreakPointProgress"
Parameter { name: "url"; type: "string" } type: "double"
Parameter { name: "callable"; type: "QJSValue" } Parameter { name: "request"; type: "HttpRequest"; isPointer: true }
Parameter { name: "params"; type: "QVariantMap" }
}
Method {
name: "upload"
Parameter { name: "url"; type: "string" }
Parameter { name: "callable"; type: "QJSValue" }
} }
Method { name: "cancel" } Method { name: "cancel" }
} }
@ -189,6 +122,21 @@ Module {
exports: ["FluentUI/FluHttpInterceptor 1.0"] exports: ["FluentUI/FluHttpInterceptor 1.0"]
exportMetaObjectRevisions: [0] exportMetaObjectRevisions: [0]
} }
Component {
name: "FluHttpType"
exports: ["FluentUI/FluHttpType 1.0"]
isCreatable: false
exportMetaObjectRevisions: [0]
Enum {
name: "CacheMode"
values: {
"NoCache": 0,
"RequestFailedReadCache": 1,
"IfNoneCacheRequest": 2,
"FirstCacheThenRequest": 4
}
}
}
Component { Component {
name: "FluNavigationViewType" name: "FluNavigationViewType"
exports: ["FluentUI/FluNavigationViewType 1.0"] exports: ["FluentUI/FluNavigationViewType 1.0"]
@ -238,20 +186,6 @@ Module {
"File": 1 "File": 1
} }
} }
}
Component {
name: "FluTimelineType"
exports: ["FluentUI/FluTimelineType 1.0"]
isCreatable: false
exportMetaObjectRevisions: [0]
Enum {
name: "Mode"
values: {
"Left": 0,
"Right": 1,
"Alternate": 2
}
}
} }
Component { Component {
name: "FluStatusViewType" name: "FluStatusViewType"
@ -317,6 +251,20 @@ Module {
} }
} }
} }
Component {
name: "FluTimelineType"
exports: ["FluentUI/FluTimelineType 1.0"]
isCreatable: false
exportMetaObjectRevisions: [0]
Enum {
name: "Mode"
values: {
"Left": 0,
"Right": 1,
"Alternate": 2
}
}
}
Component { Component {
name: "FluTreeViewType" name: "FluTreeViewType"
exports: ["FluentUI/FluTreeViewType 1.0"] exports: ["FluentUI/FluTreeViewType 1.0"]
@ -1772,6 +1720,50 @@ Module {
} }
} }
} }
Component {
name: "HttpCallable"
prototype: "QObject"
exports: ["FluentUI/HttpCallable 1.0"]
exportMetaObjectRevisions: [0]
Signal { name: "start" }
Signal { name: "finish" }
Signal {
name: "error"
Parameter { name: "status"; type: "int" }
Parameter { name: "errorString"; type: "string" }
Parameter { name: "result"; type: "string" }
}
Signal {
name: "success"
Parameter { name: "result"; type: "string" }
}
Signal {
name: "cache"
Parameter { name: "result"; type: "string" }
}
Signal {
name: "downloadProgress"
Parameter { name: "recv"; type: "qlonglong" }
Parameter { name: "total"; type: "qlonglong" }
}
Signal {
name: "uploadProgress"
Parameter { name: "sent"; type: "qlonglong" }
Parameter { name: "total"; type: "qlonglong" }
}
}
Component {
name: "HttpRequest"
prototype: "QObject"
exports: ["FluentUI/HttpRequest 1.0"]
exportMetaObjectRevisions: [0]
Property { name: "url"; type: "string" }
Property { name: "params"; type: "QVariant" }
Property { name: "headers"; type: "QVariant" }
Property { name: "method"; type: "string" }
Property { name: "downloadSavePath"; type: "string" }
Method { name: "httpId"; type: "string" }
}
Component { Component {
name: "QRCode" name: "QRCode"
defaultProperty: "data" defaultProperty: "data"

View File

@ -94,4 +94,5 @@ 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
plugin fluentuiplugin plugin fluentuiplugin

View File

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

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

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

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

@ -38,7 +38,10 @@ void FluentUIPlugin::registerTypes(const char *uri)
qmlRegisterType<FluColorSet>(uri,major,minor,"FluColorSet"); qmlRegisterType<FluColorSet>(uri,major,minor,"FluColorSet");
qmlRegisterType<FluHttpInterceptor>(uri,major,minor,"FluHttpInterceptor"); qmlRegisterType<FluHttpInterceptor>(uri,major,minor,"FluHttpInterceptor");
qmlRegisterType<FluHttp>(uri,major,minor,"FluHttp"); qmlRegisterType<FluHttp>(uri,major,minor,"FluHttp");
qmlRegisterType<HttpCallable>(uri,major,minor,"HttpCallable");
qmlRegisterType<HttpRequest>(uri,major,minor,"HttpRequest");
qmlRegisterUncreatableMetaObject(Fluent_Awesome::staticMetaObject, uri,major,minor,"FluentIcons", "Access to enums & flags only"); qmlRegisterUncreatableMetaObject(Fluent_Awesome::staticMetaObject, uri,major,minor,"FluentIcons", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluHttpType::staticMetaObject, uri,major,minor,"FluHttpType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluThemeType::staticMetaObject, uri,major,minor,"FluThemeType", "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(FluPageType::staticMetaObject, uri,major,minor,"FluPageType", "Access to enums & flags only");
qmlRegisterUncreatableMetaObject(FluWindowType::staticMetaObject, uri,major,minor,"FluWindowType", "Access to enums & flags only"); qmlRegisterUncreatableMetaObject(FluWindowType::staticMetaObject, uri,major,minor,"FluWindowType", "Access to enums & flags only");

View File

@ -94,5 +94,6 @@
<file>Qt5/imports/FluentUI/Controls/ColorPicker/Content/SBPicker.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/FluRangeSlider.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluStaggeredView.qml</file> <file>Qt5/imports/FluentUI/Controls/FluStaggeredView.qml</file>
<file>Qt5/imports/FluentUI/Controls/FluProgressButton.qml</file>
</qresource> </qresource>
</RCC> </RCC>