This commit is contained in:
朱子楚\zhuzi 2023-09-04 20:45:56 +08:00
commit f60eaec07c
16 changed files with 666 additions and 387 deletions

View File

@ -263,3 +263,28 @@ zxing-cpp
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
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

@ -10,14 +10,57 @@ import "qrc:///example/qml/component"
FluContentPage{
title:"Http"
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
FluHttp{
id:http
}
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
console.debug(result)
}
onCache:
(result)=>{
text_info.text = result
console.debug(result)
}
}
Flickable{
id:layout_flick
width: 160
width: 200
clip: true
anchors{
top: parent.top
@ -36,20 +79,6 @@ FluContentPage{
implicitHeight: 36
text: "Get请求"
onClicked: {
var callable = {}
callable.onStart = function(){
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)
}
}
@ -58,20 +87,6 @@ FluContentPage{
implicitHeight: 36
text: "Post表单请求"
onClicked: {
var callable = {}
callable.onStart = function(){
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)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
@ -84,20 +99,6 @@ FluContentPage{
implicitHeight: 36
text: "Post Json请求"
onClicked: {
var callable = {}
callable.onStart = function(){
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)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
@ -110,20 +111,6 @@ FluContentPage{
implicitHeight: 36
text: "Post String请求"
onClicked: {
var callable = {}
callable.onStart = function(){
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)
}
var param = "我命由我不由天"
http.postString("https://httpbingo.org/post",callable,param)
}
@ -146,6 +133,83 @@ FluContentPage{
file_dialog.open()
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "FirstCacheThenRequest缓存"
onClicked: {
var param = {}
param.cacheMode = "FirstCacheThenRequest"
http_cache_firstcachethenrequest.post("https://httpbingo.org/post",callable,param)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "RequestFailedReadCache缓存"
onClicked: {
var param = {}
param.cacheMode = "RequestFailedReadCache"
http_cache_requestfailedreadcache.post("https://httpbingo.org/post",callable,param)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "IfNoneCacheRequest缓存"
onClicked: {
var param = {}
param.cacheMode = "IfNoneCacheRequest"
http_cache_ifnonecacherequest.post("https://httpbingo.org/post",callable,param)
}
}
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
btn_upload.text = "上传文件"
layout_upload_file_size.visible = false
text_upload_file_size.text = ""
}
onError:
(status,errorString,result)=>{
text_info.text = result
console.debug(result)
}
onSuccess:
(result)=>{
text_info.text = result
}
onUploadProgress:
(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
}
}
@ -159,33 +223,37 @@ FluContentPage{
var filePath = FluTools.toLocalPath(fileUrl)
param[fileName] = filePath
}
console.debug(JSON.stringify(param))
var callable = {}
callable.onStart = function(){
btn_upload.disabled = true
http.upload("https://httpbingo.org/post",callable_upload,param)
}
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)
HttpCallable{
id:callable_download
onStart: {
btn_download.disabled = true
}
callable.onError = function(status,errorString,result){
text_info.text = result
console.debug(result)
onFinish: {
btn_download.disabled = false
btn_download.text = "下载文件"
layout_download_file_size.visible = false
text_download_file_size.text = ""
}
callable.onUploadProgress = function(sent,total){
onError:
(status,errorString,result)=>{
showError(errorString)
console.debug(status+";"+errorString+";"+result)
}
onSuccess:
(result)=>{
showSuccess(result)
}
onDownloadProgress:
(recv,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)
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
}
}
@ -193,31 +261,8 @@ FluContentPage{
id: folder_dialog
currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
onAccepted: {
var callable = {}
callable.onStart = function(){
btn_download.disabled = true
}
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)
http.download("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",callable_download,path)
}
}
@ -261,7 +306,6 @@ FluContentPage{
}
}
FluRectangle{
id:layout_upload_file_size
radius: [4,4,4,4]

View File

@ -309,15 +309,17 @@ CustomWindow {
}
}
function checkUpdate(){
var callable = {}
callable.onStart = function(){
HttpCallable{
id:callable
onStart: {
console.debug("satrt check update...")
}
callable.onFinish = function(){
onFinish: {
console.debug("check update finish")
}
callable.onSuccess = function(result){
onSuccess:
(result)=>{
var data = JSON.parse(result)
console.debug("current version "+appInfo.version)
console.debug("new version "+data.tag_name)
@ -327,9 +329,13 @@ CustomWindow {
dialog_update.open()
}
}
callable.onError = function(status,errorString){
onError:
(status,errorString)=>{
console.debug(status+";"+errorString)
}
}
function checkUpdate(){
http.get("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest",callable)
}

View File

@ -11,14 +11,57 @@ import "../component"
FluContentPage{
title:"Http"
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
FluHttp{
id:http
}
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
console.debug(result)
}
onCache:
(result)=>{
text_info.text = result
console.debug(result)
}
}
Flickable{
id:layout_flick
width: 160
width: 200
clip: true
anchors{
top: parent.top
@ -37,20 +80,6 @@ FluContentPage{
implicitHeight: 36
text: "Get请求"
onClicked: {
var callable = {}
callable.onStart = function(){
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)
}
}
@ -59,20 +88,6 @@ FluContentPage{
implicitHeight: 36
text: "Post表单请求"
onClicked: {
var callable = {}
callable.onStart = function(){
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)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
@ -85,20 +100,6 @@ FluContentPage{
implicitHeight: 36
text: "Post Json请求"
onClicked: {
var callable = {}
callable.onStart = function(){
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)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
@ -111,20 +112,6 @@ FluContentPage{
implicitHeight: 36
text: "Post String请求"
onClicked: {
var callable = {}
callable.onStart = function(){
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)
}
var param = "我命由我不由天"
http.postString("https://httpbingo.org/post",callable,param)
}
@ -147,6 +134,83 @@ FluContentPage{
file_dialog.open()
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "FirstCacheThenRequest缓存"
onClicked: {
var param = {}
param.cacheMode = "FirstCacheThenRequest"
http_cache_firstcachethenrequest.post("https://httpbingo.org/post",callable,param)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "RequestFailedReadCache缓存"
onClicked: {
var param = {}
param.cacheMode = "RequestFailedReadCache"
http_cache_requestfailedreadcache.post("https://httpbingo.org/post",callable,param)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "IfNoneCacheRequest缓存"
onClicked: {
var param = {}
param.cacheMode = "IfNoneCacheRequest"
http_cache_ifnonecacherequest.post("https://httpbingo.org/post",callable,param)
}
}
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
btn_upload.text = "上传文件"
layout_upload_file_size.visible = false
text_upload_file_size.text = ""
}
onError:
(status,errorString,result)=>{
text_info.text = result
console.debug(result)
}
onSuccess:
(result)=>{
text_info.text = result
}
onUploadProgress:
(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
}
}
@ -160,33 +224,37 @@ FluContentPage{
var filePath = FluTools.toLocalPath(fileUrl)
param[fileName] = filePath
}
console.debug(JSON.stringify(param))
var callable = {}
callable.onStart = function(){
btn_upload.disabled = true
http.upload("https://httpbingo.org/post",callable_upload,param)
}
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)
HttpCallable{
id:callable_download
onStart: {
btn_download.disabled = true
}
callable.onError = function(status,errorString,result){
text_info.text = result
console.debug(result)
onFinish: {
btn_download.disabled = false
btn_download.text = "下载文件"
layout_download_file_size.visible = false
text_download_file_size.text = ""
}
callable.onUploadProgress = function(sent,total){
onError:
(status,errorString,result)=>{
showError(errorString)
console.debug(status+";"+errorString+";"+result)
}
onSuccess:
(result)=>{
showSuccess(result)
}
onDownloadProgress:
(recv,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)
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
}
}
@ -194,31 +262,8 @@ FluContentPage{
id: folder_dialog
currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
onAccepted: {
var callable = {}
callable.onStart = function(){
btn_download.disabled = true
}
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)
http.download("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",callable_download,path)
}
}
@ -262,7 +307,6 @@ FluContentPage{
}
}
FluRectangle{
id:layout_upload_file_size
radius: [4,4,4,4]

View File

@ -310,15 +310,16 @@ CustomWindow {
}
}
function checkUpdate(){
var callable = {}
callable.onStart = function(){
HttpCallable{
id:callable
onStart: {
console.debug("satrt check update...")
}
callable.onFinish = function(){
onFinish: {
console.debug("check update finish")
}
callable.onSuccess = function(result){
onSuccess:
(result)=>{
var data = JSON.parse(result)
console.debug("current version "+appInfo.version)
console.debug("new version "+data.tag_name)
@ -328,9 +329,13 @@ CustomWindow {
dialog_update.open()
}
}
callable.onError = function(status,errorString){
onError:
(status,errorString)=>{
console.debug(status+";"+errorString)
}
}
function checkUpdate(){
http.get("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest",callable)
}

View File

@ -4,6 +4,24 @@
#include <QObject>
#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 {
Q_NAMESPACE

View File

@ -6,14 +6,26 @@
#include <QUrlQuery>
#include <QHttpMultiPart>
#include <QJsonDocument>
#include <QStandardPaths>
#include <QTextStream>
#include <QDir>
#include "Def.h"
#include "MainThread.h"
#include "FluApp.h"
#include "FluTools.h"
HttpCallable::HttpCallable(QObject *parent)
: QObject{parent}
{
}
FluHttp::FluHttp(QObject *parent)
: QObject{parent}
{
retry(3);
timeout(15000);
cacheMode(FluHttpType::CacheMode::NoCache);
cacheDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)+"/httpcache");
}
FluHttp::~FluHttp(){
@ -21,7 +33,7 @@ FluHttp::~FluHttp(){
}
void FluHttp::cancel(){
foreach (QPointer<QNetworkReply> item, _cache) {
foreach (QPointer<QNetworkReply> item, _cacheReply) {
if(item){
item->abort();
}
@ -29,13 +41,22 @@ void FluHttp::cancel(){
}
void FluHttp::handleReply(QNetworkReply* reply){
_cache.append(reply);
_cacheReply.append(reply);
}
void FluHttp::post(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"post").toMap();
void FluHttp::post(QString url,HttpCallable* callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QThreadPool::globalInstance()->start([=](){
onStart(callable);
auto requestMap = toRequest(url,params,headers,"post");
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
Q_EMIT callable->start();
if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(requestMap)){
Q_EMIT callable->cache(readCache(requestMap));
Q_EMIT callable->finish();
return;
}
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest){
Q_EMIT callable->cache(readCache(requestMap));
}
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
@ -55,7 +76,7 @@ void FluHttp::post(QString url,QJSValue callable,QMap<QString, QVariant> params,
}
QEventLoop loop;
QNetworkReply* reply = manager.post(request,&multiPart);
_cache.append(reply);
_cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
@ -64,26 +85,40 @@ void FluHttp::post(QString url,QJSValue callable,QMap<QString, QVariant> params,
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
_cacheReply.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
handleCache(requestMap,result);
Q_EMIT callable->success(result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString,result);
if(_cacheMode == FluHttpType::CacheMode::RequestFailedReadCache){
Q_EMIT callable->cache(readCache(requestMap));
}
Q_EMIT callable->error(status,errorString,result);
}
}
}
onFinish(callable);
Q_EMIT callable->finish();
// Q_EMIT callable->finish();
});
}
void FluHttp::postString(QString url,QJSValue callable,QString params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"postString").toMap();
void FluHttp::postString(QString url,HttpCallable* callable,QString params,QMap<QString, QVariant> headers){
QThreadPool::globalInstance()->start([=](){
onStart(callable);
auto requestMap = toRequest(url,params,headers,"postString");
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
Q_EMIT callable->start();
if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(requestMap)){
Q_EMIT callable->cache(readCache(requestMap));
Q_EMIT callable->finish();
return;
}
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest){
Q_EMIT callable->cache(readCache(requestMap));
}
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
@ -94,7 +129,7 @@ void FluHttp::postString(QString url,QJSValue callable,QString params,QMap<QStri
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop;
QNetworkReply* reply = manager.post(request,params.toUtf8());
_cache.append(reply);
_cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
@ -103,26 +138,39 @@ void FluHttp::postString(QString url,QJSValue callable,QString params,QMap<QStri
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
_cacheReply.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
handleCache(requestMap,result);
Q_EMIT callable->success(result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString,result);
if(_cacheMode == FluHttpType::CacheMode::RequestFailedReadCache){
Q_EMIT callable->cache(readCache(requestMap));
}
Q_EMIT callable->error(status,errorString,result);
}
}
}
onFinish(callable);
Q_EMIT callable->finish();
});
}
void FluHttp::postJson(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"postJson").toMap();
void FluHttp::postJson(QString url,HttpCallable* callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QThreadPool::globalInstance()->start([=](){
onStart(callable);
auto requestMap = toRequest(url,params,headers,"postJson");
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
Q_EMIT callable->start();
if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(requestMap)){
Q_EMIT callable->cache(readCache(requestMap));
Q_EMIT callable->finish();
return;
}
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest){
Q_EMIT callable->cache(readCache(requestMap));
}
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
@ -133,7 +181,7 @@ void FluHttp::postJson(QString url,QJSValue callable,QMap<QString, QVariant> par
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop;
QNetworkReply* reply = manager.post(request,QJsonDocument::fromVariant(data["params"]).toJson());
_cache.append(reply);
_cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
@ -142,27 +190,40 @@ void FluHttp::postJson(QString url,QJSValue callable,QMap<QString, QVariant> par
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
_cacheReply.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
handleCache(requestMap,result);
Q_EMIT callable->success(result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString,result);
if(_cacheMode == FluHttpType::CacheMode::RequestFailedReadCache){
Q_EMIT callable->cache(readCache(requestMap));
}
Q_EMIT callable->error(status,errorString,result);
}
}
}
onFinish(callable);
Q_EMIT callable->finish();
});
}
void FluHttp::get(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"get").toMap();
void FluHttp::get(QString url,HttpCallable* callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QThreadPool::globalInstance()->start([=](){
auto requestMap = toRequest(url,params,headers,"get");
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
Q_EMIT callable->start();
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest){
Q_EMIT callable->cache(readCache(requestMap));
}
if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(requestMap)){
Q_EMIT callable->cache(readCache(requestMap));
Q_EMIT callable->finish();
return;
}
for (int i = 0; i < retry(); ++i) {
onStart(callable);
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
@ -171,7 +232,7 @@ void FluHttp::get(QString url,QJSValue callable,QMap<QString, QVariant> params,Q
addHeaders(&request,data["headers"].toMap());
QEventLoop loop;
QNetworkReply* reply = manager.get(request);
_cache.append(reply);
_cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
@ -180,26 +241,31 @@ void FluHttp::get(QString url,QJSValue callable,QMap<QString, QVariant> params,Q
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
_cacheReply.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
handleCache(requestMap,result);
Q_EMIT callable->success(result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString,result);
if(_cacheMode == FluHttpType::CacheMode::RequestFailedReadCache){
Q_EMIT callable->cache(readCache(requestMap));
}
Q_EMIT callable->error(status,errorString,result);
}
}
}
onFinish(callable);
Q_EMIT callable->finish();
});
}
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();
void FluHttp::download(QString url,HttpCallable* callable,QString filePath,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QThreadPool::globalInstance()->start([=](){
onStart(callable);
auto requestMap = toRequest(url,params,headers,"download");
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
Q_EMIT callable->start();
QNetworkAccessManager manager;
QUrl _url(url);
addQueryParam(&_url,data["params"].toMap());
@ -209,8 +275,8 @@ void FluHttp::download(QString url,QJSValue callable,QString filePath,QMap<QStri
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);
Q_EMIT callable->error(-1,QString("Url: %1 %2 Non-Writable").arg(request.url().toString(),file->fileName()),"");
Q_EMIT callable->finish();
return;
}
QEventLoop loop;
@ -218,28 +284,29 @@ void FluHttp::download(QString url,QJSValue callable,QString filePath,QMap<QStri
loop.quit();
});
QPointer<QNetworkReply> reply = manager.get(request);
_cache.append(reply);
_cacheReply.append(reply);
connect(reply,&QNetworkReply::downloadProgress,this,[=](qint64 bytesReceived, qint64 bytesTotal){
onDownloadProgress(callable,bytesReceived,bytesTotal);
Q_EMIT callable->downloadProgress(bytesReceived,bytesTotal);
});
loop.exec();
if (reply->error() == QNetworkReply::NoError) {
file->write(reply->readAll());
onSuccess(callable,filePath);
Q_EMIT callable->success(filePath);
}else{
onError(callable,reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),reply->errorString(),"");
Q_EMIT callable->error(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),reply->errorString(),"");
}
_cache.removeOne(reply);
_cacheReply.removeOne(reply);
reply->deleteLater();
reply = nullptr;
onFinish(callable);
Q_EMIT callable->finish();
});
}
void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"upload").toMap();
void FluHttp::upload(QString url,HttpCallable* callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QThreadPool::globalInstance()->start([=](){
onStart(callable);
auto requestMap = toRequest(url,params,headers,"upload");
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
Q_EMIT callable->start();
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
@ -262,41 +329,46 @@ void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> param
}
QEventLoop loop;
QNetworkReply* reply = manager.post(request,&multiPart);
_cache.append(reply);
_cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
connect(reply,&QNetworkReply::uploadProgress,this,[=](qint64 bytesSent, qint64 bytesTotal){
onUploadProgress(callable,bytesSent,bytesTotal);
Q_EMIT callable->uploadProgress(bytesSent,bytesTotal);
});
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);
_cacheReply.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
Q_EMIT callable->success(result);
}else{
onError(callable,status,errorString,result);
Q_EMIT callable->error(status,errorString,result);
}
onFinish(callable);
Q_EMIT callable->finish();
});
}
QVariant FluHttp::invokeIntercept(const QVariant& params,const QVariant& headers,const QString& method){
QMap<QString, QVariant> requet = {
QMap<QString, QVariant> FluHttp::toRequest(const QString& url,const QVariant& params,const QVariant& headers,const QString& method){
QMap<QString, QVariant> request = {
{"url",url},
{"params",params},
{"headers",headers},
{"method",method}
};
return request;
}
QVariant FluHttp::invokeIntercept(QMap<QString, QVariant> request){
if(!FluApp::getInstance()->httpInterceptor()){
return requet;
return request;
}
QVariant target;
QMetaObject::invokeMethod(FluApp::getInstance()->httpInterceptor(), "onIntercept",Q_RETURN_ARG(QVariant,target),Q_ARG(QVariant, requet));
QMetaObject::invokeMethod(FluApp::getInstance()->httpInterceptor(), "onIntercept",Qt::BlockingQueuedConnection,Q_RETURN_ARG(QVariant,target),Q_ARG(QVariant, request));
return target;
}
@ -320,54 +392,44 @@ void FluHttp::addHeaders(QNetworkRequest* request,const QMap<QString, QVariant>&
}
}
void FluHttp::onStart(const QJSValue& callable){
MainThread::post([=](){
QJSValue onStart = callable.property("onStart");
onStart.call();
});
QString FluHttp::readCache(const QMap<QString, QVariant>& request){
auto filePath = getCacheFilePath(request);
QString result;
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){
MainThread::post([=](){
QJSValue onFinish = callable.property("onFinish");
onFinish.call();
});
bool FluHttp::cacheExists(const QMap<QString, QVariant>& request){
return QFile(getCacheFilePath(request)).exists();
}
void FluHttp::onError(const QJSValue& callable,int status,QString errorString,QString result){
MainThread::post([=](){
QJSValue onError = callable.property("onError");
QJSValueList args;
args<<status<<errorString<<result;
onError.call(args);
});
QString FluHttp::getCacheFilePath(const QMap<QString, QVariant>& request){
auto fileName = FluTools::getInstance()->md5(QJsonDocument::fromVariant(QVariant(request)).toJson());
QDir dir = _cacheDir;
if (!dir.exists(_cacheDir)){
dir.mkpath(_cacheDir);
}
auto filePath = _cacheDir+"/"+fileName;
return filePath;
}
void FluHttp::onSuccess(const QJSValue& callable,QString result){
MainThread::post([=](){
QJSValueList args;
args<<result;
QJSValue onSuccess = callable.property("onSuccess");
onSuccess.call(args);
});
}
void FluHttp::onDownloadProgress(const QJSValue& callable,qint64 recv, qint64 total){
MainThread::post([=](){
QJSValueList args;
args<<static_cast<double>(recv);
args<<static_cast<double>(total);
QJSValue onDownloadProgress = callable.property("onDownloadProgress");
onDownloadProgress.call(args);
});
}
void FluHttp::onUploadProgress(const QJSValue& callable,qint64 sent, qint64 total){
MainThread::post([=](){
QJSValueList args;
args<<static_cast<double>(sent);
args<<static_cast<double>(total);
QJSValue onUploadProgress = callable.property("onUploadProgress");
onUploadProgress.call(args);
});
void FluHttp::handleCache(QMap<QString, QVariant> request,const QString& result){
if(_cacheMode==FluHttpType::CacheMode::NoCache){
return;
}
auto filePath = getCacheFilePath(request);
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());
}

View File

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

View File

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

View File

@ -6,6 +6,8 @@
#include <QScreen>
#include <QColor>
#include <QFileInfo>
#include <QDir>
#include <QCryptographicHash>
#include <QTextDocument>
FluTools* FluTools::m_instance = nullptr;
@ -128,3 +130,23 @@ QUrl FluTools::getUrlByFilePath(const QString& path){
QColor FluTools::colorAlpha(const QColor& color,qreal 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();
}

View File

@ -144,6 +144,35 @@ public:
*/
Q_INVOKABLE QColor colorAlpha(const QColor&,qreal alpha);
/**
* @brief md5
* @param text
* @return
*/
Q_INVOKABLE QString md5(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);
};
#endif // FLUTOOLS_H

View File

@ -752,7 +752,12 @@ Item {
id:nav_stack2
anchors.fill: nav_stack
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(){
return nav_stack

View File

@ -96,24 +96,22 @@ Popup{
ctx.globalCompositeOperation = 'destination-out'
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)
ctx.fillRect(rect.x,rect.y,rect.width,rect.height)
drawRoundedRect(rect,2,ctx)
ctx.restore()
}
//Todo
function drawRoundedRect(rect, r, ctx) {
var ptA = Qt.point(rect.x + r, rect.y)
var ptB = Qt.point(rect.x + rect.width, rect.y)
var ptC = Qt.point(rect.x + rect.width, rect.y + rect.height)
var ptD = Qt.point(rect.x, rect.y + rect.height)
var ptE = Qt.point(rect.x, rect.y)
ctx.beginPath()
ctx.moveTo(ptA.x, ptA.y)
ctx.arcTo(ptB.x, ptB.y, ptC.x, ptC.y, r)
ctx.arcTo(ptC.x, ptC.y, ptD.x, ptD.y, r)
ctx.arcTo(ptD.x, ptD.y, ptE.x, ptE.y, r)
ctx.arcTo(ptE.x, ptE.y, ptA.x, ptA.y, r)
ctx.beginPath();
ctx.moveTo(rect.x + r, rect.y);
ctx.lineTo(rect.x + rect.width - r, rect.y);
ctx.arcTo(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + r, r);
ctx.lineTo(rect.x + rect.width, rect.y + rect.height - r);
ctx.arcTo(rect.x + rect.width, rect.y + rect.height, rect.x + rect.width - r, rect.y + rect.height, r);
ctx.lineTo(rect.x + r, rect.y + rect.height);
ctx.arcTo(rect.x, rect.y + rect.height, rect.x, rect.y + rect.height - r, r);
ctx.lineTo(rect.x, rect.y + r);
ctx.arcTo(rect.x, rect.y, rect.x + r, rect.y, r);
ctx.closePath();
ctx.fill()
ctx.closePath()
}
}
FluArea{

View File

@ -753,7 +753,12 @@ Item {
id:nav_stack2
anchors.fill: nav_stack
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(){
return nav_stack

View File

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

View File

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