mirror of
https://github.com/zhuzichu520/FluentUI.git
synced 2025-07-01 15:42:20 +08:00
Compare commits
61 Commits
Author | SHA1 | Date | |
---|---|---|---|
7a1776407f | |||
f88b330f8e | |||
67ef7f13aa | |||
23ec52ce6a | |||
5b7fdab1d9 | |||
4c1a96c03e | |||
ab4090ea9b | |||
8fb2ef723e | |||
77d9b4bde9 | |||
a96191b2af | |||
28e1799ca4 | |||
8337e278ff | |||
7ad8c969da | |||
66ae37a023 | |||
b27a88d261 | |||
257f3a7b3d | |||
4710379324 | |||
8fc74fe43b | |||
be194e7624 | |||
e6d9de34ea | |||
c47fa5ebc7 | |||
af74f35e43 | |||
3b61985cfe | |||
eb96cf5b47 | |||
d48ad16ae3 | |||
39fb4d1b1a | |||
674de3f881 | |||
0c2b3173eb | |||
b2471bcf0d | |||
79a7c97fe8 | |||
fd30819393 | |||
05040ec4a9 | |||
0297445218 | |||
3990c95489 | |||
618b21854f | |||
ef40e3b109 | |||
f2b67af58a | |||
c0f15060af | |||
24f3cb1027 | |||
4b01fcf2b4 | |||
752fe8cfba | |||
e1292966c1 | |||
249c294926 | |||
573898149a | |||
c92d807ec1 | |||
531bffdf1a | |||
db47a75f6b | |||
ad79480345 | |||
ed5956d824 | |||
ddee70cdca | |||
d6bbe3a5ec | |||
d93d6b7c26 | |||
54be482833 | |||
f60eaec07c | |||
f7b7d30a6f | |||
ba32c92133 | |||
08c4f78454 | |||
e29150ca52 | |||
1be9103412 | |||
8583427a49 | |||
94a96cf75e |
@ -1,5 +1,6 @@
|
||||
include(CMakeParseArguments)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Core)
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
|
||||
|
||||
function(FindQmlPluginDump)
|
||||
get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION)
|
||||
@ -35,7 +36,6 @@ function(add_qmlplugin TARGET)
|
||||
${QMLPLUGIN_SOURCES}
|
||||
)
|
||||
set(LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib)
|
||||
add_custom_target("${TARGET}-qmlfiles" SOURCES ${QMLPLUGIN_QMLFILES})
|
||||
|
||||
if(QMLPLUGIN_NO_AUTORCC)
|
||||
set_target_properties(${TARGET} PROPERTIES AUTOMOC OFF)
|
||||
|
@ -1,5 +1,3 @@
|
||||
// 应用程序版本信息
|
||||
// 请勿修改此头文件,因为这个文件是自动生成的
|
||||
#ifndef VERSION_H
|
||||
#define VERSION_H
|
||||
|
||||
|
9
.github/workflows/windows-mingw.yml
vendored
9
.github/workflows/windows-mingw.yml
vendored
@ -18,9 +18,10 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: windows-2022
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-2022]
|
||||
include:
|
||||
- qt_arch: win64_mingw
|
||||
qt_ver: 6.5.0
|
||||
@ -34,6 +35,11 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup ninja
|
||||
uses: seanmiddleditch/gha-setup-ninja@master
|
||||
with:
|
||||
version: 1.10.2
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v3
|
||||
@ -62,6 +68,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
ninja --version
|
||||
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\FluentUI\Qt\6.5.0\mingw_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
|
||||
cmake --build . --target all --config Release --parallel
|
||||
|
||||
|
85
.github/workflows/windows-qt5.yml
vendored
Normal file
85
.github/workflows/windows-qt5.yml
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
name: Windows Qt5.15.2
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- '*.txt'
|
||||
- 'src/**'
|
||||
- 'example/**'
|
||||
- 'scripts/**'
|
||||
- '.github/workflows/windows_qt5.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- '*.txt'
|
||||
- 'example/**'
|
||||
- 'src/**'
|
||||
- 'scripts/**'
|
||||
- '.github/workflows/windows_qt5.yml'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [windows-2019]
|
||||
include:
|
||||
- qt_ver: 5.15.2
|
||||
qt_arch: win32_msvc2019
|
||||
msvc_arch: x86
|
||||
qt_arch_install: msvc2019
|
||||
env:
|
||||
targetName: example.exe
|
||||
fileName: example
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
version: ${{ matrix.qt_ver }}
|
||||
arch: ${{ matrix.qt_arch }}
|
||||
cache: ${{steps.cache-qt.outputs.cache-hit}}
|
||||
|
||||
- name: msvc-build
|
||||
id: build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.msvc_arch }}
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\FluentUI\Qt\5.15.2\msvc2019 -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -GNinja ..
|
||||
cmake --build . --target all --config Release --parallel
|
||||
echo winSdkDir=%WindowsSdkDir% >> %GITHUB_ENV%
|
||||
echo winSdkVer=%WindowsSdkVersion% >> %GITHUB_ENV%
|
||||
echo vcToolsInstallDir=%VCToolsInstallDir% >> %GITHUB_ENV%
|
||||
echo vcToolsRedistDir=%VCToolsRedistDir% >> %GITHUB_ENV%
|
||||
|
||||
- name: package
|
||||
id: package
|
||||
env:
|
||||
archiveName: ${{ env.fileName }}-${{ matrix.qt_arch }}-${{ matrix.qt_ver }}
|
||||
msvcArch: ${{ matrix.msvc_arch }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
& scripts\windows-publish.ps1 ${env:archiveName} ${env:targetName}
|
||||
# 记录packageName给后续step
|
||||
$name = ${env:archiveName}
|
||||
echo "::set-output name=packageName::$name"
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ steps.package.outputs.packageName }}
|
||||
path: ${{ steps.package.outputs.packageName }}
|
||||
|
||||
- name: uploadRelease
|
||||
if: startsWith(github.event.ref, 'refs/tags/')
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ${{ steps.package.outputs.packageName }}.zip
|
||||
asset_name: ${{ env.fileName }}_${{ github.ref_name }}_${{ matrix.qt_arch }}_Qt${{ matrix.qt_ver }}.zip
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
3
.github/workflows/windows.yml
vendored
3
.github/workflows/windows.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
- qt_ver: 6.5.0
|
||||
qt_arch: win64_msvc2019_64
|
||||
msvc_arch: x64
|
||||
qt_arch_install: msvc2019_64
|
||||
qt_arch_install: msvc2019_64
|
||||
env:
|
||||
targetName: example.exe
|
||||
fileName: example
|
||||
@ -49,6 +49,7 @@ jobs:
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.msvc_arch }}
|
||||
ninja --version
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\FluentUI\Qt\6.5.0\msvc2019_64 -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -GNinja ..
|
||||
|
BIN
3rdparty/Win_x86/mingw/libcrypto-1_1.dll
vendored
Normal file
BIN
3rdparty/Win_x86/mingw/libcrypto-1_1.dll
vendored
Normal file
Binary file not shown.
BIN
3rdparty/Win_x86/mingw/libssl-1_1.dll
vendored
Normal file
BIN
3rdparty/Win_x86/mingw/libssl-1_1.dll
vendored
Normal file
Binary file not shown.
BIN
3rdparty/Win_x86/msvc/libcrypto-1_1.dll
vendored
Normal file
BIN
3rdparty/Win_x86/msvc/libcrypto-1_1.dll
vendored
Normal file
Binary file not shown.
BIN
3rdparty/Win_x86/msvc/libssl-1_1.dll
vendored
Normal file
BIN
3rdparty/Win_x86/msvc/libssl-1_1.dll
vendored
Normal file
Binary file not shown.
@ -262,4 +262,54 @@ zxing-cpp
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
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.
|
||||
limitations under the License.
|
||||
|
||||
************************************************************************************
|
||||
ChartJs2QML
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 ChartJs2QML contributors (starting with Elypson, Michael A. Voelkel, https://github.com/Elypson)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
************************************************************************************
|
||||
qml-colorpicker
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ruslan Shestopalyuk
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -53,7 +53,7 @@ endforeach(filepath)
|
||||
|
||||
if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
|
||||
#遍历所有qml文件
|
||||
file(GLOB_RECURSE QML_PATHS *.qml qmldir)
|
||||
file(GLOB_RECURSE QML_PATHS *.qml)
|
||||
foreach(filepath ${QML_PATHS})
|
||||
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
|
||||
if(${filepath} MATCHES "Qt${QT_VERSION_MAJOR}/")
|
||||
@ -64,7 +64,7 @@ if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
|
||||
endforeach(filepath)
|
||||
|
||||
#遍历所有资源文件
|
||||
file(GLOB_RECURSE RES_PATHS *.png *.jpg *.svg *.ico *.ttf *.webp)
|
||||
file(GLOB_RECURSE RES_PATHS *.png *.jpg *.svg *.ico *.ttf *.webp qmldir)
|
||||
foreach(filepath ${RES_PATHS})
|
||||
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
|
||||
list(APPEND resource_files ${filename})
|
||||
@ -93,12 +93,17 @@ else ()
|
||||
)
|
||||
endif ()
|
||||
|
||||
#复制动态库到可执行文件同级目录下
|
||||
if(WIN32)
|
||||
#复制动态库到可执行文件同级目录下
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||
set(3RDPARTY_ARCH_DIR ${CMAKE_SOURCE_DIR}/3rdparty/Win_x86)
|
||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(3RDPARTY_ARCH_DIR ${CMAKE_SOURCE_DIR}/3rdparty/Win_x64)
|
||||
endif()
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set(DLLPATH ${CMAKE_SOURCE_DIR}/3rdparty/msvc/*.dll)
|
||||
set(DLLPATH ${3RDPARTY_ARCH_DIR}/msvc/*.dll)
|
||||
else()
|
||||
set(DLLPATH ${CMAKE_SOURCE_DIR}/3rdparty/mingw/*.dll)
|
||||
set(DLLPATH ${3RDPARTY_ARCH_DIR}/mingw/*.dll)
|
||||
endif()
|
||||
string(REPLACE "/" ${PATH_SEPARATOR} DLLPATH "${DLLPATH}")
|
||||
file(GLOB DLL_FILES ${DLLPATH})
|
||||
@ -122,7 +127,7 @@ else()
|
||||
target_include_directories(example PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
target_sources(example PRIVATE resource.qrc)
|
||||
target_sources(example PRIVATE example.qrc)
|
||||
endif()
|
||||
|
||||
#导入component头文件,不然通过QML_NAMED_ELEMENT生成的c++类会找不到头文件报错
|
||||
@ -130,6 +135,13 @@ target_include_directories(example PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/component
|
||||
)
|
||||
|
||||
#如何是静态库则需要手动注册插件,导入FluentUI.h头文件
|
||||
if(FLUENTUI_BUILD_STATIC_LIB)
|
||||
target_include_directories(example PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
)
|
||||
endif()
|
||||
|
||||
#设置属性
|
||||
set_target_properties(example PROPERTIES
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
|
||||
@ -139,27 +151,14 @@ set_target_properties(example PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
)
|
||||
|
||||
#链接库
|
||||
if (FLUENTUI_BUILD_STATIC_LIB)
|
||||
target_link_libraries(example PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Quick
|
||||
Qt${QT_VERSION_MAJOR}::Svg
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
fluentui
|
||||
fluentuiplugin
|
||||
FramelessHelper::Core
|
||||
FramelessHelper::Quick
|
||||
)
|
||||
else()
|
||||
target_link_libraries(example PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Quick
|
||||
Qt${QT_VERSION_MAJOR}::Svg
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
fluentuiplugin
|
||||
FramelessHelper::Core
|
||||
FramelessHelper::Quick
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(example PRIVATE
|
||||
Qt${QT_VERSION_MAJOR}::Quick
|
||||
Qt${QT_VERSION_MAJOR}::Svg
|
||||
Qt${QT_VERSION_MAJOR}::Network
|
||||
fluentuiplugin
|
||||
FramelessHelper::Core
|
||||
FramelessHelper::Quick
|
||||
)
|
||||
|
||||
#安装
|
||||
install(TARGETS example
|
||||
|
@ -126,7 +126,6 @@
|
||||
<file>qml/component/CustomWindow.qml</file>
|
||||
<file>qml/global/ItemsFooter.qml</file>
|
||||
<file>qml/global/ItemsOriginal.qml</file>
|
||||
<file>qml/global/MainEvent.qml</file>
|
||||
<file>qml/global/qmldir</file>
|
||||
<file>qml/page/T_Acrylic.qml</file>
|
||||
<file>qml/page/T_Awesome.qml</file>
|
||||
@ -186,5 +185,7 @@
|
||||
<file>res/image/image_1.jpg</file>
|
||||
<file>qml/window/PageWindow.qml</file>
|
||||
<file>qml/page/T_StaggeredView.qml</file>
|
||||
<file>qml/viewmodel/SettingsViewModel.qml</file>
|
||||
<file>qml/viewmodel/TextBoxViewModel.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -8,6 +8,13 @@ Window {
|
||||
id: app
|
||||
flags: Qt.SplashScreen
|
||||
|
||||
Connections{
|
||||
target: FluTheme
|
||||
function onDarkModeChanged(){
|
||||
SettingsHelper.saveDarkMode(FluTheme.darkMode)
|
||||
}
|
||||
}
|
||||
|
||||
FluHttpInterceptor{
|
||||
id:interceptor
|
||||
function onIntercept(request){
|
||||
@ -26,7 +33,7 @@ Window {
|
||||
|
||||
Component.onCompleted: {
|
||||
FluApp.init(app)
|
||||
FluTheme.darkMode = FluThemeType.System
|
||||
FluTheme.darkMode = SettingsHelper.getDarkMode()
|
||||
FluTheme.enableAnimation = true
|
||||
FluApp.routes = {
|
||||
"/":"qrc:/example/qml/window/MainWindow.qml",
|
||||
|
@ -80,7 +80,6 @@ FluExpander{
|
||||
"FluIcon",
|
||||
"FluIconButton",
|
||||
"FluInfoBar",
|
||||
"FluItem",
|
||||
"FluMediaPlayer",
|
||||
"FluMenu",
|
||||
"FluMenuItem",
|
||||
@ -138,7 +137,9 @@ FluExpander{
|
||||
"FluTimeline",
|
||||
"FluChart",
|
||||
"FluRangeSlider",
|
||||
"FluStaggeredView"
|
||||
"FluStaggeredView",
|
||||
"FluProgressButton",
|
||||
"FluLoadingButton"
|
||||
];
|
||||
code = code.replace(/\n/g, "<br>");
|
||||
code = code.replace(/ /g, " ");
|
||||
|
@ -88,7 +88,10 @@ FluObject{
|
||||
}
|
||||
url:"qrc:/example/qml/page/T_Text.qml"
|
||||
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
|
||||
onTap:{ navigationView.push(url) }
|
||||
onTap:{
|
||||
item_text.count = 0
|
||||
navigationView.push(url)
|
||||
}
|
||||
}
|
||||
FluPaneItem{
|
||||
title:"Image"
|
||||
@ -394,7 +397,7 @@ FluObject{
|
||||
onTap:{ navigationView.push(url) }
|
||||
}
|
||||
FluPaneItem{
|
||||
title:"Screenshot"
|
||||
title:"Screenshot(Todo)"
|
||||
url:"qrc:/example/qml/page/T_Screenshot.qml"
|
||||
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
|
||||
onTap:{ navigationView.push(url) }
|
||||
@ -474,6 +477,9 @@ FluObject{
|
||||
}
|
||||
|
||||
function getSearchData(){
|
||||
if(!navigationView){
|
||||
return
|
||||
}
|
||||
var arr = []
|
||||
var items = navigationView.getItems();
|
||||
for(var i=0;i<items.length;i++){
|
||||
|
@ -1,9 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import FluentUI
|
||||
|
||||
QtObject {
|
||||
property int displayMode : FluNavigationViewType.Auto
|
||||
}
|
@ -1,3 +1,2 @@
|
||||
singleton ItemsOriginal 1.0 ItemsOriginal.qml
|
||||
singleton ItemsFooter 1.0 ItemsFooter.qml
|
||||
singleton MainEvent 1.0 MainEvent.qml
|
||||
|
@ -47,10 +47,10 @@ FluScrollablePage{
|
||||
height: 1200/4+20
|
||||
paddings: 10
|
||||
Layout.topMargin: 10
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 1920/4
|
||||
height: 1200/4
|
||||
radius:[15,15,15,15]
|
||||
radius:[8,8,8,8]
|
||||
Image {
|
||||
id:image
|
||||
asynchronous: true
|
||||
|
@ -161,6 +161,97 @@ FluScrollablePage{
|
||||
}'
|
||||
}
|
||||
|
||||
Timer{
|
||||
id:timer_progress
|
||||
interval: 200
|
||||
onTriggered: {
|
||||
btn_progress.progress = (btn_progress.progress + 0.1).toFixed(1)
|
||||
if(btn_progress.progress==1){
|
||||
timer_progress.stop()
|
||||
}else{
|
||||
timer_progress.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
height: 68
|
||||
Layout.topMargin: 20
|
||||
paddings: 10
|
||||
|
||||
FluProgressButton{
|
||||
id:btn_progress
|
||||
disabled:progress_button_switch.checked
|
||||
text:"Progress Button"
|
||||
anchors{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
onClicked: {
|
||||
btn_progress.progress = 0
|
||||
timer_progress.restart()
|
||||
}
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:progress_button_switch
|
||||
anchors{
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
text:"Disabled"
|
||||
}
|
||||
}
|
||||
CodeExpander{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -1
|
||||
code:'FluProgressButton{
|
||||
text:"Progress Button"
|
||||
onClicked: {
|
||||
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
height: 68
|
||||
Layout.topMargin: 20
|
||||
paddings: 10
|
||||
|
||||
FluLoadingButton{
|
||||
id:btn_loading
|
||||
loading:loading_button_switch.checked
|
||||
text:"Loading Button"
|
||||
anchors{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
onClicked: {
|
||||
|
||||
}
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:loading_button_switch
|
||||
checked: true
|
||||
anchors{
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
text:"Loading"
|
||||
}
|
||||
}
|
||||
CodeExpander{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -1
|
||||
code:'FluLoadingButton{
|
||||
text:"Loading Button"
|
||||
onClicked: {
|
||||
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
|
@ -9,10 +9,10 @@ FluScrollablePage{
|
||||
|
||||
title:"Captcha"
|
||||
|
||||
|
||||
FluCaptcha{
|
||||
id:captcha
|
||||
Layout.topMargin: 20
|
||||
ignoreCase:switch_case.checked
|
||||
MouseArea{
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
@ -30,6 +30,13 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
|
||||
FluToggleSwitch{
|
||||
id:switch_case
|
||||
text:"Ignore Case"
|
||||
checked: true
|
||||
Layout.topMargin: 10
|
||||
}
|
||||
|
||||
RowLayout{
|
||||
spacing: 10
|
||||
Layout.topMargin: 10
|
||||
@ -49,6 +56,4 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -36,20 +36,27 @@ FluScrollablePage{
|
||||
FluText{
|
||||
text:"轮播图,支持无限轮播,无限滑动,用ListView实现的组件"
|
||||
}
|
||||
FluCarousel{
|
||||
radius:[5,5,5,5]
|
||||
delegate: Component{
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: model.url
|
||||
asynchronous: true
|
||||
fillMode:Image.PreserveAspectCrop
|
||||
}
|
||||
Item{
|
||||
width: 400
|
||||
height: 300
|
||||
FluShadow{
|
||||
radius: 8
|
||||
}
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 5
|
||||
Component.onCompleted: {
|
||||
model = [{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}]
|
||||
FluCarousel{
|
||||
anchors.fill: parent
|
||||
delegate: Component{
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: model.url
|
||||
asynchronous: true
|
||||
fillMode:Image.PreserveAspectCrop
|
||||
}
|
||||
}
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 5
|
||||
Component.onCompleted: {
|
||||
model = [{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,46 +73,54 @@ FluScrollablePage{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left:parent.left
|
||||
}
|
||||
FluCarousel{
|
||||
radius:[15,15,15,15]
|
||||
loopTime:1500
|
||||
indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
|
||||
indicatorMarginTop:15
|
||||
delegate: Component{
|
||||
Item{
|
||||
anchors.fill: parent
|
||||
Image {
|
||||
Item{
|
||||
width: 400
|
||||
height: 300
|
||||
FluShadow{
|
||||
radius: 8
|
||||
}
|
||||
FluCarousel{
|
||||
anchors.fill: parent
|
||||
loopTime:1500
|
||||
indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
|
||||
indicatorMarginTop:15
|
||||
delegate: Component{
|
||||
Item{
|
||||
anchors.fill: parent
|
||||
source: model.url
|
||||
asynchronous: true
|
||||
fillMode:Image.PreserveAspectCrop
|
||||
}
|
||||
Rectangle{
|
||||
height: 40
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
color: "#33000000"
|
||||
FluText{
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text:model.title
|
||||
color: FluColors.Grey10
|
||||
font.pixelSize: 15
|
||||
source: model.url
|
||||
asynchronous: true
|
||||
fillMode:Image.PreserveAspectCrop
|
||||
}
|
||||
Rectangle{
|
||||
height: 40
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
color: "#33000000"
|
||||
FluText{
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text:model.title
|
||||
color: FluColors.Grey10
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 5
|
||||
Component.onCompleted: {
|
||||
var arr = []
|
||||
arr.push({url:"qrc:/example/res/image/banner_1.jpg",title:"共同应对全球性问题"})
|
||||
arr.push({url:"qrc:/example/res/image/banner_2.jpg",title:"三小只全程没互动"})
|
||||
arr.push({url:"qrc:/example/res/image/banner_3.jpg",title:"有效投资扩大 激发增长动能"})
|
||||
model = arr
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 5
|
||||
Component.onCompleted: {
|
||||
var arr = []
|
||||
arr.push({url:"qrc:/example/res/image/banner_1.jpg",title:"共同应对全球性问题"})
|
||||
arr.push({url:"qrc:/example/res/image/banner_2.jpg",title:"三小只全程没互动"})
|
||||
arr.push({url:"qrc:/example/res/image/banner_3.jpg",title:"有效投资扩大 激发增长动能"})
|
||||
model = arr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,6 @@ FluScrollablePage{
|
||||
}
|
||||
FluComboBox {
|
||||
editable: true
|
||||
font:FluTextStyle.BodyStrong
|
||||
model: ListModel {
|
||||
id: model_2
|
||||
ListElement { text: "Banana" }
|
||||
|
@ -27,7 +27,9 @@ FluScrollablePage{
|
||||
id: bg
|
||||
fillMode:Image.PreserveAspectCrop
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
verticalAlignment: Qt.AlignTop
|
||||
sourceSize: Qt.size(960,640)
|
||||
source: "qrc:/example/res/image/bg_home_header.png"
|
||||
}
|
||||
Rectangle{
|
||||
@ -48,33 +50,19 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
|
||||
ListView{
|
||||
id: list
|
||||
anchors{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
orientation: ListView.Horizontal
|
||||
height: 240
|
||||
model: model_header
|
||||
header: Item{height: 10;width: 10}
|
||||
footer: Item{height: 10;width: 10}
|
||||
ScrollBar.horizontal: FluScrollBar{
|
||||
id: scrollbar_header
|
||||
}
|
||||
clip: false
|
||||
delegate:Item{
|
||||
Component{
|
||||
id:com_grallery
|
||||
Item{
|
||||
id: control
|
||||
width: 220
|
||||
height: 240
|
||||
FluShadow{
|
||||
radius:8
|
||||
radius:5
|
||||
anchors.fill: item_content
|
||||
}
|
||||
FluItem{
|
||||
FluClip{
|
||||
id:item_content
|
||||
radius: [8,8,8,8]
|
||||
radius: [5,5,5,5]
|
||||
width: 200
|
||||
height: 220
|
||||
anchors.centerIn: parent
|
||||
@ -88,20 +76,15 @@ FluScrollablePage{
|
||||
}
|
||||
Rectangle{
|
||||
anchors.fill: parent
|
||||
radius: 8
|
||||
color:{
|
||||
if(FluTheme.dark){
|
||||
if(item_mouse.containsMouse){
|
||||
return Qt.rgba(1,1,1,0.03)
|
||||
}
|
||||
return Qt.rgba(0,0,0,0.0)
|
||||
}else{
|
||||
if(item_mouse.containsMouse){
|
||||
return Qt.rgba(0,0,0,0.03)
|
||||
}
|
||||
return Qt.rgba(0,0,0,0.0)
|
||||
}
|
||||
}
|
||||
radius: 5
|
||||
color:FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
|
||||
visible: item_mouse.containsMouse
|
||||
}
|
||||
Rectangle{
|
||||
anchors.fill: parent
|
||||
radius: 5
|
||||
color:Qt.rgba(0,0,0,0.0)
|
||||
visible: !item_mouse.containsMouse
|
||||
}
|
||||
ColumnLayout{
|
||||
Image {
|
||||
@ -153,11 +136,31 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView{
|
||||
id: list
|
||||
anchors{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
orientation: ListView.Horizontal
|
||||
height: 240
|
||||
model: model_header
|
||||
header: Item{height: 10;width: 10}
|
||||
footer: Item{height: 10;width: 10}
|
||||
ScrollBar.horizontal: FluScrollBar{
|
||||
id: scrollbar_header
|
||||
}
|
||||
clip: false
|
||||
delegate: com_grallery
|
||||
}
|
||||
}
|
||||
|
||||
Component{
|
||||
id:com_item
|
||||
Item{
|
||||
property string desc: modelData.desc
|
||||
width: 320
|
||||
height: 120
|
||||
FluArea{
|
||||
@ -193,7 +196,6 @@ FluScrollablePage{
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
FluText{
|
||||
id:item_title
|
||||
text:modelData.title
|
||||
@ -204,10 +206,9 @@ FluScrollablePage{
|
||||
top: item_icon.top
|
||||
}
|
||||
}
|
||||
|
||||
FluText{
|
||||
id:item_desc
|
||||
text:modelData.desc
|
||||
text:desc
|
||||
color:FluColors.Grey120
|
||||
wrapMode: Text.WrapAnywhere
|
||||
elide: Text.ElideRight
|
||||
|
@ -10,14 +10,63 @@ import "qrc:///example/qml/component"
|
||||
FluContentPage{
|
||||
|
||||
title:"Http"
|
||||
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
|
||||
property bool isDownCompleted: false
|
||||
|
||||
FluHttp{
|
||||
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{
|
||||
id:layout_flick
|
||||
width: 160
|
||||
width: 200
|
||||
clip: true
|
||||
anchors{
|
||||
top: parent.top
|
||||
@ -36,21 +85,8 @@ 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)
|
||||
var request = http.newRequest("https://httpbingo.org/get")
|
||||
http.get(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
@ -58,25 +94,13 @@ 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"
|
||||
param.custemail = "zhuzichu520@gmail.com"
|
||||
http.post("https://httpbingo.org/post",callable,param)
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
var params = {}
|
||||
params.custname = "朱子楚"
|
||||
params.custtel = "1234567890"
|
||||
params.custemail = "zhuzichu520@gmail.com"
|
||||
request.params = params
|
||||
http.post(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
@ -84,25 +108,13 @@ 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"
|
||||
param.custemail = "zhuzichu520@gmail.com"
|
||||
http.postJson("https://httpbingo.org/post",callable,param)
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
var params = {}
|
||||
params.custname = "朱子楚"
|
||||
params.custtel = "1234567890"
|
||||
params.custemail = "zhuzichu520@gmail.com"
|
||||
request.params = params
|
||||
http.postJson(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
@ -110,25 +122,12 @@ 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)
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
request.params = "我命由我不由天"
|
||||
http.postString(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
FluProgressButton{
|
||||
id:btn_download
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
@ -137,7 +136,90 @@ FluContentPage{
|
||||
folder_dialog.open()
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
FluProgressButton{
|
||||
property bool downloading: false
|
||||
id:btn_breakpoint_download
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: {
|
||||
if(downloading){
|
||||
return "暂停下载"
|
||||
}
|
||||
if(progress === 0){
|
||||
return "断点下载文件"
|
||||
}else if(progress === 1){
|
||||
return "打开文件"
|
||||
}else{
|
||||
return "继续下载"
|
||||
}
|
||||
}
|
||||
HttpRequest{
|
||||
id:request_breakpoint_download
|
||||
url: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
|
||||
downloadSavePath: FluTools.getApplicationDirPath()+ "/download/big_buck_bunny.mp4"
|
||||
}
|
||||
HttpCallable{
|
||||
id:callable_breakpoint_download
|
||||
onStart: {
|
||||
btn_breakpoint_download.downloading = true
|
||||
}
|
||||
onFinish: {
|
||||
btn_breakpoint_download.downloading = false
|
||||
}
|
||||
onError:
|
||||
(status,errorString,result)=>{
|
||||
console.debug(status+";"+errorString+";"+result)
|
||||
}
|
||||
onSuccess:
|
||||
(result)=>{
|
||||
if(!isDownCompleted){
|
||||
tour.open()
|
||||
isDownCompleted = true
|
||||
}
|
||||
showSuccess(result)
|
||||
}
|
||||
onDownloadProgress:
|
||||
(recv,total)=>{
|
||||
btn_breakpoint_download.progress = recv/total
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
progress = http_breakpoint_download.getBreakPointProgress(request_breakpoint_download)
|
||||
}
|
||||
onClicked: {
|
||||
if(downloading){
|
||||
http_breakpoint_download.cancel()
|
||||
return
|
||||
}
|
||||
if(progress === 1){
|
||||
FluTools.showFileInFolder(request_breakpoint_download.downloadSavePath)
|
||||
}else{
|
||||
http_breakpoint_download.download(request_breakpoint_download,callable_breakpoint_download)
|
||||
}
|
||||
}
|
||||
FluMenu{
|
||||
id:menu_breakpoint_download
|
||||
width: 120
|
||||
FluMenuItem{
|
||||
text: "删除文件"
|
||||
onClicked: {
|
||||
if(FluTools.removeFile(request_breakpoint_download.downloadSavePath)){
|
||||
btn_breakpoint_download.progress = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: {
|
||||
if(btn_breakpoint_download.progress === 1){
|
||||
menu_breakpoint_download.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FluProgressButton{
|
||||
id:btn_upload
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
@ -146,81 +228,143 @@ FluContentPage{
|
||||
file_dialog.open()
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "FirstCacheThenRequest缓存"
|
||||
onClicked: {
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
request.params = {cacheMode:"FirstCacheThenRequest"}
|
||||
http_cache_firstcachethenrequest.post(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "RequestFailedReadCache缓存"
|
||||
onClicked: {
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
request.params = {cacheMode:"RequestFailedReadCache"}
|
||||
http_cache_requestfailedreadcache.post(request,callable)
|
||||
}
|
||||
}
|
||||
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "IfNoneCacheRequest缓存"
|
||||
onClicked: {
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
request.params = {cacheMode:"IfNoneCacheRequest"}
|
||||
http_cache_ifnonecacherequest.post(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "打开缓存路径"
|
||||
onClicked: {
|
||||
Qt.openUrlExternally("file:///"+cacheDirPath)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "删除缓存"
|
||||
onClicked: {
|
||||
console.debug(FluTools.removeDir(cacheDirPath))
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "清空右边数据"
|
||||
onClicked: {
|
||||
text_info.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluTour{
|
||||
id:tour
|
||||
steps:[
|
||||
{title:"友情提示",description: "下载已完成,左击这里可以打开文件所在路径,右击可以弹出菜单删除文件!",target:()=>btn_breakpoint_download}
|
||||
]
|
||||
}
|
||||
|
||||
HttpCallable{
|
||||
id:callable_upload
|
||||
onStart: {
|
||||
btn_upload.disabled = true
|
||||
}
|
||||
onFinish: {
|
||||
btn_upload.disabled = false
|
||||
}
|
||||
onError:
|
||||
(status,errorString,result)=>{
|
||||
btn_upload.progress = 0
|
||||
text_info.text = result
|
||||
console.debug(result)
|
||||
}
|
||||
onSuccess:
|
||||
(result)=>{
|
||||
text_info.text = result
|
||||
}
|
||||
onUploadProgress:
|
||||
(sent,total)=>{
|
||||
btn_upload.progress = sent/total
|
||||
}
|
||||
}
|
||||
FileDialog {
|
||||
id: file_dialog
|
||||
onAccepted: {
|
||||
var param = {}
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
var params = {}
|
||||
for(var i=0;i<selectedFiles.length;i++){
|
||||
var fileUrl = selectedFiles[i]
|
||||
var fileName = FluTools.getFileNameByUrl(fileUrl)
|
||||
var filePath = FluTools.toLocalPath(fileUrl)
|
||||
param[fileName] = filePath
|
||||
params[fileName] = filePath
|
||||
}
|
||||
console.debug(JSON.stringify(param))
|
||||
var callable = {}
|
||||
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)
|
||||
request.params = params
|
||||
http.upload(request,callable_upload)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
var request = http.newRequest("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
|
||||
request.downloadSavePath = FluTools.toLocalPath(currentFolder)+ "/big_buck_bunny.mp4"
|
||||
http.download(request,callable_download)
|
||||
}
|
||||
}
|
||||
|
||||
FluArea{
|
||||
anchors{
|
||||
top: layout_flick.top
|
||||
@ -246,34 +390,4 @@ FluContentPage{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluRectangle{
|
||||
id:layout_download_file_size
|
||||
radius: [4,4,4,4]
|
||||
height: 36
|
||||
width: 160
|
||||
visible: false
|
||||
x:layout_flick.width
|
||||
y: 173 - layout_flick.contentY
|
||||
FluText{
|
||||
id:text_download_file_size
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FluRectangle{
|
||||
id:layout_upload_file_size
|
||||
radius: [4,4,4,4]
|
||||
height: 36
|
||||
width: 160
|
||||
visible: false
|
||||
x:layout_flick.width
|
||||
y: 210 - layout_flick.contentY
|
||||
FluText{
|
||||
id:text_upload_file_size
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ FluScrollablePage{
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
height: 240
|
||||
height: 270
|
||||
paddings: 10
|
||||
ColumnLayout{
|
||||
spacing: 14
|
||||
@ -44,6 +44,12 @@ FluScrollablePage{
|
||||
showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar")
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
text:"手动关闭的InfoBar"
|
||||
onClicked: {
|
||||
showInfo("这是一个Info样式的InfoBar",0,"支持手动关闭")
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
text:"Loading"
|
||||
onClicked: {
|
||||
|
@ -3,7 +3,6 @@ import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import FluentUI
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import "qrc:///example/qml/component"
|
||||
|
||||
FluScrollablePage{
|
||||
|
@ -68,7 +68,7 @@ FluScrollablePage{
|
||||
}
|
||||
RowLayout{
|
||||
spacing: 14
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 50
|
||||
height: 50
|
||||
radius:[25,0,25,25]
|
||||
@ -79,7 +79,7 @@ FluScrollablePage{
|
||||
sourceSize: Qt.size(width,height)
|
||||
}
|
||||
}
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 50
|
||||
height: 50
|
||||
radius:[10,10,10,10]
|
||||
@ -90,7 +90,7 @@ FluScrollablePage{
|
||||
source: "qrc:/example/res/svg/avatar_2.svg"
|
||||
}
|
||||
}
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 50
|
||||
height: 50
|
||||
radius:[25,25,25,25]
|
||||
@ -101,7 +101,7 @@ FluScrollablePage{
|
||||
source: "qrc:/example/res/svg/avatar_3.svg"
|
||||
}
|
||||
}
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 50
|
||||
height: 50
|
||||
radius:[0,25,25,25]
|
||||
@ -113,10 +113,10 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
}
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 1920/5
|
||||
height: 1200/5
|
||||
radius:[15,15,15,15]
|
||||
radius:[8,8,8,8]
|
||||
Image {
|
||||
asynchronous: true
|
||||
source: "qrc:/example/res/image/banner_1.jpg"
|
||||
|
@ -30,9 +30,10 @@ FluScrollablePage{
|
||||
Layout.topMargin: 10
|
||||
Layout.leftMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
radius: 4
|
||||
color: FluTheme.dark ? FluColors.Black : FluColors.White
|
||||
FluShadow{
|
||||
radius: 0
|
||||
radius: 4
|
||||
color: FluTheme.primaryColor.dark
|
||||
}
|
||||
Image{
|
||||
|
@ -5,11 +5,90 @@ import QtQuick.Controls
|
||||
import FluentUI
|
||||
import "qrc:///example/qml/global"
|
||||
import "qrc:///example/qml/component"
|
||||
import "qrc:///example/qml/viewmodel"
|
||||
|
||||
FluScrollablePage{
|
||||
|
||||
title:"Settings"
|
||||
|
||||
SettingsViewModel{
|
||||
id:viewmodel_settings
|
||||
}
|
||||
|
||||
FluEvent{
|
||||
id:event_checkupdate_finish
|
||||
name: "checkUpdateFinish"
|
||||
onTriggered: {
|
||||
btn_checkupdate.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
FluEventBus.registerEvent(event_checkupdate_finish)
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
FluEventBus.unRegisterEvent(event_checkupdate_finish)
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
height: 60
|
||||
paddings: 10
|
||||
Row{
|
||||
spacing: 20
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
FluText{
|
||||
text:"当前版本 v%1".arg(AppInfo.version)
|
||||
font: FluTextStyle.Body
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
FluLoadingButton{
|
||||
id:btn_checkupdate
|
||||
text:"检查更新"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: {
|
||||
loading = true
|
||||
FluEventBus.post("checkUpdate")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
height: 50
|
||||
paddings: 10
|
||||
FluCheckBox{
|
||||
text:"Software Render"
|
||||
checked: SettingsHelper.getReander() === "software"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: {
|
||||
if(SettingsHelper.getReander() === "software"){
|
||||
SettingsHelper.saveRender("")
|
||||
}else{
|
||||
SettingsHelper.saveRender("software")
|
||||
}
|
||||
dialog_render.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluContentDialog{
|
||||
id:dialog_render
|
||||
title:"友情提示"
|
||||
message:"此操作需要重启才能生效,是否重新启动?"
|
||||
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
|
||||
negativeText: "取消"
|
||||
positiveText:"确定"
|
||||
onPositiveClicked:{
|
||||
window.deleteWindow()
|
||||
AppInfo.restart()
|
||||
}
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
@ -60,10 +139,10 @@ FluScrollablePage{
|
||||
Repeater{
|
||||
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
|
||||
delegate: FluRadioButton{
|
||||
checked : MainEvent.displayMode===modelData.mode
|
||||
checked : viewmodel_settings.displayMode===modelData.mode
|
||||
text:modelData.title
|
||||
clickListener:function(){
|
||||
MainEvent.displayMode = modelData.mode
|
||||
viewmodel_settings.displayMode = modelData.mode
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,10 +173,10 @@ FluScrollablePage{
|
||||
Repeater{
|
||||
model: ["Zh","En"]
|
||||
delegate: FluRadioButton{
|
||||
checked: appInfo.lang.objectName === modelData
|
||||
checked: AppInfo.lang.objectName === modelData
|
||||
text:modelData
|
||||
clickListener:function(){
|
||||
appInfo.changeLang(modelData)
|
||||
AppInfo.changeLang(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,19 @@ import QtQuick.Layouts
|
||||
import QtQuick.Window
|
||||
import FluentUI
|
||||
import "qrc:///example/qml/component"
|
||||
import "qrc:///example/qml/viewmodel"
|
||||
|
||||
FluScrollablePage{
|
||||
|
||||
launchMode: FluPageType.SingleInstance
|
||||
|
||||
title:"TextBox"
|
||||
|
||||
TextBoxViewModel{
|
||||
id:viewModel
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
console.debug("T_TextBox页面销毁了")
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
height: 68
|
||||
@ -20,6 +27,10 @@ FluScrollablePage{
|
||||
placeholderText: "单行输入框"
|
||||
disabled:text_box_switch.checked
|
||||
cleanEnabled: true
|
||||
text:viewModel.text1
|
||||
onTextChanged: {
|
||||
viewModel.text1 = text
|
||||
}
|
||||
anchors{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
@ -84,6 +95,10 @@ FluScrollablePage{
|
||||
FluMultilineTextBox{
|
||||
id:multiine_textbox
|
||||
placeholderText: "多行输入框"
|
||||
text:viewModel.text2
|
||||
onTextChanged: {
|
||||
viewModel.text2 = text
|
||||
}
|
||||
disabled:text_box_multi_switch.checked
|
||||
anchors{
|
||||
verticalCenter: parent.verticalCenter
|
||||
|
@ -5,152 +5,127 @@ import QtQuick.Controls
|
||||
import FluentUI
|
||||
import "qrc:///example/qml/component"
|
||||
|
||||
FluScrollablePage {
|
||||
FluContentPage {
|
||||
|
||||
title:"TreeView"
|
||||
|
||||
function randomName() {
|
||||
var names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十"]
|
||||
return names[Math.floor(Math.random() * names.length)]
|
||||
function treeData(){
|
||||
const dig = (path = '0', level = 4) => {
|
||||
const list = [];
|
||||
for (let i = 0; i < 6; i += 1) {
|
||||
const key = `${path}-${i}`;
|
||||
const treeNode = {
|
||||
title: key,
|
||||
key,
|
||||
};
|
||||
if (level > 0) {
|
||||
treeNode.children = dig(key, level - 1);
|
||||
}
|
||||
list.push(treeNode);
|
||||
}
|
||||
return list;
|
||||
};
|
||||
return dig();
|
||||
}
|
||||
|
||||
function randomCompany() {
|
||||
var companies = ["阿里巴巴", "腾讯", "百度", "京东", "华为", "小米", "字节跳动", "美团", "滴滴"]
|
||||
return companies[Math.floor(Math.random() * companies.length)]
|
||||
}
|
||||
|
||||
function randomDepartment() {
|
||||
var departments = ["技术部", "销售部", "市场部", "人事部", "财务部", "客服部", "产品部", "设计部", "运营部"]
|
||||
return departments[Math.floor(Math.random() * departments.length)]
|
||||
}
|
||||
|
||||
function createEmployee() {
|
||||
var name = randomName()
|
||||
return tree_view.createItem(name, false)
|
||||
}
|
||||
|
||||
function createSubtree(numEmployees) {
|
||||
var employees = []
|
||||
for (var i = 0; i < numEmployees; i++) {
|
||||
employees.push(createEmployee())
|
||||
Column{
|
||||
id:layout_column
|
||||
spacing: 12
|
||||
width: 300
|
||||
anchors{
|
||||
topMargin: 20
|
||||
top:parent.top
|
||||
left: parent.left
|
||||
leftMargin: 10
|
||||
bottom:parent.bottom
|
||||
bottomMargin: 20
|
||||
}
|
||||
return tree_view.createItem(randomDepartment(), true, employees)
|
||||
}
|
||||
|
||||
function createOrg(numLevels, numSubtrees, numEmployees) {
|
||||
if (numLevels === 0) {
|
||||
return []
|
||||
FluText{
|
||||
text:"共计%1条数据,当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount())
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"共计选中%1条数据".arg(tree_view.selectionModel().length)
|
||||
}
|
||||
var subtrees = []
|
||||
for (var i = 0; i < numSubtrees; i++) {
|
||||
subtrees.push(createSubtree(numEmployees))
|
||||
}
|
||||
return [tree_view.createItem(randomCompany(), true, subtrees)].concat(createOrg(numLevels - 1, numSubtrees, numEmployees))
|
||||
}
|
||||
|
||||
FluArea{
|
||||
id:layout_actions
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
height: 50
|
||||
paddings: 10
|
||||
RowLayout{
|
||||
spacing: 14
|
||||
FluDropDownButton{
|
||||
id:btn_selection_model
|
||||
Layout.preferredWidth: 140
|
||||
text:"None"
|
||||
FluMenuItem{
|
||||
text:"None"
|
||||
onClicked: {
|
||||
btn_selection_model.text = text
|
||||
tree_view.selectionMode = FluTabViewType.Equal
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Single"
|
||||
onClicked: {
|
||||
btn_selection_model.text = text
|
||||
tree_view.selectionMode = FluTabViewType.SizeToContent
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Muiltple"
|
||||
onClicked: {
|
||||
btn_selection_model.text = text
|
||||
tree_view.selectionMode = FluTabViewType.Compact
|
||||
}
|
||||
}
|
||||
spacing: 10
|
||||
FluText{
|
||||
text:"cellHeight:"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
FluFilledButton{
|
||||
text:"获取选中的数据"
|
||||
onClicked: {
|
||||
if(tree_view.selectionMode === FluTreeViewType.None){
|
||||
showError("当前非选择模式,没有选中的数据")
|
||||
}
|
||||
if(tree_view.selectionMode === FluTreeViewType.Single){
|
||||
if(!tree_view.signleData()){
|
||||
showError("没有选中数据")
|
||||
return
|
||||
}
|
||||
showSuccess(tree_view.signleData().text)
|
||||
}
|
||||
if(tree_view.selectionMode === FluTreeViewType.Multiple){
|
||||
if(tree_view.multipData().length===0){
|
||||
showError("没有选中数据")
|
||||
return
|
||||
}
|
||||
var info = []
|
||||
tree_view.multipData().map((value)=>info.push(value.text))
|
||||
showSuccess(info.join(","))
|
||||
}
|
||||
}
|
||||
FluSlider{
|
||||
id:slider_cell_height
|
||||
value: 30
|
||||
from: 30
|
||||
to:100
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
spacing: 10
|
||||
FluText{
|
||||
text:"depthPadding:"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
FluSlider{
|
||||
id:slider_depth_padding
|
||||
value: 30
|
||||
from: 30
|
||||
to:100
|
||||
}
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:switch_showline
|
||||
text:"showLine"
|
||||
checked: false
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:switch_draggable
|
||||
text:"draggable"
|
||||
checked: false
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:switch_checkable
|
||||
text:"checkable"
|
||||
checked: false
|
||||
}
|
||||
FluButton{
|
||||
text:"all expand"
|
||||
onClicked: {
|
||||
tree_view.allExpand()
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
text:"all collapse"
|
||||
onClicked: {
|
||||
tree_view.allCollapse()
|
||||
}
|
||||
}
|
||||
}
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
paddings: 10
|
||||
height: 400
|
||||
anchors{
|
||||
left: layout_column.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
rightMargin: 5
|
||||
topMargin: 5
|
||||
bottomMargin: 5
|
||||
}
|
||||
FluShadow{}
|
||||
FluTreeView{
|
||||
id:tree_view
|
||||
width:240
|
||||
anchors{
|
||||
top:parent.top
|
||||
left:parent.left
|
||||
bottom:parent.bottom
|
||||
}
|
||||
onItemClicked:
|
||||
(model)=>{
|
||||
showSuccess(model.text)
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
cellHeight: slider_cell_height.value
|
||||
draggable:switch_draggable.checked
|
||||
showLine: switch_showline.checked
|
||||
checkable:switch_checkable.checked
|
||||
depthPadding: slider_depth_padding.value
|
||||
Component.onCompleted: {
|
||||
var org = createOrg(3, 3, 3)
|
||||
createItem()
|
||||
updateData(org)
|
||||
var data = treeData()
|
||||
dataSource = data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeExpander{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -1
|
||||
code:'FluTreeView{
|
||||
id:tree_view
|
||||
width:240
|
||||
height:600
|
||||
Component.onCompleted: {
|
||||
var datas = []
|
||||
datas.push(createItem("Node1",false))
|
||||
datas.push(createItem("Node2",false))
|
||||
datas.push(createItem("Node2",true,[createItem("Node2-1",false),createItem("Node2-2",false)]))
|
||||
updateData(datas)
|
||||
}
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
13
example/qml-Qt6/viewmodel/SettingsViewModel.qml
Normal file
13
example/qml-Qt6/viewmodel/SettingsViewModel.qml
Normal file
@ -0,0 +1,13 @@
|
||||
import QtQuick
|
||||
import FluentUI
|
||||
|
||||
FluViewModel{
|
||||
|
||||
objectName: "SettingsViewModel"
|
||||
property int displayMode
|
||||
|
||||
onInitData: {
|
||||
displayMode = FluNavigationViewType.Auto
|
||||
}
|
||||
|
||||
}
|
8
example/qml-Qt6/viewmodel/TextBoxViewModel.qml
Normal file
8
example/qml-Qt6/viewmodel/TextBoxViewModel.qml
Normal file
@ -0,0 +1,8 @@
|
||||
import QtQuick
|
||||
import FluentUI
|
||||
|
||||
FluViewModel {
|
||||
objectName: "TextBoxView"
|
||||
property string text1
|
||||
property string text2
|
||||
}
|
@ -35,7 +35,7 @@ CustomWindow {
|
||||
}
|
||||
}
|
||||
FluText{
|
||||
text:"v%1".arg(appInfo.version)
|
||||
text:"v%1".arg(AppInfo.version)
|
||||
font: FluTextStyle.Body
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import FluentUI
|
||||
import example
|
||||
import "qrc:///example/qml/component"
|
||||
import "qrc:///example/qml/global"
|
||||
import "qrc:///example/qml/viewmodel"
|
||||
|
||||
CustomWindow {
|
||||
|
||||
@ -20,15 +21,32 @@ CustomWindow {
|
||||
appBarVisible: false
|
||||
launchMode: FluWindowType.SingleTask
|
||||
|
||||
SettingsViewModel{
|
||||
id:viewmodel_settings
|
||||
}
|
||||
|
||||
closeFunc:function(event){
|
||||
dialog_close.open()
|
||||
event.accepted = false
|
||||
}
|
||||
|
||||
FluEvent{
|
||||
id:event_checkupdate
|
||||
name: "checkUpdate"
|
||||
onTriggered: {
|
||||
checkUpdate(false)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
FluTools.setQuitOnLastWindowClosed(false)
|
||||
tour.open()
|
||||
checkUpdate()
|
||||
checkUpdate(true)
|
||||
FluEventBus.registerEvent(event_checkupdate)
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
FluEventBus.unRegisterEvent(event_checkupdate)
|
||||
}
|
||||
|
||||
SystemTrayIcon {
|
||||
@ -138,7 +156,7 @@ CustomWindow {
|
||||
id:loader
|
||||
lazy: true
|
||||
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{
|
||||
@ -164,13 +182,13 @@ CustomWindow {
|
||||
height: parent.height
|
||||
z:999
|
||||
//Stack模式,每次切换都会将页面压入栈中,随着栈的页面增多,消耗的内存也越多,内存消耗多就会卡顿,这时候就需要按返回将页面pop掉,释放内存。该模式可以配合FluPage中的launchMode属性,设置页面的启动模式
|
||||
pageMode: FluNavigationViewType.Stack
|
||||
//NoStack模式,每次切换都会销毁之前的页面然后创建一个新的页面,只需消耗少量内存(推荐)
|
||||
// pageMode: FluNavigationViewType.NoStack
|
||||
// pageMode: FluNavigationViewType.Stack
|
||||
//NoStack模式,每次切换都会销毁之前的页面然后创建一个新的页面,只需消耗少量内存,可以配合FluViewModel保存页面数据(推荐)
|
||||
pageMode: FluNavigationViewType.NoStack
|
||||
items: ItemsOriginal
|
||||
footerItems:ItemsFooter
|
||||
topPadding:FluTools.isMacos() ? 20 : 0
|
||||
displayMode:MainEvent.displayMode
|
||||
displayMode:viewmodel_settings.displayMode
|
||||
logo: "qrc:/example/res/image/favicon.ico"
|
||||
title:"FluentUI"
|
||||
onLogoClicked:{
|
||||
@ -300,7 +318,7 @@ CustomWindow {
|
||||
property string body
|
||||
id:dialog_update
|
||||
title:"升级提示"
|
||||
message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+appInfo.version+" \n现在是否去下载新版本?\n\n更新内容:\n"+body
|
||||
message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+AppInfo.version+" \n现在是否去下载新版本?\n\n更新内容:\n"+body
|
||||
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
|
||||
negativeText: "取消"
|
||||
positiveText:"确定"
|
||||
@ -309,28 +327,44 @@ CustomWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate(){
|
||||
var callable = {}
|
||||
callable.onStart = function(){
|
||||
HttpCallable{
|
||||
id:callable
|
||||
property bool silent: true
|
||||
onStart: {
|
||||
console.debug("satrt check update...")
|
||||
}
|
||||
callable.onFinish = function(){
|
||||
onFinish: {
|
||||
console.debug("check update finish")
|
||||
FluEventBus.post("checkUpdateFinish");
|
||||
}
|
||||
callable.onSuccess = function(result){
|
||||
var data = JSON.parse(result)
|
||||
console.debug("current version "+appInfo.version)
|
||||
console.debug("new version "+data.tag_name)
|
||||
if(data.tag_name !== appInfo.version){
|
||||
dialog_update.newVerson = data.tag_name
|
||||
dialog_update.body = data.body
|
||||
dialog_update.open()
|
||||
onSuccess:
|
||||
(result)=>{
|
||||
var data = JSON.parse(result)
|
||||
console.debug("current version "+AppInfo.version)
|
||||
console.debug("new version "+data.tag_name)
|
||||
if(data.tag_name !== AppInfo.version){
|
||||
dialog_update.newVerson = data.tag_name
|
||||
dialog_update.body = data.body
|
||||
dialog_update.open()
|
||||
}else{
|
||||
if(!silent){
|
||||
showInfo("当前版本已经是最新版")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
callable.onError = function(status,errorString){
|
||||
console.debug(status+";"+errorString)
|
||||
}
|
||||
http.get("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest",callable)
|
||||
onError:
|
||||
(status,errorString)=>{
|
||||
if(!silent){
|
||||
showError("网络异常!")
|
||||
}
|
||||
console.debug(status+";"+errorString)
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate(silent){
|
||||
callable.silent = silent
|
||||
var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest")
|
||||
http.get(request,callable);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,13 @@ Window {
|
||||
id: app
|
||||
flags: Qt.SplashScreen
|
||||
|
||||
Connections{
|
||||
target: FluTheme
|
||||
function onDarkModeChanged(){
|
||||
SettingsHelper.saveDarkMode(FluTheme.darkMode)
|
||||
}
|
||||
}
|
||||
|
||||
FluHttpInterceptor{
|
||||
id:interceptor
|
||||
function onIntercept(request){
|
||||
@ -26,7 +33,7 @@ Window {
|
||||
|
||||
Component.onCompleted: {
|
||||
FluApp.init(app)
|
||||
FluTheme.darkMode = FluThemeType.System
|
||||
FluTheme.darkMode = SettingsHelper.getDarkMode()
|
||||
FluTheme.enableAnimation = true
|
||||
FluApp.routes = {
|
||||
"/":"qrc:/example/qml/window/MainWindow.qml",
|
||||
|
@ -80,7 +80,6 @@ FluExpander{
|
||||
"FluIcon",
|
||||
"FluIconButton",
|
||||
"FluInfoBar",
|
||||
"FluItem",
|
||||
"FluMediaPlayer",
|
||||
"FluMenu",
|
||||
"FluMenuItem",
|
||||
@ -138,7 +137,9 @@ FluExpander{
|
||||
"FluTimeline",
|
||||
"FluChart",
|
||||
"FluRangeSlider",
|
||||
"FluStaggeredView"
|
||||
"FluStaggeredView",
|
||||
"FluProgressButton",
|
||||
"FluLoadingButton"
|
||||
];
|
||||
code = code.replace(/\n/g, "<br>");
|
||||
code = code.replace(/ /g, " ");
|
||||
|
@ -88,7 +88,10 @@ FluObject{
|
||||
}
|
||||
url:"qrc:/example/qml/page/T_Text.qml"
|
||||
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
|
||||
onTap:{ navigationView.push(url) }
|
||||
onTap:{
|
||||
item_text.count = 0
|
||||
navigationView.push(url)
|
||||
}
|
||||
}
|
||||
FluPaneItem{
|
||||
title:"Image"
|
||||
@ -394,7 +397,7 @@ FluObject{
|
||||
onTap:{ navigationView.push(url) }
|
||||
}
|
||||
FluPaneItem{
|
||||
title:"Screenshot"
|
||||
title:"Screenshot(Todo)"
|
||||
url:"qrc:/example/qml/page/T_Screenshot.qml"
|
||||
onDropped:{ FluApp.navigate("/pageWindow",{title:title,url:url}) }
|
||||
onTap:{ navigationView.push(url) }
|
||||
@ -474,6 +477,9 @@ FluObject{
|
||||
}
|
||||
|
||||
function getSearchData(){
|
||||
if(!navigationView){
|
||||
return
|
||||
}
|
||||
var arr = []
|
||||
var items = navigationView.getItems();
|
||||
for(var i=0;i<items.length;i++){
|
||||
|
@ -1,9 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import FluentUI 1.0
|
||||
|
||||
QtObject {
|
||||
property int displayMode : FluNavigationViewType.Auto
|
||||
}
|
@ -1,3 +1,2 @@
|
||||
singleton ItemsOriginal 1.0 ItemsOriginal.qml
|
||||
singleton ItemsFooter 1.0 ItemsFooter.qml
|
||||
singleton MainEvent 1.0 MainEvent.qml
|
||||
|
@ -48,10 +48,10 @@ FluScrollablePage{
|
||||
height: 1200/4+20
|
||||
paddings: 10
|
||||
Layout.topMargin: 10
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 1920/4
|
||||
height: 1200/4
|
||||
radius:[15,15,15,15]
|
||||
radius:[8,8,8,8]
|
||||
Image {
|
||||
id:image
|
||||
asynchronous: true
|
||||
|
@ -161,6 +161,97 @@ FluScrollablePage{
|
||||
}'
|
||||
}
|
||||
|
||||
Timer{
|
||||
id:timer_progress
|
||||
interval: 200
|
||||
onTriggered: {
|
||||
btn_progress.progress = (btn_progress.progress + 0.1).toFixed(1)
|
||||
if(btn_progress.progress==1){
|
||||
timer_progress.stop()
|
||||
}else{
|
||||
timer_progress.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
height: 68
|
||||
Layout.topMargin: 20
|
||||
paddings: 10
|
||||
|
||||
FluProgressButton{
|
||||
id:btn_progress
|
||||
disabled:progress_button_switch.checked
|
||||
text:"Progress Button"
|
||||
anchors{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
onClicked: {
|
||||
btn_progress.progress = 0
|
||||
timer_progress.restart()
|
||||
}
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:progress_button_switch
|
||||
anchors{
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
text:"Disabled"
|
||||
}
|
||||
}
|
||||
CodeExpander{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -1
|
||||
code:'FluProgressButton{
|
||||
text:"Progress Button"
|
||||
onClicked: {
|
||||
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
height: 68
|
||||
Layout.topMargin: 20
|
||||
paddings: 10
|
||||
|
||||
FluLoadingButton{
|
||||
id:btn_loading
|
||||
loading:loading_button_switch.checked
|
||||
text:"Loading Button"
|
||||
anchors{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
onClicked: {
|
||||
|
||||
}
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:loading_button_switch
|
||||
checked: true
|
||||
anchors{
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
text:"Loading"
|
||||
}
|
||||
}
|
||||
CodeExpander{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -1
|
||||
code:'FluLoadingButton{
|
||||
text:"Loading Button"
|
||||
onClicked: {
|
||||
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
|
@ -10,10 +10,10 @@ FluScrollablePage{
|
||||
|
||||
title:"Captcha"
|
||||
|
||||
|
||||
FluCaptcha{
|
||||
id:captcha
|
||||
Layout.topMargin: 20
|
||||
ignoreCase:switch_case.checked
|
||||
MouseArea{
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
@ -31,6 +31,13 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
|
||||
FluToggleSwitch{
|
||||
id:switch_case
|
||||
text:"Ignore Case"
|
||||
checked: true
|
||||
Layout.topMargin: 10
|
||||
}
|
||||
|
||||
RowLayout{
|
||||
spacing: 10
|
||||
Layout.topMargin: 10
|
||||
@ -50,6 +57,4 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -37,20 +37,27 @@ FluScrollablePage{
|
||||
FluText{
|
||||
text:"轮播图,支持无限轮播,无限滑动,用ListView实现的组件"
|
||||
}
|
||||
FluCarousel{
|
||||
radius:[5,5,5,5]
|
||||
delegate: Component{
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: model.url
|
||||
asynchronous: true
|
||||
fillMode:Image.PreserveAspectCrop
|
||||
}
|
||||
Item{
|
||||
width: 400
|
||||
height: 300
|
||||
FluShadow{
|
||||
radius: 8
|
||||
}
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 5
|
||||
Component.onCompleted: {
|
||||
model = [{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}]
|
||||
FluCarousel{
|
||||
anchors.fill: parent
|
||||
delegate: Component{
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: model.url
|
||||
asynchronous: true
|
||||
fillMode:Image.PreserveAspectCrop
|
||||
}
|
||||
}
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 5
|
||||
Component.onCompleted: {
|
||||
model = [{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,46 +74,54 @@ FluScrollablePage{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left:parent.left
|
||||
}
|
||||
FluCarousel{
|
||||
radius:[15,15,15,15]
|
||||
loopTime:1500
|
||||
indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
|
||||
indicatorMarginTop:15
|
||||
delegate: Component{
|
||||
Item{
|
||||
anchors.fill: parent
|
||||
Image {
|
||||
Item{
|
||||
width: 400
|
||||
height: 300
|
||||
FluShadow{
|
||||
radius: 8
|
||||
}
|
||||
FluCarousel{
|
||||
anchors.fill: parent
|
||||
loopTime:1500
|
||||
indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
|
||||
indicatorMarginTop:15
|
||||
delegate: Component{
|
||||
Item{
|
||||
anchors.fill: parent
|
||||
source: model.url
|
||||
asynchronous: true
|
||||
fillMode:Image.PreserveAspectCrop
|
||||
}
|
||||
Rectangle{
|
||||
height: 40
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
color: "#33000000"
|
||||
FluText{
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text:model.title
|
||||
color: FluColors.Grey10
|
||||
font.pixelSize: 15
|
||||
source: model.url
|
||||
asynchronous: true
|
||||
fillMode:Image.PreserveAspectCrop
|
||||
}
|
||||
Rectangle{
|
||||
height: 40
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
color: "#33000000"
|
||||
FluText{
|
||||
anchors.fill: parent
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text:model.title
|
||||
color: FluColors.Grey10
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 5
|
||||
Component.onCompleted: {
|
||||
var arr = []
|
||||
arr.push({url:"qrc:/example/res/image/banner_1.jpg",title:"共同应对全球性问题"})
|
||||
arr.push({url:"qrc:/example/res/image/banner_2.jpg",title:"三小只全程没互动"})
|
||||
arr.push({url:"qrc:/example/res/image/banner_3.jpg",title:"有效投资扩大 激发增长动能"})
|
||||
model = arr
|
||||
Layout.topMargin: 20
|
||||
Layout.leftMargin: 5
|
||||
Component.onCompleted: {
|
||||
var arr = []
|
||||
arr.push({url:"qrc:/example/res/image/banner_1.jpg",title:"共同应对全球性问题"})
|
||||
arr.push({url:"qrc:/example/res/image/banner_2.jpg",title:"三小只全程没互动"})
|
||||
arr.push({url:"qrc:/example/res/image/banner_3.jpg",title:"有效投资扩大 激发增长动能"})
|
||||
model = arr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,6 @@ FluScrollablePage{
|
||||
}
|
||||
FluComboBox {
|
||||
editable: true
|
||||
font:FluTextStyle.BodyStrong
|
||||
model: ListModel {
|
||||
id: model_2
|
||||
ListElement { text: "Banana" }
|
||||
|
@ -2,6 +2,7 @@ import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Window 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtGraphicalEffects 1.0
|
||||
import "qrc:///example/qml/global"
|
||||
import FluentUI 1.0
|
||||
|
||||
@ -29,6 +30,7 @@ FluScrollablePage{
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
verticalAlignment: Qt.AlignTop
|
||||
sourceSize: Qt.size(960,640)
|
||||
source: "qrc:/example/res/image/bg_home_header.png"
|
||||
}
|
||||
Rectangle{
|
||||
@ -49,33 +51,19 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
|
||||
ListView{
|
||||
id: list
|
||||
anchors{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
orientation: ListView.Horizontal
|
||||
height: 240
|
||||
model: model_header
|
||||
header: Item{height: 10;width: 10}
|
||||
footer: Item{height: 10;width: 10}
|
||||
ScrollBar.horizontal: FluScrollBar{
|
||||
id: scrollbar_header
|
||||
}
|
||||
clip: false
|
||||
delegate:Item{
|
||||
Component{
|
||||
id:com_grallery
|
||||
Item{
|
||||
id: control
|
||||
width: 220
|
||||
height: 240
|
||||
FluShadow{
|
||||
radius:8
|
||||
radius:5
|
||||
anchors.fill: item_content
|
||||
}
|
||||
FluItem{
|
||||
FluClip{
|
||||
id:item_content
|
||||
radius: [8,8,8,8]
|
||||
radius: [5,5,5,5]
|
||||
width: 200
|
||||
height: 220
|
||||
anchors.centerIn: parent
|
||||
@ -89,20 +77,15 @@ FluScrollablePage{
|
||||
}
|
||||
Rectangle{
|
||||
anchors.fill: parent
|
||||
radius: 8
|
||||
color:{
|
||||
if(FluTheme.dark){
|
||||
if(item_mouse.containsMouse){
|
||||
return Qt.rgba(1,1,1,0.03)
|
||||
}
|
||||
return Qt.rgba(0,0,0,0.0)
|
||||
}else{
|
||||
if(item_mouse.containsMouse){
|
||||
return Qt.rgba(0,0,0,0.03)
|
||||
}
|
||||
return Qt.rgba(0,0,0,0.0)
|
||||
}
|
||||
}
|
||||
radius: 5
|
||||
color:FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
|
||||
visible: item_mouse.containsMouse
|
||||
}
|
||||
Rectangle{
|
||||
anchors.fill: parent
|
||||
radius: 5
|
||||
color:Qt.rgba(0,0,0,0.0)
|
||||
visible: !item_mouse.containsMouse
|
||||
}
|
||||
ColumnLayout{
|
||||
Image {
|
||||
@ -154,11 +137,31 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView{
|
||||
id: list
|
||||
anchors{
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
orientation: ListView.Horizontal
|
||||
height: 240
|
||||
model: model_header
|
||||
header: Item{height: 10;width: 10}
|
||||
footer: Item{height: 10;width: 10}
|
||||
ScrollBar.horizontal: FluScrollBar{
|
||||
id: scrollbar_header
|
||||
}
|
||||
clip: false
|
||||
delegate: com_grallery
|
||||
}
|
||||
}
|
||||
|
||||
Component{
|
||||
id:com_item
|
||||
Item{
|
||||
property string desc: modelData.desc
|
||||
width: 320
|
||||
height: 120
|
||||
FluArea{
|
||||
@ -194,7 +197,6 @@ FluScrollablePage{
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
FluText{
|
||||
id:item_title
|
||||
text:modelData.title
|
||||
@ -205,10 +207,9 @@ FluScrollablePage{
|
||||
top: item_icon.top
|
||||
}
|
||||
}
|
||||
|
||||
FluText{
|
||||
id:item_desc
|
||||
text:modelData.desc
|
||||
text:desc
|
||||
color:FluColors.Grey120
|
||||
wrapMode: Text.WrapAnywhere
|
||||
elide: Text.ElideRight
|
||||
|
@ -11,14 +11,63 @@ import "../component"
|
||||
FluContentPage{
|
||||
|
||||
title:"Http"
|
||||
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
|
||||
property bool isDownCompleted: false
|
||||
|
||||
FluHttp{
|
||||
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{
|
||||
id:layout_flick
|
||||
width: 160
|
||||
width: 200
|
||||
clip: true
|
||||
anchors{
|
||||
top: parent.top
|
||||
@ -37,21 +86,8 @@ 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)
|
||||
var request = http.newRequest("https://httpbingo.org/get")
|
||||
http.get(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
@ -59,25 +95,13 @@ 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"
|
||||
param.custemail = "zhuzichu520@gmail.com"
|
||||
http.post("https://httpbingo.org/post",callable,param)
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
var params = {}
|
||||
params.custname = "朱子楚"
|
||||
params.custtel = "1234567890"
|
||||
params.custemail = "zhuzichu520@gmail.com"
|
||||
request.params = params
|
||||
http.post(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
@ -85,25 +109,13 @@ 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"
|
||||
param.custemail = "zhuzichu520@gmail.com"
|
||||
http.postJson("https://httpbingo.org/post",callable,param)
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
var params = {}
|
||||
params.custname = "朱子楚"
|
||||
params.custtel = "1234567890"
|
||||
params.custemail = "zhuzichu520@gmail.com"
|
||||
request.params = params
|
||||
http.postJson(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
@ -111,25 +123,12 @@ 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)
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
request.params = "我命由我不由天"
|
||||
http.postString(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
FluProgressButton{
|
||||
id:btn_download
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
@ -138,7 +137,90 @@ FluContentPage{
|
||||
folder_dialog.open()
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
FluProgressButton{
|
||||
property bool downloading: false
|
||||
id:btn_breakpoint_download
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: {
|
||||
if(downloading){
|
||||
return "暂停下载"
|
||||
}
|
||||
if(progress === 0){
|
||||
return "断点下载文件"
|
||||
}else if(progress === 1){
|
||||
return "打开文件"
|
||||
}else{
|
||||
return "继续下载"
|
||||
}
|
||||
}
|
||||
HttpRequest{
|
||||
id:request_breakpoint_download
|
||||
url: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
|
||||
downloadSavePath: FluTools.getApplicationDirPath()+ "/download/big_buck_bunny.mp4"
|
||||
}
|
||||
HttpCallable{
|
||||
id:callable_breakpoint_download
|
||||
onStart: {
|
||||
btn_breakpoint_download.downloading = true
|
||||
}
|
||||
onFinish: {
|
||||
btn_breakpoint_download.downloading = false
|
||||
}
|
||||
onError:
|
||||
(status,errorString,result)=>{
|
||||
console.debug(status+";"+errorString+";"+result)
|
||||
}
|
||||
onSuccess:
|
||||
(result)=>{
|
||||
if(!isDownCompleted){
|
||||
tour.open()
|
||||
isDownCompleted = true
|
||||
}
|
||||
showSuccess(result)
|
||||
}
|
||||
onDownloadProgress:
|
||||
(recv,total)=>{
|
||||
btn_breakpoint_download.progress = recv/total
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
progress = http_breakpoint_download.getBreakPointProgress(request_breakpoint_download)
|
||||
}
|
||||
onClicked: {
|
||||
if(downloading){
|
||||
http_breakpoint_download.cancel()
|
||||
return
|
||||
}
|
||||
if(progress === 1){
|
||||
FluTools.showFileInFolder(request_breakpoint_download.downloadSavePath)
|
||||
}else{
|
||||
http_breakpoint_download.download(request_breakpoint_download,callable_breakpoint_download)
|
||||
}
|
||||
}
|
||||
FluMenu{
|
||||
id:menu_breakpoint_download
|
||||
width: 120
|
||||
FluMenuItem{
|
||||
text: "删除文件"
|
||||
onClicked: {
|
||||
if(FluTools.removeFile(request_breakpoint_download.downloadSavePath)){
|
||||
btn_breakpoint_download.progress = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton
|
||||
onClicked: {
|
||||
if(btn_breakpoint_download.progress === 1){
|
||||
menu_breakpoint_download.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FluProgressButton{
|
||||
id:btn_upload
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
@ -147,81 +229,143 @@ FluContentPage{
|
||||
file_dialog.open()
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "FirstCacheThenRequest缓存"
|
||||
onClicked: {
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
request.params = {cacheMode:"FirstCacheThenRequest"}
|
||||
http_cache_firstcachethenrequest.post(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "RequestFailedReadCache缓存"
|
||||
onClicked: {
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
request.params = {cacheMode:"RequestFailedReadCache"}
|
||||
http_cache_requestfailedreadcache.post(request,callable)
|
||||
}
|
||||
}
|
||||
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "IfNoneCacheRequest缓存"
|
||||
onClicked: {
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
request.params = {cacheMode:"IfNoneCacheRequest"}
|
||||
http_cache_ifnonecacherequest.post(request,callable)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "打开缓存路径"
|
||||
onClicked: {
|
||||
Qt.openUrlExternally("file:///"+cacheDirPath)
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "删除缓存"
|
||||
onClicked: {
|
||||
console.debug(FluTools.removeDir(cacheDirPath))
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 36
|
||||
text: "清空右边数据"
|
||||
onClicked: {
|
||||
text_info.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluTour{
|
||||
id:tour
|
||||
steps:[
|
||||
{title:"友情提示",description: "下载已完成,左击这里可以打开文件所在路径,右击可以弹出菜单删除文件!",target:()=>btn_breakpoint_download}
|
||||
]
|
||||
}
|
||||
|
||||
HttpCallable{
|
||||
id:callable_upload
|
||||
onStart: {
|
||||
btn_upload.disabled = true
|
||||
}
|
||||
onFinish: {
|
||||
btn_upload.disabled = false
|
||||
}
|
||||
onError:
|
||||
(status,errorString,result)=>{
|
||||
btn_upload.progress = 0
|
||||
text_info.text = result
|
||||
console.debug(result)
|
||||
}
|
||||
onSuccess:
|
||||
(result)=>{
|
||||
text_info.text = result
|
||||
}
|
||||
onUploadProgress:
|
||||
(sent,total)=>{
|
||||
btn_upload.progress = sent/total
|
||||
}
|
||||
}
|
||||
FileDialog {
|
||||
id: file_dialog
|
||||
onAccepted: {
|
||||
var param = {}
|
||||
var request = http.newRequest("https://httpbingo.org/post")
|
||||
var params = {}
|
||||
for(var i=0;i<selectedFiles.length;i++){
|
||||
var fileUrl = selectedFiles[i]
|
||||
var fileName = FluTools.getFileNameByUrl(fileUrl)
|
||||
var filePath = FluTools.toLocalPath(fileUrl)
|
||||
param[fileName] = filePath
|
||||
params[fileName] = filePath
|
||||
}
|
||||
console.debug(JSON.stringify(param))
|
||||
var callable = {}
|
||||
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)
|
||||
request.params = params
|
||||
http.upload(request,callable_upload)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
var request = http.newRequest("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")
|
||||
request.downloadSavePath = FluTools.toLocalPath(currentFolder)+ "/big_buck_bunny.mp4"
|
||||
http.download(request,callable_download)
|
||||
}
|
||||
}
|
||||
|
||||
FluArea{
|
||||
anchors{
|
||||
top: layout_flick.top
|
||||
@ -247,34 +391,4 @@ FluContentPage{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluRectangle{
|
||||
id:layout_download_file_size
|
||||
radius: [4,4,4,4]
|
||||
height: 36
|
||||
width: 160
|
||||
visible: false
|
||||
x:layout_flick.width
|
||||
y: 173 - layout_flick.contentY
|
||||
FluText{
|
||||
id:text_download_file_size
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FluRectangle{
|
||||
id:layout_upload_file_size
|
||||
radius: [4,4,4,4]
|
||||
height: 36
|
||||
width: 160
|
||||
visible: false
|
||||
x:layout_flick.width
|
||||
y: 210 - layout_flick.contentY
|
||||
FluText{
|
||||
id:text_upload_file_size
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ FluScrollablePage{
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
height: 240
|
||||
height: 270
|
||||
paddings: 10
|
||||
ColumnLayout{
|
||||
spacing: 14
|
||||
@ -45,6 +45,12 @@ FluScrollablePage{
|
||||
showSuccess("这是一个Success样式的InfoBar这是一个Success样式的InfoBar")
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
text:"手动关闭的InfoBar"
|
||||
onClicked: {
|
||||
showInfo("这是一个Info样式的InfoBar",0,"支持手动关闭")
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
text:"Loading"
|
||||
onClicked: {
|
||||
|
@ -16,9 +16,12 @@ FluScrollablePage{
|
||||
height: 400
|
||||
paddings: 10
|
||||
|
||||
|
||||
|
||||
FluPivot{
|
||||
anchors.fill: parent
|
||||
currentIndex: 2
|
||||
|
||||
FluPivotItem{
|
||||
title:"All"
|
||||
contentItem:FluText{
|
||||
|
@ -3,7 +3,6 @@ import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtQuick.Window 2.15
|
||||
import FluentUI 1.0
|
||||
import QtGraphicalEffects 1.15
|
||||
import "qrc:///example/qml/component"
|
||||
import "../component"
|
||||
|
||||
|
@ -69,7 +69,7 @@ FluScrollablePage{
|
||||
}
|
||||
RowLayout{
|
||||
spacing: 14
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 50
|
||||
height: 50
|
||||
radius:[25,0,25,25]
|
||||
@ -80,7 +80,7 @@ FluScrollablePage{
|
||||
sourceSize: Qt.size(width,height)
|
||||
}
|
||||
}
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 50
|
||||
height: 50
|
||||
radius:[10,10,10,10]
|
||||
@ -91,7 +91,7 @@ FluScrollablePage{
|
||||
source: "qrc:/example/res/svg/avatar_2.svg"
|
||||
}
|
||||
}
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 50
|
||||
height: 50
|
||||
radius:[25,25,25,25]
|
||||
@ -102,7 +102,7 @@ FluScrollablePage{
|
||||
source: "qrc:/example/res/svg/avatar_3.svg"
|
||||
}
|
||||
}
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 50
|
||||
height: 50
|
||||
radius:[0,25,25,25]
|
||||
@ -114,10 +114,10 @@ FluScrollablePage{
|
||||
}
|
||||
}
|
||||
}
|
||||
FluRectangle{
|
||||
FluClip{
|
||||
width: 1920/5
|
||||
height: 1200/5
|
||||
radius:[15,15,15,15]
|
||||
radius:[8,8,8,8]
|
||||
Image {
|
||||
asynchronous: true
|
||||
source: "qrc:/example/res/image/banner_1.jpg"
|
||||
|
@ -31,9 +31,10 @@ FluScrollablePage{
|
||||
Layout.topMargin: 10
|
||||
Layout.leftMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
radius: 4
|
||||
color: FluTheme.dark ? FluColors.Black : FluColors.White
|
||||
FluShadow{
|
||||
radius: 0
|
||||
radius: 4
|
||||
color: FluTheme.primaryColor.dark
|
||||
}
|
||||
Image{
|
||||
|
@ -5,12 +5,93 @@ import QtQuick.Controls 2.15
|
||||
import FluentUI 1.0
|
||||
import "qrc:///example/qml/global"
|
||||
import "qrc:///example/qml/component"
|
||||
import "qrc:///example/qml/viewmodel"
|
||||
import "../component"
|
||||
import "../viewmodel"
|
||||
import "../global"
|
||||
|
||||
FluScrollablePage{
|
||||
|
||||
title:"Settings"
|
||||
|
||||
SettingsViewModel{
|
||||
id:viewmodel_settings
|
||||
}
|
||||
|
||||
FluEvent{
|
||||
id:event_checkupdate_finish
|
||||
name: "checkUpdateFinish"
|
||||
onTriggered: {
|
||||
btn_checkupdate.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
FluEventBus.registerEvent(event_checkupdate_finish)
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
FluEventBus.unRegisterEvent(event_checkupdate_finish)
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
height: 60
|
||||
paddings: 10
|
||||
Row{
|
||||
spacing: 20
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
FluText{
|
||||
text:"当前版本 v%1".arg(AppInfo.version)
|
||||
font: FluTextStyle.Body
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
FluLoadingButton{
|
||||
id:btn_checkupdate
|
||||
text:"检查更新"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: {
|
||||
loading = true
|
||||
FluEventBus.post("checkUpdate")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
height: 50
|
||||
paddings: 10
|
||||
FluCheckBox{
|
||||
text:"Software Render"
|
||||
checked: SettingsHelper.getReander() === "software"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: {
|
||||
if(SettingsHelper.getReander() === "software"){
|
||||
SettingsHelper.saveRender("")
|
||||
}else{
|
||||
SettingsHelper.saveRender("software")
|
||||
}
|
||||
dialog_render.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluContentDialog{
|
||||
id:dialog_render
|
||||
title:"友情提示"
|
||||
message:"此操作需要重启才能生效,是否重新启动?"
|
||||
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
|
||||
negativeText: "取消"
|
||||
positiveText:"确定"
|
||||
onPositiveClicked:{
|
||||
window.deleteWindow()
|
||||
AppInfo.restart()
|
||||
}
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
@ -61,10 +142,10 @@ FluScrollablePage{
|
||||
Repeater{
|
||||
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
|
||||
delegate: FluRadioButton{
|
||||
checked : MainEvent.displayMode===modelData.mode
|
||||
checked : viewmodel_settings.displayMode===modelData.mode
|
||||
text:modelData.title
|
||||
clickListener:function(){
|
||||
MainEvent.displayMode = modelData.mode
|
||||
viewmodel_settings.displayMode = modelData.mode
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,10 +176,10 @@ FluScrollablePage{
|
||||
Repeater{
|
||||
model: ["Zh","En"]
|
||||
delegate: FluRadioButton{
|
||||
checked: appInfo.lang.objectName === modelData
|
||||
checked: AppInfo.lang.objectName === modelData
|
||||
text:modelData
|
||||
clickListener:function(){
|
||||
appInfo.changeLang(modelData)
|
||||
AppInfo.changeLang(modelData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,21 @@ import QtQuick.Layouts 1.15
|
||||
import QtQuick.Window 2.15
|
||||
import FluentUI 1.0
|
||||
import "qrc:///example/qml/component"
|
||||
import "qrc:///example/qml/viewmodel"
|
||||
import "../component"
|
||||
import "../viewmodel"
|
||||
|
||||
FluScrollablePage{
|
||||
|
||||
launchMode: FluPageType.SingleInstance
|
||||
|
||||
title:"TextBox"
|
||||
|
||||
TextBoxViewModel{
|
||||
id:viewModel
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
console.debug("T_TextBox页面销毁了")
|
||||
}
|
||||
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
height: 68
|
||||
@ -21,6 +29,10 @@ FluScrollablePage{
|
||||
placeholderText: "单行输入框"
|
||||
disabled:text_box_switch.checked
|
||||
cleanEnabled: true
|
||||
text:viewModel.text1
|
||||
onTextChanged: {
|
||||
viewModel.text1 = text
|
||||
}
|
||||
anchors{
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
@ -85,6 +97,10 @@ FluScrollablePage{
|
||||
FluMultilineTextBox{
|
||||
id:multiine_textbox
|
||||
placeholderText: "多行输入框"
|
||||
text:viewModel.text2
|
||||
onTextChanged: {
|
||||
viewModel.text2 = text
|
||||
}
|
||||
disabled:text_box_multi_switch.checked
|
||||
anchors{
|
||||
verticalCenter: parent.verticalCenter
|
||||
|
@ -6,152 +6,127 @@ import FluentUI 1.0
|
||||
import "qrc:///example/qml/component"
|
||||
import "../component"
|
||||
|
||||
FluScrollablePage {
|
||||
FluContentPage {
|
||||
|
||||
title:"TreeView"
|
||||
|
||||
function randomName() {
|
||||
var names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十"]
|
||||
return names[Math.floor(Math.random() * names.length)]
|
||||
function treeData(){
|
||||
const dig = (path = '0', level = 4) => {
|
||||
const list = [];
|
||||
for (let i = 0; i < 6; i += 1) {
|
||||
const key = `${path}-${i}`;
|
||||
const treeNode = {
|
||||
title: key,
|
||||
key,
|
||||
};
|
||||
if (level > 0) {
|
||||
treeNode.children = dig(key, level - 1);
|
||||
}
|
||||
list.push(treeNode);
|
||||
}
|
||||
return list;
|
||||
};
|
||||
return dig();
|
||||
}
|
||||
|
||||
function randomCompany() {
|
||||
var companies = ["阿里巴巴", "腾讯", "百度", "京东", "华为", "小米", "字节跳动", "美团", "滴滴"]
|
||||
return companies[Math.floor(Math.random() * companies.length)]
|
||||
}
|
||||
|
||||
function randomDepartment() {
|
||||
var departments = ["技术部", "销售部", "市场部", "人事部", "财务部", "客服部", "产品部", "设计部", "运营部"]
|
||||
return departments[Math.floor(Math.random() * departments.length)]
|
||||
}
|
||||
|
||||
function createEmployee() {
|
||||
var name = randomName()
|
||||
return tree_view.createItem(name, false)
|
||||
}
|
||||
|
||||
function createSubtree(numEmployees) {
|
||||
var employees = []
|
||||
for (var i = 0; i < numEmployees; i++) {
|
||||
employees.push(createEmployee())
|
||||
Column{
|
||||
id:layout_column
|
||||
spacing: 12
|
||||
width: 300
|
||||
anchors{
|
||||
topMargin: 20
|
||||
top:parent.top
|
||||
left: parent.left
|
||||
leftMargin: 10
|
||||
bottom:parent.bottom
|
||||
bottomMargin: 20
|
||||
}
|
||||
return tree_view.createItem(randomDepartment(), true, employees)
|
||||
}
|
||||
|
||||
function createOrg(numLevels, numSubtrees, numEmployees) {
|
||||
if (numLevels === 0) {
|
||||
return []
|
||||
FluText{
|
||||
text:"共计%1条数据,当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount())
|
||||
}
|
||||
|
||||
FluText{
|
||||
text:"共计选中%1条数据".arg(tree_view.selectionModel().length)
|
||||
}
|
||||
var subtrees = []
|
||||
for (var i = 0; i < numSubtrees; i++) {
|
||||
subtrees.push(createSubtree(numEmployees))
|
||||
}
|
||||
return [tree_view.createItem(randomCompany(), true, subtrees)].concat(createOrg(numLevels - 1, numSubtrees, numEmployees))
|
||||
}
|
||||
|
||||
FluArea{
|
||||
id:layout_actions
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 20
|
||||
height: 50
|
||||
paddings: 10
|
||||
RowLayout{
|
||||
spacing: 14
|
||||
FluDropDownButton{
|
||||
id:btn_selection_model
|
||||
Layout.preferredWidth: 140
|
||||
text:"None"
|
||||
FluMenuItem{
|
||||
text:"None"
|
||||
onClicked: {
|
||||
btn_selection_model.text = text
|
||||
tree_view.selectionMode = FluTabViewType.Equal
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Single"
|
||||
onClicked: {
|
||||
btn_selection_model.text = text
|
||||
tree_view.selectionMode = FluTabViewType.SizeToContent
|
||||
}
|
||||
}
|
||||
FluMenuItem{
|
||||
text:"Muiltple"
|
||||
onClicked: {
|
||||
btn_selection_model.text = text
|
||||
tree_view.selectionMode = FluTabViewType.Compact
|
||||
}
|
||||
}
|
||||
spacing: 10
|
||||
FluText{
|
||||
text:"cellHeight:"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
FluFilledButton{
|
||||
text:"获取选中的数据"
|
||||
onClicked: {
|
||||
if(tree_view.selectionMode === FluTreeViewType.None){
|
||||
showError("当前非选择模式,没有选中的数据")
|
||||
}
|
||||
if(tree_view.selectionMode === FluTreeViewType.Single){
|
||||
if(!tree_view.signleData()){
|
||||
showError("没有选中数据")
|
||||
return
|
||||
}
|
||||
showSuccess(tree_view.signleData().text)
|
||||
}
|
||||
if(tree_view.selectionMode === FluTreeViewType.Multiple){
|
||||
if(tree_view.multipData().length===0){
|
||||
showError("没有选中数据")
|
||||
return
|
||||
}
|
||||
var info = []
|
||||
tree_view.multipData().map((value)=>info.push(value.text))
|
||||
showSuccess(info.join(","))
|
||||
}
|
||||
}
|
||||
FluSlider{
|
||||
id:slider_cell_height
|
||||
value: 30
|
||||
from: 30
|
||||
to:100
|
||||
}
|
||||
}
|
||||
RowLayout{
|
||||
spacing: 10
|
||||
FluText{
|
||||
text:"depthPadding:"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
FluSlider{
|
||||
id:slider_depth_padding
|
||||
value: 30
|
||||
from: 30
|
||||
to:100
|
||||
}
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:switch_showline
|
||||
text:"showLine"
|
||||
checked: false
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:switch_draggable
|
||||
text:"draggable"
|
||||
checked: false
|
||||
}
|
||||
FluToggleSwitch{
|
||||
id:switch_checkable
|
||||
text:"checkable"
|
||||
checked: false
|
||||
}
|
||||
FluButton{
|
||||
text:"all expand"
|
||||
onClicked: {
|
||||
tree_view.allExpand()
|
||||
}
|
||||
}
|
||||
FluButton{
|
||||
text:"all collapse"
|
||||
onClicked: {
|
||||
tree_view.allCollapse()
|
||||
}
|
||||
}
|
||||
}
|
||||
FluArea{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
paddings: 10
|
||||
height: 400
|
||||
anchors{
|
||||
left: layout_column.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
rightMargin: 5
|
||||
topMargin: 5
|
||||
bottomMargin: 5
|
||||
}
|
||||
FluShadow{}
|
||||
FluTreeView{
|
||||
id:tree_view
|
||||
width:240
|
||||
anchors{
|
||||
top:parent.top
|
||||
left:parent.left
|
||||
bottom:parent.bottom
|
||||
}
|
||||
onItemClicked:
|
||||
(model)=>{
|
||||
showSuccess(model.text)
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
cellHeight: slider_cell_height.value
|
||||
draggable:switch_draggable.checked
|
||||
showLine: switch_showline.checked
|
||||
checkable:switch_checkable.checked
|
||||
depthPadding: slider_depth_padding.value
|
||||
Component.onCompleted: {
|
||||
var org = createOrg(3, 3, 3)
|
||||
createItem()
|
||||
updateData(org)
|
||||
var data = treeData()
|
||||
dataSource = data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeExpander{
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: -1
|
||||
code:'FluTreeView{
|
||||
id:tree_view
|
||||
width:240
|
||||
height:600
|
||||
Component.onCompleted: {
|
||||
var datas = []
|
||||
datas.push(createItem("Node1",false))
|
||||
datas.push(createItem("Node2",false))
|
||||
datas.push(createItem("Node2",true,[createItem("Node2-1",false),createItem("Node2-2",false)]))
|
||||
updateData(datas)
|
||||
}
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
13
example/qml/viewmodel/SettingsViewModel.qml
Normal file
13
example/qml/viewmodel/SettingsViewModel.qml
Normal file
@ -0,0 +1,13 @@
|
||||
import QtQuick 2.15
|
||||
import FluentUI 1.0
|
||||
|
||||
FluViewModel{
|
||||
|
||||
objectName: "SettingsViewModel"
|
||||
property int displayMode
|
||||
|
||||
onInitData: {
|
||||
displayMode = FluNavigationViewType.Auto
|
||||
}
|
||||
|
||||
}
|
8
example/qml/viewmodel/TextBoxViewModel.qml
Normal file
8
example/qml/viewmodel/TextBoxViewModel.qml
Normal file
@ -0,0 +1,8 @@
|
||||
import QtQuick 2.15
|
||||
import FluentUI 1.0
|
||||
|
||||
FluViewModel {
|
||||
objectName: "TextBoxView"
|
||||
property string text1
|
||||
property string text2
|
||||
}
|
@ -36,7 +36,7 @@ CustomWindow {
|
||||
}
|
||||
}
|
||||
FluText{
|
||||
text:"v%1".arg(appInfo.version)
|
||||
text:"v%1".arg(AppInfo.version)
|
||||
font: FluTextStyle.Body
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
}
|
||||
|
@ -6,8 +6,11 @@ import Qt.labs.platform 1.1
|
||||
import FluentUI 1.0
|
||||
import example 1.0
|
||||
import "qrc:///example/qml/component"
|
||||
import "../component"
|
||||
import "qrc:///example/qml/global"
|
||||
import "qrc:///example/qml/viewmodel"
|
||||
import "../component"
|
||||
import "../viewmodel"
|
||||
import "../global"
|
||||
|
||||
CustomWindow {
|
||||
|
||||
@ -21,15 +24,32 @@ CustomWindow {
|
||||
appBarVisible: false
|
||||
launchMode: FluWindowType.SingleTask
|
||||
|
||||
SettingsViewModel{
|
||||
id:viewmodel_settings
|
||||
}
|
||||
|
||||
closeFunc:function(event){
|
||||
dialog_close.open()
|
||||
event.accepted = false
|
||||
}
|
||||
|
||||
FluEvent{
|
||||
id:event_checkupdate
|
||||
name: "checkUpdate"
|
||||
onTriggered: {
|
||||
checkUpdate(false)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
FluTools.setQuitOnLastWindowClosed(false)
|
||||
tour.open()
|
||||
checkUpdate()
|
||||
checkUpdate(true)
|
||||
FluEventBus.registerEvent(event_checkupdate)
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
FluEventBus.unRegisterEvent(event_checkupdate)
|
||||
}
|
||||
|
||||
SystemTrayIcon {
|
||||
@ -139,7 +159,7 @@ CustomWindow {
|
||||
id:loader
|
||||
lazy: true
|
||||
anchors.fill: parent
|
||||
source: "https://zhu-zichu.gitee.io/Qt5_155_LieflatPage.qml"
|
||||
source: "https://zhu-zichu.gitee.io/Qt5_156_LieflatPage.qml"
|
||||
}
|
||||
}
|
||||
front: Item{
|
||||
@ -165,13 +185,13 @@ CustomWindow {
|
||||
height: parent.height
|
||||
z:999
|
||||
//Stack模式,每次切换都会将页面压入栈中,随着栈的页面增多,消耗的内存也越多,内存消耗多就会卡顿,这时候就需要按返回将页面pop掉,释放内存。该模式可以配合FluPage中的launchMode属性,设置页面的启动模式
|
||||
pageMode: FluNavigationViewType.Stack
|
||||
//NoStack模式,每次切换都会销毁之前的页面然后创建一个新的页面,只需消耗少量内存(推荐)
|
||||
// pageMode: FluNavigationViewType.NoStack
|
||||
// pageMode: FluNavigationViewType.Stack
|
||||
//NoStack模式,每次切换都会销毁之前的页面然后创建一个新的页面,只需消耗少量内存,可以配合FluViewModel保存页面数据(推荐)
|
||||
pageMode: FluNavigationViewType.NoStack
|
||||
items: ItemsOriginal
|
||||
footerItems:ItemsFooter
|
||||
topPadding:FluTools.isMacos() ? 20 : 0
|
||||
displayMode:MainEvent.displayMode
|
||||
displayMode:viewmodel_settings.displayMode
|
||||
logo: "qrc:/example/res/image/favicon.ico"
|
||||
title:"FluentUI"
|
||||
onLogoClicked:{
|
||||
@ -301,7 +321,7 @@ CustomWindow {
|
||||
property string body
|
||||
id:dialog_update
|
||||
title:"升级提示"
|
||||
message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+appInfo.version+" \n现在是否去下载新版本?\n\n更新内容:\n"+body
|
||||
message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+AppInfo.version+" \n现在是否去下载新版本?\n\n更新内容:\n"+body
|
||||
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
|
||||
negativeText: "取消"
|
||||
positiveText:"确定"
|
||||
@ -310,28 +330,44 @@ CustomWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate(){
|
||||
var callable = {}
|
||||
callable.onStart = function(){
|
||||
HttpCallable{
|
||||
id:callable
|
||||
property bool silent: true
|
||||
onStart: {
|
||||
console.debug("satrt check update...")
|
||||
}
|
||||
callable.onFinish = function(){
|
||||
onFinish: {
|
||||
console.debug("check update finish")
|
||||
FluEventBus.post("checkUpdateFinish");
|
||||
}
|
||||
callable.onSuccess = function(result){
|
||||
var data = JSON.parse(result)
|
||||
console.debug("current version "+appInfo.version)
|
||||
console.debug("new version "+data.tag_name)
|
||||
if(data.tag_name !== appInfo.version){
|
||||
dialog_update.newVerson = data.tag_name
|
||||
dialog_update.body = data.body
|
||||
dialog_update.open()
|
||||
onSuccess:
|
||||
(result)=>{
|
||||
var data = JSON.parse(result)
|
||||
console.debug("current version "+AppInfo.version)
|
||||
console.debug("new version "+data.tag_name)
|
||||
if(data.tag_name !== AppInfo.version){
|
||||
dialog_update.newVerson = data.tag_name
|
||||
dialog_update.body = data.body
|
||||
dialog_update.open()
|
||||
}else{
|
||||
if(!silent){
|
||||
showInfo("当前版本已经是最新版")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
callable.onError = function(status,errorString){
|
||||
console.debug(status+";"+errorString)
|
||||
}
|
||||
http.get("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest",callable)
|
||||
onError:
|
||||
(status,errorString)=>{
|
||||
if(!silent){
|
||||
showError("网络异常!")
|
||||
}
|
||||
console.debug(status+";"+errorString)
|
||||
}
|
||||
}
|
||||
|
||||
function checkUpdate(silent){
|
||||
callable.silent = silent
|
||||
var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest")
|
||||
http.get(request,callable);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <QQmlContext>
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
#include "lang/En.h"
|
||||
#include "lang/Zh.h"
|
||||
#include "Version.h"
|
||||
@ -20,7 +21,6 @@ void AppInfo::init(QQmlApplicationEngine *engine){
|
||||
QObject::connect(this,&AppInfo::langChanged,this,[=]{
|
||||
context->setContextProperty("lang",this->lang());
|
||||
});
|
||||
context->setContextProperty("appInfo",this);
|
||||
}
|
||||
|
||||
void AppInfo::changeLang(const QString& locale){
|
||||
@ -35,3 +35,7 @@ void AppInfo::changeLang(const QString& locale){
|
||||
lang(new En());
|
||||
}
|
||||
}
|
||||
|
||||
void AppInfo::restart(){
|
||||
qApp->exit(931);
|
||||
}
|
||||
|
@ -5,16 +5,20 @@
|
||||
#include <QQmlApplicationEngine>
|
||||
#include "lang/Lang.h"
|
||||
#include "stdafx.h"
|
||||
#include "singleton.h"
|
||||
|
||||
class AppInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY_AUTO(QString,version)
|
||||
Q_PROPERTY_AUTO(Lang*,lang)
|
||||
public:
|
||||
private:
|
||||
explicit AppInfo(QObject *parent = nullptr);
|
||||
public:
|
||||
SINGLETONG(AppInfo)
|
||||
void init(QQmlApplicationEngine *engine);
|
||||
Q_INVOKABLE void changeLang(const QString& locale);
|
||||
Q_INVOKABLE void restart();
|
||||
};
|
||||
|
||||
#endif // APPINFO_H
|
||||
|
40
example/src/helper/SettingsHelper.cpp
Normal file
40
example/src/helper/SettingsHelper.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "SettingsHelper.h"
|
||||
|
||||
#include <QDataStream>
|
||||
|
||||
SettingsHelper::SettingsHelper(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SettingsHelper::~SettingsHelper() = default;
|
||||
|
||||
void SettingsHelper::save(const QString& key,QVariant val)
|
||||
{
|
||||
QByteArray data = {};
|
||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
||||
stream.setVersion(QDataStream::Qt_5_6);
|
||||
stream << val;
|
||||
m_settings->setValue(key, data);
|
||||
}
|
||||
|
||||
QVariant SettingsHelper::get(const QString& key){
|
||||
const QByteArray data = m_settings->value(key).toByteArray();
|
||||
if (data.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
QDataStream stream(data);
|
||||
stream.setVersion(QDataStream::Qt_5_6);
|
||||
QVariant val;
|
||||
stream >> val;
|
||||
return val;
|
||||
}
|
||||
|
||||
void SettingsHelper::init(char *argv[]){
|
||||
auto applicationPath = QString::fromStdString(argv[0]);
|
||||
const QFileInfo fileInfo(applicationPath);
|
||||
const QString iniFileName = fileInfo.completeBaseName() + ".ini";
|
||||
const QString iniFilePath = fileInfo.dir().path() + "/" + iniFileName;
|
||||
qDebug()<<iniFilePath;
|
||||
m_settings.reset(new QSettings(iniFilePath, QSettings::IniFormat));
|
||||
}
|
33
example/src/helper/SettingsHelper.h
Normal file
33
example/src/helper/SettingsHelper.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef SETTINGSHELPER_H
|
||||
#define SETTINGSHELPER_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtQml/qqml.h>
|
||||
#include <QSettings>
|
||||
#include <QScopedPointer>
|
||||
#include <QFileInfo>
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include "src/singleton.h"
|
||||
|
||||
class SettingsHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
explicit SettingsHelper(QObject* parent = nullptr);
|
||||
public:
|
||||
SINGLETONG(SettingsHelper)
|
||||
~SettingsHelper() override;
|
||||
void init(char *argv[]);
|
||||
Q_INVOKABLE void saveRender(const QString& render){save("render",render);}
|
||||
Q_INVOKABLE QString getReander(){return get("render").toString();}
|
||||
Q_INVOKABLE void saveDarkMode(int darkModel){save("darkMode",darkModel);}
|
||||
Q_INVOKABLE int getDarkMode(){return get("darkMode").toInt(0);}
|
||||
private:
|
||||
void save(const QString& key,QVariant val);
|
||||
QVariant get(const QString& key);
|
||||
private:
|
||||
QScopedPointer<QSettings> m_settings;
|
||||
};
|
||||
|
||||
#endif // SETTINGSHELPER_H
|
@ -8,15 +8,25 @@
|
||||
#include <QProcess>
|
||||
#include <FramelessHelper/Quick/framelessquickmodule.h>
|
||||
#include <FramelessHelper/Core/private/framelessconfig_p.h>
|
||||
#include <QtQml/qqmlextensionplugin.h>
|
||||
#include "AppInfo.h"
|
||||
#include "src/component/CircularReveal.h"
|
||||
#include "src/component/FileWatcher.h"
|
||||
#include "src/component/FpsItem.h"
|
||||
#include "src/helper/SettingsHelper.h"
|
||||
|
||||
#ifdef FLUENTUI_BUILD_STATIC_LIB
|
||||
#if (QT_VERSION > QT_VERSION_CHECK(6, 2, 0))
|
||||
Q_IMPORT_QML_PLUGIN(FluentUIPlugin)
|
||||
#endif
|
||||
#include <FluentUI.h>
|
||||
#endif
|
||||
|
||||
FRAMELESSHELPER_USE_NAMESPACE
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SettingsHelper::getInstance()->init(argv);
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
|
||||
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
@ -24,34 +34,42 @@ int main(int argc, char *argv[])
|
||||
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
|
||||
#endif
|
||||
#endif
|
||||
//将样式设置为Basic,不然会导致组件显示异常
|
||||
qputenv("QT_QUICK_CONTROLS_STYLE","Basic");
|
||||
FramelessHelper::Quick::initialize();
|
||||
QGuiApplication::setOrganizationName("ZhuZiChu");
|
||||
QGuiApplication::setOrganizationDomain("https://zhuzichu520.github.io");
|
||||
QGuiApplication::setApplicationName("FluentUI");
|
||||
if(SettingsHelper::getInstance()->getReander()=="software"){
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
|
||||
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
|
||||
#endif
|
||||
}
|
||||
QGuiApplication app(argc, argv);
|
||||
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
|
||||
FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow);
|
||||
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur);
|
||||
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
|
||||
#ifdef Q_OS_WIN // 此设置仅在Windows下生效
|
||||
#ifdef Q_OS_WIN
|
||||
FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder);
|
||||
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow,false);
|
||||
#endif
|
||||
#ifdef Q_OS_MACOS
|
||||
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur,false);
|
||||
#endif
|
||||
AppInfo* appInfo = new AppInfo();
|
||||
QQmlApplicationEngine engine;
|
||||
AppInfo::getInstance()->init(&engine);
|
||||
engine.rootContext()->setContextProperty("AppInfo",AppInfo::getInstance());
|
||||
engine.rootContext()->setContextProperty("SettingsHelper",SettingsHelper::getInstance());
|
||||
FramelessHelper::Quick::registerTypes(&engine);
|
||||
#ifdef FLUENTUI_BUILD_STATIC_LIB
|
||||
engine.addImportPath("qrc:/"); // 让静态资源可以被QML引擎搜索到
|
||||
FluentUI::getInstance()->registerTypes(&engine);
|
||||
#endif
|
||||
qDebug()<<engine.importPathList();
|
||||
qmlRegisterType<CircularReveal>("example", 1, 0, "CircularReveal");
|
||||
qmlRegisterType<FileWatcher>("example", 1, 0, "FileWatcher");
|
||||
qmlRegisterType<FpsItem>("example", 1, 0, "FpsItem");
|
||||
appInfo->init(&engine);
|
||||
const QUrl url(QStringLiteral("qrc:/example/qml/App.qml"));
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
|
||||
&app, [url](QObject *obj, const QUrl &objUrl) {
|
||||
@ -59,5 +77,9 @@ int main(int argc, char *argv[])
|
||||
QCoreApplication::exit(-1);
|
||||
}, Qt::QueuedConnection);
|
||||
engine.load(url);
|
||||
return app.exec();
|
||||
const int exec = QGuiApplication::exec();
|
||||
if (exec == 931) {
|
||||
QProcess::startDetached(qApp->applicationFilePath(), QStringList());
|
||||
}
|
||||
return exec;
|
||||
}
|
||||
|
47
example/src/singleton.h
Normal file
47
example/src/singleton.h
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef SINGLETON_H
|
||||
#define SINGLETON_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QScopedPointer>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
template <typename T>
|
||||
class Singleton {
|
||||
public:
|
||||
static T* getInstance();
|
||||
|
||||
Singleton(const Singleton& other) = delete;
|
||||
Singleton<T>& operator=(const Singleton& other) = delete;
|
||||
|
||||
private:
|
||||
static std::mutex mutex;
|
||||
static T* instance;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::mutex Singleton<T>::mutex;
|
||||
template <typename T>
|
||||
T* Singleton<T>::instance;
|
||||
template <typename T>
|
||||
T* Singleton<T>::getInstance() {
|
||||
if (instance == nullptr) {
|
||||
std::lock_guard<std::mutex> locker(mutex);
|
||||
if (instance == nullptr) {
|
||||
instance = new T();
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
#define SINGLETONG(Class) \
|
||||
private: \
|
||||
friend class Singleton<Class>; \
|
||||
friend struct QScopedPointerDeleter<Class>; \
|
||||
\
|
||||
public: \
|
||||
static Class* getInstance() { \
|
||||
return Singleton<Class>::getInstance(); \
|
||||
}
|
||||
|
||||
#endif // SINGLETON_H
|
Submodule framelesshelper updated: 75ca85ec42...96f3981f0a
@ -1,16 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
if (FLUENTUI_BUILD_STATIC_LIB)
|
||||
if (FLUENTUI_BUILD_STATIC_LIB AND (QT_VERSION VERSION_GREATER_EQUAL "6.2"))
|
||||
project(fluentui LANGUAGES CXX)
|
||||
else()
|
||||
project(fluentuiplugin LANGUAGES CXX)
|
||||
endif()
|
||||
|
||||
#配置通用编译
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
if(APPLE)
|
||||
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
if (FLUENTUI_BUILD_STATIC_LIB)
|
||||
add_definitions(-DFLUENTUI_BUILD_STATIC_LIB)
|
||||
endif()
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Quick Qml)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Quick Qml)
|
||||
|
||||
@ -32,6 +37,9 @@ endforeach(filepath)
|
||||
if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
|
||||
#删除fluentuiplugin.cpp与fluentuiplugin.h,这些只要Qt5使用,Qt6不需要
|
||||
list(REMOVE_ITEM sources_files fluentuiplugin.h fluentuiplugin.cpp)
|
||||
if (NOT FLUENTUI_BUILD_STATIC_LIB)
|
||||
list(REMOVE_ITEM sources_files FluentUI.h FluentUI.cpp)
|
||||
endif()
|
||||
|
||||
#遍历所有qml文件
|
||||
file(GLOB_RECURSE QML_PATHS *.qml)
|
||||
@ -83,6 +91,9 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
|
||||
if(FLUENTUI_BUILD_STATIC_LIB)
|
||||
set(FLUENTUI_QML_PLUGIN_DIRECTORY ${CMAKE_BINARY_DIR}/FluentUI)
|
||||
endif()
|
||||
qt_add_library(${PROJECT_NAME} ${LIB_TYPE})
|
||||
qt_add_qml_module(${PROJECT_NAME}
|
||||
PLUGIN_TARGET ${PLUGIN_TARGET_NAME}
|
||||
@ -94,14 +105,14 @@ if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
|
||||
SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH}
|
||||
QML_FILES ${qml_files}
|
||||
RESOURCES ${resource_files}
|
||||
RESOURCE_PREFIX "/"
|
||||
RESOURCE_PREFIX "/qt/qml"
|
||||
)
|
||||
else()
|
||||
include(QmlPlugin)
|
||||
add_qmlplugin(${PROJECT_NAME}
|
||||
URI "FluentUI"
|
||||
VERSION 1.0
|
||||
SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH} resource.qrc
|
||||
SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH} Qt5/imports/fluentui.qrc
|
||||
QMLFILES ${qml_files}
|
||||
QMLDIR imports/FluentUI
|
||||
BINARY_DIR ${FLUENTUI_QML_PLUGIN_DIRECTORY}
|
||||
@ -109,6 +120,9 @@ else()
|
||||
)
|
||||
endif()
|
||||
|
||||
#去掉mingw生成的动态库libxxx前缀lib,不去掉前缀会导致 module "FluentUI" plugin "fluentuiplugin" not found
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
||||
|
||||
#链接库
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||
Qt${QT_VERSION_MAJOR}::CorePrivate
|
||||
|
21
src/Def.h
21
src/Def.h
@ -4,6 +4,27 @@
|
||||
#include <QObject>
|
||||
#include <QtQml/qqml.h>
|
||||
|
||||
namespace FluViewModelType {
|
||||
Q_NAMESPACE
|
||||
enum Scope {
|
||||
Window = 0x0000,
|
||||
Application = 0x0001
|
||||
};
|
||||
Q_ENUM_NS(Scope)
|
||||
QML_NAMED_ELEMENT(FluViewModelType)
|
||||
}
|
||||
|
||||
namespace FluHttpType {
|
||||
Q_NAMESPACE
|
||||
enum CacheMode {
|
||||
NoCache = 0x0000,
|
||||
RequestFailedReadCache = 0x0001,
|
||||
IfNoneCacheRequest = 0x0002,
|
||||
FirstCacheThenRequest = 0x0004,
|
||||
};
|
||||
Q_ENUM_NS(CacheMode)
|
||||
QML_NAMED_ELEMENT(FluHttpType)
|
||||
}
|
||||
|
||||
namespace FluScreenshotType {
|
||||
Q_NAMESPACE
|
||||
|
@ -10,19 +10,7 @@
|
||||
#include <QClipboard>
|
||||
#include "Def.h"
|
||||
|
||||
FluApp* FluApp::m_instance = nullptr;
|
||||
|
||||
FluApp *FluApp::getInstance()
|
||||
{
|
||||
if(FluApp::m_instance == nullptr){
|
||||
FluApp::m_instance = new FluApp;
|
||||
}
|
||||
return FluApp::m_instance;
|
||||
}
|
||||
|
||||
FluApp::FluApp(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
FluApp::FluApp(QObject *parent):QObject{parent}{
|
||||
httpInterceptor(nullptr);
|
||||
}
|
||||
|
||||
@ -83,8 +71,7 @@ void FluApp::navigate(const QString& route,const QJsonObject& argument,FluRegist
|
||||
view->setColor(QColor(Qt::transparent));
|
||||
}
|
||||
|
||||
QJsonArray FluApp::awesomelist(const QString& keyword)
|
||||
{
|
||||
QJsonArray FluApp::awesomelist(const QString& keyword){
|
||||
QJsonArray arr;
|
||||
QMetaEnum enumType = Fluent_Awesome::staticMetaObject.enumerator(Fluent_Awesome::staticMetaObject.indexOfEnumerator("Fluent_AwesomeType"));
|
||||
for(int i=0; i < enumType.keyCount(); ++i){
|
||||
|
73
src/FluApp.h
73
src/FluApp.h
@ -11,6 +11,7 @@
|
||||
#include "FluRegister.h"
|
||||
#include "FluHttpInterceptor.h"
|
||||
#include "stdafx.h"
|
||||
#include "singleton.h"
|
||||
|
||||
/**
|
||||
* @brief The FluApp class
|
||||
@ -18,80 +19,26 @@
|
||||
class FluApp : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
/**
|
||||
* @brief initialRoute 初始路由
|
||||
*/
|
||||
Q_PROPERTY_AUTO(QString,initialRoute);
|
||||
|
||||
/**
|
||||
* @brief routes 路由表
|
||||
*/
|
||||
Q_PROPERTY_AUTO(QJsonObject,routes);
|
||||
|
||||
/**
|
||||
* @brief http拦截器
|
||||
*/
|
||||
Q_PROPERTY_AUTO(FluHttpInterceptor*,httpInterceptor);
|
||||
|
||||
QML_NAMED_ELEMENT(FluApp)
|
||||
QML_SINGLETON
|
||||
private:
|
||||
/**
|
||||
* @brief FluApp 将默认构造函数设置为私有,则qml创建单例就会走create工厂方法创建单例
|
||||
* @param parent
|
||||
*/
|
||||
explicit FluApp(QObject *parent = nullptr);
|
||||
public:
|
||||
~FluApp();
|
||||
static FluApp *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
|
||||
{
|
||||
return getInstance();
|
||||
}
|
||||
static FluApp *getInstance();
|
||||
/**
|
||||
* @brief run
|
||||
*/
|
||||
Q_INVOKABLE void run();
|
||||
|
||||
/**
|
||||
* @brief navigate
|
||||
* @param route
|
||||
* @param argument
|
||||
* @param fluRegister
|
||||
*/
|
||||
Q_INVOKABLE void navigate(const QString& route,const QJsonObject& argument = {},FluRegister* fluRegister = nullptr);
|
||||
|
||||
/**
|
||||
* @brief init
|
||||
* @param window
|
||||
*/
|
||||
Q_INVOKABLE void init(QQuickWindow *window);
|
||||
|
||||
/**
|
||||
* @brief awesomelist
|
||||
* @param keyword
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE QJsonArray awesomelist(const QString& keyword = "");
|
||||
|
||||
/**
|
||||
* @brief closeApp
|
||||
*/
|
||||
Q_INVOKABLE void closeApp();
|
||||
|
||||
Q_INVOKABLE void deleteWindow(QQuickWindow* window);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief wnds
|
||||
*/
|
||||
SINGLETONG(FluApp)
|
||||
static FluApp *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
|
||||
Q_INVOKABLE void run();
|
||||
Q_INVOKABLE void navigate(const QString& route,const QJsonObject& argument = {},FluRegister* fluRegister = nullptr);
|
||||
Q_INVOKABLE void init(QQuickWindow *window);
|
||||
Q_INVOKABLE QJsonArray awesomelist(const QString& keyword = "");
|
||||
Q_INVOKABLE void closeApp();
|
||||
Q_INVOKABLE void deleteWindow(QQuickWindow* window);
|
||||
public:
|
||||
QMap<quint64, QQuickWindow*> wnds;
|
||||
|
||||
private:
|
||||
static FluApp* m_instance;
|
||||
/**
|
||||
* @brief appWindow
|
||||
*/
|
||||
QWindow *appWindow;
|
||||
};
|
||||
|
||||
|
@ -5,17 +5,18 @@
|
||||
#include <QRandomGenerator>
|
||||
#include <qmath.h>
|
||||
|
||||
FluCaptcha::FluCaptcha(QQuickItem *parent)
|
||||
: QQuickPaintedItem(parent)
|
||||
{
|
||||
font(QFont("楷体",25,QFont::Bold,true));
|
||||
FluCaptcha::FluCaptcha(QQuickItem *parent):QQuickPaintedItem(parent){
|
||||
ignoreCase(true);
|
||||
QFont fontStype;
|
||||
fontStype.setPixelSize(28);
|
||||
fontStype.setBold(true);
|
||||
font(fontStype);
|
||||
setWidth(180);
|
||||
setHeight(80);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void FluCaptcha::paint(QPainter* painter)
|
||||
{
|
||||
void FluCaptcha::paint(QPainter* painter){
|
||||
painter->save();
|
||||
painter->fillRect(boundingRect().toRect(),QColor(255,255,255,255));
|
||||
QPen pen;
|
||||
@ -69,5 +70,8 @@ void FluCaptcha::refresh(){
|
||||
}
|
||||
|
||||
bool FluCaptcha::verify(const QString& code){
|
||||
if(_ignoreCase){
|
||||
return this->_code.toUpper() == code.toUpper();
|
||||
}
|
||||
return this->_code == code;
|
||||
}
|
||||
|
@ -10,15 +10,17 @@ class FluCaptcha : public QQuickPaintedItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY_AUTO(QFont,font);
|
||||
Q_PROPERTY_AUTO(bool,ignoreCase);
|
||||
QML_NAMED_ELEMENT(FluCaptcha)
|
||||
private:
|
||||
int _generaNumber(int number);
|
||||
QString _code;
|
||||
public:
|
||||
explicit FluCaptcha(QQuickItem *parent = nullptr);
|
||||
void paint(QPainter* painter) override;
|
||||
Q_INVOKABLE void refresh();
|
||||
Q_INVOKABLE bool verify(const QString& code);
|
||||
private:
|
||||
QString _code;
|
||||
};
|
||||
|
||||
#endif // FLUCAPTCHA_H
|
||||
|
@ -1,7 +1,4 @@
|
||||
#include "FluColorSet.h"
|
||||
|
||||
FluColorSet::FluColorSet(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
|
||||
FluColorSet::FluColorSet(QObject *parent):QObject{parent}{
|
||||
}
|
||||
|
@ -1,18 +1,6 @@
|
||||
#include "FluColors.h"
|
||||
|
||||
FluColors* FluColors::m_instance = nullptr;
|
||||
|
||||
FluColors *FluColors::getInstance()
|
||||
{
|
||||
if(FluColors::m_instance == nullptr){
|
||||
FluColors::m_instance = new FluColors;
|
||||
}
|
||||
return FluColors::m_instance;
|
||||
}
|
||||
|
||||
FluColors::FluColors(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
FluColors::FluColors(QObject *parent):QObject{parent}{
|
||||
Transparent("#00000000");
|
||||
Black("#000000");
|
||||
White("#ffffff");
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QtQml/qqml.h>
|
||||
#include "FluColorSet.h"
|
||||
#include "stdafx.h"
|
||||
#include "singleton.h"
|
||||
|
||||
/**
|
||||
* @brief The FluColors class
|
||||
@ -49,13 +50,9 @@ class FluColors : public QObject
|
||||
QML_SINGLETON
|
||||
private:
|
||||
explicit FluColors(QObject *parent = nullptr);
|
||||
static FluColors* m_instance;
|
||||
public:
|
||||
static FluColors *getInstance();
|
||||
static FluColors *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
|
||||
{
|
||||
return getInstance();
|
||||
}
|
||||
SINGLETONG(FluColors)
|
||||
static FluColors *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
|
||||
};
|
||||
|
||||
#endif // FLUCOLORS_H
|
||||
|
23
src/FluEventBus.cpp
Normal file
23
src/FluEventBus.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include "FluEventBus.h"
|
||||
|
||||
FluEvent::FluEvent(QObject *parent):QObject{parent}{
|
||||
}
|
||||
|
||||
FluEventBus::FluEventBus(QObject *parent):QObject{parent}{
|
||||
}
|
||||
|
||||
void FluEventBus::registerEvent(FluEvent* event){
|
||||
_eventData.append(event);
|
||||
}
|
||||
|
||||
void FluEventBus::unRegisterEvent(FluEvent* event){
|
||||
_eventData.removeOne(event);
|
||||
}
|
||||
|
||||
void FluEventBus::post(const QString& name,const QMap<QString, QVariant>& data){
|
||||
foreach (auto event, _eventData) {
|
||||
if(event->name()==name){
|
||||
Q_EMIT event->triggered(data);
|
||||
}
|
||||
}
|
||||
}
|
35
src/FluEventBus.h
Normal file
35
src/FluEventBus.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef FLUEVENTBUS_H
|
||||
#define FLUEVENTBUS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtQml/qqml.h>
|
||||
#include "stdafx.h"
|
||||
#include "singleton.h"
|
||||
|
||||
class FluEvent : public QObject{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY_AUTO(QString,name);
|
||||
QML_NAMED_ELEMENT(FluEvent)
|
||||
public:
|
||||
explicit FluEvent(QObject *parent = nullptr);
|
||||
Q_SIGNAL void triggered(QMap<QString, QVariant> data);
|
||||
};
|
||||
|
||||
class FluEventBus : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_NAMED_ELEMENT(FluEventBus)
|
||||
QML_SINGLETON
|
||||
private:
|
||||
explicit FluEventBus(QObject *parent = nullptr);
|
||||
public:
|
||||
SINGLETONG(FluEventBus)
|
||||
static FluEventBus *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
|
||||
Q_INVOKABLE void registerEvent(FluEvent* event);
|
||||
Q_INVOKABLE void unRegisterEvent(FluEvent* event);
|
||||
Q_INVOKABLE void post(const QString& name,const QMap<QString, QVariant>& params = {});
|
||||
private:
|
||||
QList<FluEvent*> _eventData;
|
||||
};
|
||||
|
||||
#endif // FLUEVENTBUS_H
|
544
src/FluHttp.cpp
544
src/FluHttp.cpp
@ -5,15 +5,63 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QUrlQuery>
|
||||
#include <QHttpMultiPart>
|
||||
#include <QGuiApplication>
|
||||
#include <QJsonDocument>
|
||||
#include "MainThread.h"
|
||||
#include <QStandardPaths>
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
#include "Def.h"
|
||||
#include "FluApp.h"
|
||||
#include "FluTools.h"
|
||||
|
||||
FluHttp::FluHttp(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
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):QObject{parent}{
|
||||
retry(3);
|
||||
timeout(15000);
|
||||
cacheMode(FluHttpType::CacheMode::NoCache);
|
||||
cacheDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)+"/httpcache");
|
||||
breakPointDownload(false);
|
||||
}
|
||||
|
||||
FluHttp::~FluHttp(){
|
||||
@ -21,27 +69,36 @@ FluHttp::~FluHttp(){
|
||||
}
|
||||
|
||||
void FluHttp::cancel(){
|
||||
foreach (QPointer<QNetworkReply> item, _cache) {
|
||||
foreach (QPointer<QNetworkReply> item, _cacheReply) {
|
||||
if(item){
|
||||
item->abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FluHttp::handleReply(QNetworkReply* reply){
|
||||
_cache.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(HttpRequest* r,HttpCallable* c){
|
||||
auto request = QPointer(r);
|
||||
auto callable = QPointer(c);
|
||||
request->method("post");
|
||||
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) {
|
||||
QNetworkAccessManager manager;
|
||||
manager.setTransferTimeout(timeout());
|
||||
QUrl _url(url);
|
||||
QNetworkRequest request(_url);
|
||||
addHeaders(&request,data["headers"].toMap());
|
||||
QUrl url(request->url());
|
||||
QNetworkRequest req(url);
|
||||
addHeaders(&req,data["headers"].toMap());
|
||||
QHttpMultiPart multiPart(QHttpMultiPart::FormDataType);
|
||||
for (const auto& each : data["params"].toMap().toStdMap())
|
||||
{
|
||||
@ -54,198 +111,307 @@ void FluHttp::post(QString url,QJSValue callable,QMap<QString, QVariant> params,
|
||||
multiPart.append(part);
|
||||
}
|
||||
QEventLoop loop;
|
||||
QNetworkReply* reply = manager.post(request,&multiPart);
|
||||
_cache.append(reply);
|
||||
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
|
||||
loop.quit();
|
||||
});
|
||||
QNetworkReply* reply = manager.post(req,&multiPart);
|
||||
_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;
|
||||
_cache.removeOne(reply);
|
||||
QNetworkReply::NetworkError error = reply->error();
|
||||
bool isSuccess = 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);
|
||||
}
|
||||
}
|
||||
if(error == QNetworkReply::OperationCanceledError){
|
||||
break;
|
||||
}
|
||||
}
|
||||
onFinish(callable);
|
||||
onFinish(callable,request);
|
||||
});
|
||||
}
|
||||
|
||||
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(HttpRequest* r,HttpCallable* c){
|
||||
auto request = QPointer(r);
|
||||
auto callable = QPointer(c);
|
||||
request->method("postString");
|
||||
auto requestMap = request->toMap();
|
||||
auto httpId = request->httpId();
|
||||
QString params = request->params().toString();
|
||||
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
|
||||
QThreadPool::globalInstance()->start([=](){
|
||||
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) {
|
||||
QNetworkAccessManager manager;
|
||||
manager.setTransferTimeout(timeout());
|
||||
QUrl _url(url);
|
||||
QNetworkRequest request(_url);
|
||||
addHeaders(&request,data["headers"].toMap());
|
||||
QUrl url(request->url());
|
||||
QNetworkRequest req(url);
|
||||
addHeaders(&req,data["headers"].toMap());
|
||||
QString contentType = QString("text/plain;charset=utf-8");
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
|
||||
req.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();
|
||||
});
|
||||
QNetworkReply* reply = manager.post(req,params.toUtf8());
|
||||
_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;
|
||||
_cache.removeOne(reply);
|
||||
QNetworkReply::NetworkError error = reply->error();
|
||||
bool isSuccess = 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);
|
||||
}
|
||||
}
|
||||
if(error == QNetworkReply::OperationCanceledError){
|
||||
break;
|
||||
}
|
||||
}
|
||||
onFinish(callable);
|
||||
onFinish(callable,request);
|
||||
});
|
||||
}
|
||||
|
||||
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(HttpRequest* r,HttpCallable* c){
|
||||
auto request = QPointer(r);
|
||||
auto callable = QPointer(c);
|
||||
request->method("postJson");
|
||||
auto requestMap = request->toMap();
|
||||
auto httpId = request->httpId();
|
||||
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
|
||||
QThreadPool::globalInstance()->start([=](){
|
||||
onStart(callable);
|
||||
if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(httpId)){
|
||||
onCache(callable,readCache(httpId));
|
||||
onFinish(callable,request);
|
||||
return;
|
||||
}
|
||||
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest && cacheExists(httpId)){
|
||||
onCache(callable,readCache(httpId));
|
||||
}
|
||||
QNetworkAccessManager manager;
|
||||
manager.setTransferTimeout(timeout());
|
||||
for (int i = 0; i < retry(); ++i) {
|
||||
QNetworkAccessManager manager;
|
||||
manager.setTransferTimeout(timeout());
|
||||
QUrl _url(url);
|
||||
QNetworkRequest request(_url);
|
||||
addHeaders(&request,data["headers"].toMap());
|
||||
QUrl url(request->url());
|
||||
QNetworkRequest req(url);
|
||||
addHeaders(&req,data["headers"].toMap());
|
||||
QString contentType = QString("application/json;charset=utf-8");
|
||||
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
|
||||
req.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();
|
||||
});
|
||||
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;
|
||||
_cache.removeOne(reply);
|
||||
QNetworkReply::NetworkError error = reply->error();
|
||||
bool isSuccess = 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);
|
||||
}
|
||||
}
|
||||
if(error == QNetworkReply::OperationCanceledError){
|
||||
break;
|
||||
}
|
||||
}
|
||||
onFinish(callable);
|
||||
onFinish(callable,request);
|
||||
});
|
||||
}
|
||||
|
||||
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(HttpRequest* r,HttpCallable* c){
|
||||
auto request = QPointer(r);
|
||||
auto callable = QPointer(c);
|
||||
request->method("get");
|
||||
auto requestMap = request->toMap();
|
||||
auto httpId = request->httpId();
|
||||
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
|
||||
QThreadPool::globalInstance()->start([=](){
|
||||
onStart(callable);
|
||||
if(_cacheMode == FluHttpType::CacheMode::FirstCacheThenRequest && cacheExists(httpId)){
|
||||
onCache(callable,readCache(httpId));
|
||||
}
|
||||
if(_cacheMode == FluHttpType::CacheMode::IfNoneCacheRequest && cacheExists(httpId)){
|
||||
onCache(callable,readCache(httpId));
|
||||
onFinish(callable,request);
|
||||
return;
|
||||
}
|
||||
QNetworkAccessManager manager;
|
||||
manager.setTransferTimeout(timeout());
|
||||
for (int i = 0; i < retry(); ++i) {
|
||||
onStart(callable);
|
||||
QNetworkAccessManager manager;
|
||||
manager.setTransferTimeout(timeout());
|
||||
QUrl _url(url);
|
||||
addQueryParam(&_url,data["params"].toMap());
|
||||
QNetworkRequest request(_url);
|
||||
addHeaders(&request,data["headers"].toMap());
|
||||
QUrl url(request->url());
|
||||
addQueryParam(&url,data["params"].toMap());
|
||||
QNetworkRequest req(url);
|
||||
addHeaders(&req,data["headers"].toMap());
|
||||
QEventLoop loop;
|
||||
QNetworkReply* reply = manager.get(request);
|
||||
_cache.append(reply);
|
||||
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
|
||||
loop.quit();
|
||||
});
|
||||
auto reply = QPointer(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();
|
||||
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;
|
||||
QNetworkReply::NetworkError error = reply->error();
|
||||
bool isSuccess = 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;
|
||||
if(error == QNetworkReply::OperationCanceledError){
|
||||
break;
|
||||
}
|
||||
}
|
||||
onFinish(callable);
|
||||
onFinish(callable,request);
|
||||
});
|
||||
}
|
||||
|
||||
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(HttpRequest* r,HttpCallable* c){
|
||||
auto request = QPointer(r);
|
||||
auto callable = QPointer(c);
|
||||
request->method("download");
|
||||
auto requestMap = request->toMap();
|
||||
auto httpId = request->httpId();
|
||||
auto savePath = request->downloadSavePath();
|
||||
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
|
||||
QThreadPool::globalInstance()->start([=](){
|
||||
onStart(callable);
|
||||
QNetworkAccessManager manager;
|
||||
QUrl _url(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;
|
||||
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,this,[&loop](QNetworkReply *reply){
|
||||
loop.quit();
|
||||
});
|
||||
QPointer<QNetworkReply> reply = manager.get(request);
|
||||
_cache.append(reply);
|
||||
connect(reply,&QNetworkReply::downloadProgress,this,[=](qint64 bytesReceived, qint64 bytesTotal){
|
||||
onDownloadProgress(callable,bytesReceived,bytesTotal);
|
||||
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();
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
file->write(reply->readAll());
|
||||
onSuccess(callable,filePath);
|
||||
onSuccess(callable,savePath);
|
||||
}else{
|
||||
onError(callable,reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),reply->errorString(),"");
|
||||
}
|
||||
_cache.removeOne(reply);
|
||||
reply->deleteLater();
|
||||
reply = nullptr;
|
||||
onFinish(callable);
|
||||
onFinish(callable,request);
|
||||
});
|
||||
}
|
||||
|
||||
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(HttpRequest* request,HttpCallable* callable){
|
||||
request->method("upload");
|
||||
auto requestMap = request->toMap();
|
||||
QMap<QString, QVariant> data = invokeIntercept(requestMap).toMap();
|
||||
QThreadPool::globalInstance()->start([=](){
|
||||
onStart(callable);
|
||||
QNetworkAccessManager manager;
|
||||
manager.setTransferTimeout(timeout());
|
||||
QUrl _url(url);
|
||||
QNetworkRequest request(_url);
|
||||
addHeaders(&request,data["headers"].toMap());
|
||||
QUrl url(request->url());
|
||||
QNetworkRequest req(url);
|
||||
addHeaders(&req,data["headers"].toMap());
|
||||
QHttpMultiPart multiPart(QHttpMultiPart::FormDataType);
|
||||
qDebug()<<data["params"].toMap();
|
||||
for (const auto& each : data["params"].toMap().toStdMap())
|
||||
{
|
||||
const QString& key = each.first;
|
||||
@ -261,12 +427,11 @@ void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> param
|
||||
multiPart.append(part);
|
||||
}
|
||||
QEventLoop loop;
|
||||
QNetworkReply* reply = manager.post(request,&multiPart);
|
||||
_cache.append(reply);
|
||||
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
|
||||
loop.quit();
|
||||
});
|
||||
connect(reply,&QNetworkReply::uploadProgress,this,[=](qint64 bytesSent, qint64 bytesTotal){
|
||||
QNetworkReply* reply = manager.post(req,&multiPart);
|
||||
_cacheReply.append(reply);
|
||||
connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();});
|
||||
connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();});
|
||||
connect(reply,&QNetworkReply::uploadProgress,reply,[=](qint64 bytesSent, qint64 bytesTotal){
|
||||
onUploadProgress(callable,bytesSent,bytesTotal);
|
||||
});
|
||||
loop.exec();
|
||||
@ -274,7 +439,6 @@ void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> param
|
||||
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) {
|
||||
@ -282,21 +446,16 @@ void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> param
|
||||
}else{
|
||||
onError(callable,status,errorString,result);
|
||||
}
|
||||
onFinish(callable);
|
||||
onFinish(callable,request);
|
||||
});
|
||||
}
|
||||
|
||||
QVariant FluHttp::invokeIntercept(const QVariant& params,const QVariant& headers,const QString& method){
|
||||
QMap<QString, QVariant> requet = {
|
||||
{"params",params},
|
||||
{"headers",headers},
|
||||
{"method",method}
|
||||
};
|
||||
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",Q_RETURN_ARG(QVariant,target),Q_ARG(QVariant, request));
|
||||
return target;
|
||||
}
|
||||
|
||||
@ -320,54 +479,117 @@ 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 QString& httpId){
|
||||
auto filePath = getCacheFilePath(httpId);
|
||||
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 QString& httpId){
|
||||
return QFile(getCacheFilePath(httpId)).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 QString& httpId){
|
||||
QDir dir = _cacheDir;
|
||||
if (!dir.exists(_cacheDir)){
|
||||
dir.mkpath(_cacheDir);
|
||||
}
|
||||
auto filePath = _cacheDir+"/"+httpId;
|
||||
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::handleCache(const QString& httpId,const QString& result){
|
||||
if(_cacheMode==FluHttpType::CacheMode::NoCache){
|
||||
return;
|
||||
}
|
||||
auto filePath = getCacheFilePath(httpId);
|
||||
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){
|
||||
MainThread::post([=](){
|
||||
QJSValueList args;
|
||||
args<<static_cast<double>(recv);
|
||||
args<<static_cast<double>(total);
|
||||
QJSValue onDownloadProgress = callable.property("onDownloadProgress");
|
||||
onDownloadProgress.call(args);
|
||||
});
|
||||
qreal FluHttp::getBreakPointProgress(HttpRequest* request){
|
||||
request->method("download");
|
||||
auto httpId = request->httpId();
|
||||
QSharedPointer<QFile> file(new QFile(request->downloadSavePath()));
|
||||
auto filePath = getCacheFilePath(httpId);
|
||||
QSharedPointer<QFile> fileCache(new QFile(filePath));
|
||||
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){
|
||||
MainThread::post([=](){
|
||||
QJSValueList args;
|
||||
args<<static_cast<double>(sent);
|
||||
args<<static_cast<double>(total);
|
||||
QJSValue onUploadProgress = callable.property("onUploadProgress");
|
||||
onUploadProgress.call(args);
|
||||
});
|
||||
HttpRequest* FluHttp::newRequest(QString url){
|
||||
HttpRequest* request = new HttpRequest(this);
|
||||
request->url(url);
|
||||
return request;
|
||||
}
|
||||
|
||||
void FluHttp::onStart(QPointer<HttpCallable> callable){
|
||||
if(callable){
|
||||
Q_EMIT callable->start();
|
||||
}
|
||||
}
|
||||
|
||||
void FluHttp::onFinish(QPointer<HttpCallable> callable,QPointer<HttpRequest> request){
|
||||
if(callable){
|
||||
Q_EMIT callable->finish();
|
||||
}
|
||||
if(request&&request->parent()->inherits("FluHttp")){
|
||||
request->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void FluHttp::onError(QPointer<HttpCallable> callable,int status,QString errorString,QString result){
|
||||
if(callable){
|
||||
Q_EMIT callable->error(status,errorString,result);
|
||||
}
|
||||
}
|
||||
|
||||
void FluHttp::onSuccess(QPointer<HttpCallable> callable,QString result){
|
||||
if(callable){
|
||||
Q_EMIT callable->success(result);
|
||||
}
|
||||
}
|
||||
|
||||
void FluHttp::onCache(QPointer<HttpCallable> callable,QString result){
|
||||
if(callable){
|
||||
Q_EMIT callable->cache(result);
|
||||
}
|
||||
}
|
||||
|
||||
void FluHttp::onDownloadProgress(QPointer<HttpCallable> callable,qint64 recv,qint64 total){
|
||||
if(callable){
|
||||
Q_EMIT callable->downloadProgress(recv,total);
|
||||
}
|
||||
}
|
||||
|
||||
void FluHttp::onUploadProgress(QPointer<HttpCallable> callable,qint64 sent,qint64 total){
|
||||
if(callable){
|
||||
Q_EMIT callable->uploadProgress(sent,total);
|
||||
}
|
||||
}
|
||||
|
@ -7,36 +7,72 @@
|
||||
#include <QNetworkAccessManager>
|
||||
#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
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY_AUTO(int,retry);
|
||||
Q_PROPERTY_AUTO(int,timeout)
|
||||
Q_PROPERTY_AUTO(int,cacheMode);
|
||||
Q_PROPERTY_AUTO(QString,cacheDir);
|
||||
Q_PROPERTY_AUTO(bool,breakPointDownload);
|
||||
QML_NAMED_ELEMENT(FluHttp)
|
||||
private:
|
||||
QVariant invokeIntercept(const QVariant& params,const QVariant& headers,const QString& method);
|
||||
void handleReply(QNetworkReply* reply);
|
||||
QVariant invokeIntercept(QMap<QString, QVariant> request);
|
||||
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(const QString& httpId, const QString& result);
|
||||
QString readCache(const QString& httpId);
|
||||
bool cacheExists(const QString& httpId);
|
||||
QString getCacheFilePath(const QString& httpId);
|
||||
void onStart(QPointer<HttpCallable> callable);
|
||||
void onFinish(QPointer<HttpCallable> callable,QPointer<HttpRequest> request);
|
||||
void onError(QPointer<HttpCallable> callable,int status,QString errorString,QString result);
|
||||
void onSuccess(QPointer<HttpCallable> callable,QString result);
|
||||
void onCache(QPointer<HttpCallable> callable,QString result);
|
||||
void onDownloadProgress(QPointer<HttpCallable> callable,qint64 recv,qint64 total);
|
||||
void onUploadProgress(QPointer<HttpCallable> callable,qint64 sent,qint64 total);
|
||||
public:
|
||||
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 HttpRequest* newRequest(QString url = "");
|
||||
Q_INVOKABLE void get(HttpRequest* request,HttpCallable* callable);
|
||||
Q_INVOKABLE void post(HttpRequest* request,HttpCallable* callable);
|
||||
Q_INVOKABLE void postString(HttpRequest* request,HttpCallable* callable);
|
||||
Q_INVOKABLE void postJson(HttpRequest* request,HttpCallable* callable);
|
||||
Q_INVOKABLE void download(HttpRequest* request,HttpCallable* callable);
|
||||
Q_INVOKABLE void upload(HttpRequest* request,HttpCallable* callable);
|
||||
Q_INVOKABLE qreal getBreakPointProgress(HttpRequest* request);
|
||||
Q_INVOKABLE void cancel();
|
||||
private:
|
||||
QList<QPointer<QNetworkReply>> _cache;
|
||||
QList<QPointer<QNetworkReply>> _cacheReply;
|
||||
};
|
||||
|
||||
#endif // FLUHTTP_H
|
||||
|
@ -1,7 +1,4 @@
|
||||
#include "FluHttpInterceptor.h"
|
||||
|
||||
FluHttpInterceptor::FluHttpInterceptor(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
|
||||
FluHttpInterceptor::FluHttpInterceptor(QObject *parent):QObject{parent}{
|
||||
}
|
||||
|
@ -10,9 +10,6 @@ class FluHttpInterceptor : public QObject
|
||||
QML_NAMED_ELEMENT(FluHttpInterceptor)
|
||||
public:
|
||||
explicit FluHttpInterceptor(QObject *parent = nullptr);
|
||||
|
||||
signals:
|
||||
|
||||
};
|
||||
|
||||
#endif // FLUHTTPINTERCEPTOR_H
|
||||
|
28
src/FluRectangle.cpp
Normal file
28
src/FluRectangle.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
#include "FluRectangle.h"
|
||||
#include <QPainterPath>
|
||||
|
||||
FluRectangle::FluRectangle(QQuickItem* parent) : QQuickPaintedItem(parent){
|
||||
setFlag(ItemHasContents, true);
|
||||
color(QColor(255,255,255,255));
|
||||
radius({0,0,0,0});
|
||||
connect(this,&FluRectangle::colorChanged,this,[=]{update();});
|
||||
connect(this,&FluRectangle::radiusChanged,this,[=]{update();});
|
||||
}
|
||||
|
||||
void FluRectangle::paint(QPainter* painter){
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
QPainterPath path;
|
||||
QRectF rect = boundingRect();
|
||||
path.moveTo(rect.bottomRight() - QPointF(0, _radius[2]));
|
||||
path.lineTo(rect.topRight() + QPointF(0, _radius[1]));
|
||||
path.arcTo(QRectF(QPointF(rect.topRight() - QPointF(_radius[1] * 2, 0)), QSize(_radius[1] * 2, _radius[1] * 2)), 0, 90);
|
||||
path.lineTo(rect.topLeft() + QPointF(_radius[0], 0));
|
||||
path.arcTo(QRectF(QPointF(rect.topLeft()), QSize(_radius[0] * 2, _radius[0] * 2)), 90, 90);
|
||||
path.lineTo(rect.bottomLeft() - QPointF(0, _radius[3]));
|
||||
path.arcTo(QRectF(QPointF(rect.bottomLeft() - QPointF(0, _radius[3] * 2)), QSize(_radius[3] * 2, _radius[3] * 2)), 180, 90);
|
||||
path.lineTo(rect.bottomRight() - QPointF(_radius[2], 0));
|
||||
path.arcTo(QRectF(QPointF(rect.bottomRight() - QPointF(_radius[2] * 2, _radius[2] * 2)), QSize(_radius[2] * 2, _radius[2] * 2)), 270, 90);
|
||||
painter->fillPath(path,_color);
|
||||
painter->restore();
|
||||
}
|
20
src/FluRectangle.h
Normal file
20
src/FluRectangle.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef FLURECTANGLE_H
|
||||
#define FLURECTANGLE_H
|
||||
|
||||
#include <QQuickItem>
|
||||
#include <QQuickPaintedItem>
|
||||
#include <QPainter>
|
||||
#include "stdafx.h"
|
||||
|
||||
class FluRectangle : public QQuickPaintedItem
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY_AUTO(QColor,color)
|
||||
Q_PROPERTY_AUTO(QList<int>,radius)
|
||||
QML_NAMED_ELEMENT(FluRectangle)
|
||||
public:
|
||||
explicit FluRectangle(QQuickItem *parent = nullptr);
|
||||
void paint(QPainter* painter) override;
|
||||
};
|
||||
|
||||
#endif // FLURECTANGLE_H
|
@ -3,9 +3,7 @@
|
||||
#include "FluApp.h"
|
||||
#include <QCoreApplication>
|
||||
|
||||
FluRegister::FluRegister(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
FluRegister::FluRegister(QObject *parent):QObject{parent}{
|
||||
from(nullptr);
|
||||
to(nullptr);
|
||||
path("");
|
||||
|
@ -17,25 +17,9 @@ class FluRegister : public QObject
|
||||
Q_PROPERTY_AUTO(QString,path);
|
||||
public:
|
||||
explicit FluRegister(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief launch 窗口跳转
|
||||
* @param argument 跳转携带参数
|
||||
*/
|
||||
Q_INVOKABLE void launch(const QJsonObject& argument = {});
|
||||
|
||||
/**
|
||||
* @brief onResult 将结果数据回传到上一个窗口
|
||||
* @param data 结果数据
|
||||
*/
|
||||
Q_INVOKABLE void onResult(const QJsonObject& data = {});
|
||||
|
||||
/**
|
||||
* @brief result 收到结果数据的信号
|
||||
* @param data 结果数据
|
||||
*/
|
||||
Q_SIGNAL void result(const QJsonObject& data);
|
||||
|
||||
};
|
||||
|
||||
#endif // FLUREGISTER_H
|
||||
|
@ -1,19 +1,6 @@
|
||||
#include "FluTextStyle.h"
|
||||
|
||||
FluTextStyle* FluTextStyle::m_instance = nullptr;
|
||||
|
||||
FluTextStyle *FluTextStyle::getInstance()
|
||||
{
|
||||
if(FluTextStyle::m_instance == nullptr){
|
||||
FluTextStyle::m_instance = new FluTextStyle;
|
||||
}
|
||||
return FluTextStyle::m_instance;
|
||||
}
|
||||
|
||||
|
||||
FluTextStyle::FluTextStyle(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
FluTextStyle::FluTextStyle(QObject *parent):QObject{parent}{
|
||||
QFont caption;
|
||||
caption.setPixelSize(12);
|
||||
Caption(caption);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QtQml/qqml.h>
|
||||
#include <QFont>
|
||||
#include "stdafx.h"
|
||||
#include "singleton.h"
|
||||
|
||||
class FluTextStyle : public QObject
|
||||
{
|
||||
@ -21,13 +22,9 @@ public:
|
||||
QML_SINGLETON
|
||||
private:
|
||||
explicit FluTextStyle(QObject *parent = nullptr);
|
||||
static FluTextStyle* m_instance;
|
||||
public:
|
||||
static FluTextStyle *getInstance();
|
||||
static FluTextStyle *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
|
||||
{
|
||||
return getInstance();
|
||||
}
|
||||
SINGLETONG(FluTextStyle)
|
||||
static FluTextStyle *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
|
||||
};
|
||||
|
||||
#endif // FLUTEXTSTYLE_H
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "FluTheme.h"
|
||||
|
||||
#include "Def.h"
|
||||
#include "FluColors.h"
|
||||
#include <QGuiApplication>
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
|
||||
#include <QStyleHints>
|
||||
#elif ((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)))
|
||||
@ -10,35 +9,22 @@
|
||||
#else
|
||||
#include <QPalette>
|
||||
#endif
|
||||
#include "Def.h"
|
||||
#include "FluColors.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
|
||||
FluTheme* FluTheme::m_instance = nullptr;
|
||||
|
||||
FluTheme *FluTheme::getInstance()
|
||||
{
|
||||
if(FluTheme::m_instance == nullptr){
|
||||
FluTheme::m_instance = new FluTheme;
|
||||
}
|
||||
return FluTheme::m_instance;
|
||||
}
|
||||
|
||||
FluTheme::FluTheme(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
FluTheme::FluTheme(QObject *parent):QObject{parent}{
|
||||
connect(this,&FluTheme::darkModeChanged,this,[=]{
|
||||
Q_EMIT darkChanged();
|
||||
});
|
||||
primaryColor(FluColors::getInstance()->Blue());
|
||||
nativeText(false);
|
||||
enableAnimation(false);
|
||||
enableAnimation(true);
|
||||
darkMode(FluThemeType::DarkMode::Light);
|
||||
_systemDark = systemDark();
|
||||
qApp->installEventFilter(this);
|
||||
}
|
||||
|
||||
bool FluTheme::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
bool FluTheme::eventFilter(QObject *obj, QEvent *event){
|
||||
Q_UNUSED(obj);
|
||||
if (event->type() == QEvent::ApplicationPaletteChange || event->type() == QEvent::ThemeChange)
|
||||
{
|
||||
@ -50,8 +36,7 @@ bool FluTheme::eventFilter(QObject *obj, QEvent *event)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FluTheme::systemDark()
|
||||
{
|
||||
bool FluTheme::systemDark(){
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
|
||||
return (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark);
|
||||
#elif ((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)))
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <QtQml/qqml.h>
|
||||
#include "FluColorSet.h"
|
||||
#include "stdafx.h"
|
||||
#include "singleton.h"
|
||||
|
||||
/**
|
||||
* @brief The FluTheme class
|
||||
@ -12,49 +13,25 @@
|
||||
class FluTheme : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
/**
|
||||
* @brief dark 改变窗口夜间样式,只读属性,可以通过darkMode切换
|
||||
*/
|
||||
Q_PROPERTY(bool dark READ dark NOTIFY darkChanged)
|
||||
|
||||
/**
|
||||
* @brief primaryColor 主题颜色
|
||||
*/
|
||||
Q_PROPERTY_AUTO(FluColorSet*,primaryColor)
|
||||
|
||||
/**
|
||||
* @brief darkMode 夜间模式,支持System=0、Light=1、Dark=2
|
||||
*/
|
||||
Q_PROPERTY_AUTO(int,darkMode);
|
||||
|
||||
/**
|
||||
* @brief nativeText 本地渲染文本
|
||||
*/
|
||||
Q_PROPERTY_AUTO(bool,nativeText);
|
||||
|
||||
/**
|
||||
* @brief 是否开启动画效果
|
||||
*/
|
||||
Q_PROPERTY_AUTO(bool,enableAnimation);
|
||||
|
||||
QML_NAMED_ELEMENT(FluTheme)
|
||||
QML_SINGLETON
|
||||
private:
|
||||
static FluTheme* m_instance;
|
||||
explicit FluTheme(QObject *parent = nullptr);
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
bool systemDark();
|
||||
public:
|
||||
static FluTheme *getInstance();
|
||||
static FluTheme *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
|
||||
{
|
||||
return getInstance();
|
||||
}
|
||||
SINGLETONG(FluTheme)
|
||||
static FluTheme *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
|
||||
bool dark();
|
||||
Q_SIGNAL void darkChanged();
|
||||
private:
|
||||
bool _dark;
|
||||
bool _systemDark;
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
bool systemDark();
|
||||
};
|
||||
|
||||
#endif // FLUTHEME_H
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "FluTools.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QClipboard>
|
||||
#include <QUuid>
|
||||
@ -6,22 +7,15 @@
|
||||
#include <QScreen>
|
||||
#include <QColor>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
#include <QDir>
|
||||
#include <QOpenGLContext>
|
||||
#include <QCryptographicHash>
|
||||
#include <QTextDocument>
|
||||
#include <QQuickWindow>
|
||||
#include <QDateTime>
|
||||
|
||||
FluTools* FluTools::m_instance = nullptr;
|
||||
|
||||
FluTools *FluTools::getInstance()
|
||||
{
|
||||
if(FluTools::m_instance == nullptr){
|
||||
FluTools::m_instance = new FluTools;
|
||||
}
|
||||
return FluTools::m_instance;
|
||||
}
|
||||
|
||||
|
||||
FluTools::FluTools(QObject *parent)
|
||||
: QObject{parent}
|
||||
{
|
||||
FluTools::FluTools(QObject *parent):QObject{parent}{
|
||||
|
||||
}
|
||||
|
||||
@ -33,8 +27,7 @@ QString FluTools::uuid(){
|
||||
return QUuid::createUuid().toString();
|
||||
}
|
||||
|
||||
QString FluTools::readFile(const QString &fileName)
|
||||
{
|
||||
QString FluTools::readFile(const QString &fileName){
|
||||
QString content;
|
||||
QFile file(fileName);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
@ -128,3 +121,57 @@ 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();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
bool FluTools::isSoftware(){
|
||||
return QQuickWindow::sceneGraphBackend() == "software";
|
||||
}
|
||||
|
||||
QPoint FluTools::cursorPos(){
|
||||
return QCursor::pos();
|
||||
}
|
||||
|
||||
qint64 FluTools::currentTimestamp(){
|
||||
return QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
121
src/FluTools.h
121
src/FluTools.h
@ -5,6 +5,7 @@
|
||||
#include <QFile>
|
||||
#include <QColor>
|
||||
#include <QtQml/qqml.h>
|
||||
#include "singleton.h"
|
||||
|
||||
/**
|
||||
* @brief The FluTools class
|
||||
@ -12,138 +13,42 @@
|
||||
class FluTools : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QML_NAMED_ELEMENT(FluTools)
|
||||
QML_SINGLETON
|
||||
private:
|
||||
explicit FluTools(QObject *parent = nullptr);
|
||||
static FluTools* m_instance;
|
||||
public:
|
||||
static FluTools *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine)
|
||||
{
|
||||
return getInstance();
|
||||
}
|
||||
static FluTools *getInstance();
|
||||
|
||||
/**
|
||||
* @brief qtMajor Qt Major版本
|
||||
* @return
|
||||
*/
|
||||
SINGLETONG(FluTools)
|
||||
static FluTools *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
|
||||
Q_INVOKABLE int qtMajor();
|
||||
|
||||
/**
|
||||
* @brief qtMajor Qt Minor版本
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE int qtMinor();
|
||||
|
||||
/**
|
||||
* @brief isMacos 是否是Macos系统
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE bool isMacos();
|
||||
|
||||
/**
|
||||
* @brief isLinux 是否是Linux系统
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE bool isLinux();
|
||||
|
||||
/**
|
||||
* @brief isWin 是否是Windows系统
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE bool isWin();
|
||||
|
||||
/**
|
||||
* @brief clipText 将字符串添加到剪切板
|
||||
* @param text
|
||||
*/
|
||||
Q_INVOKABLE void clipText(const QString& text);
|
||||
|
||||
/**
|
||||
* @brief uuid 获取uuid
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE QString uuid();
|
||||
|
||||
/**
|
||||
* @brief readFile 读取文件内容
|
||||
* @param fileName
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE QString readFile(const QString& fileName);
|
||||
|
||||
/**
|
||||
* @brief setQuitOnLastWindowClosed 设置关闭最后一个窗口是否退出程序
|
||||
* @param val
|
||||
*/
|
||||
Q_INVOKABLE void setQuitOnLastWindowClosed(bool val);
|
||||
|
||||
/**
|
||||
* @brief setOverrideCursor 设置全局鼠标样式
|
||||
* @param shape
|
||||
*/
|
||||
Q_INVOKABLE void setOverrideCursor(Qt::CursorShape shape);
|
||||
|
||||
/**
|
||||
* @brief restoreOverrideCursor 还原全局鼠标样式
|
||||
*/
|
||||
Q_INVOKABLE void restoreOverrideCursor();
|
||||
|
||||
/**
|
||||
* @brief html2PlantText 将html转换成纯文本
|
||||
* @param html
|
||||
*/
|
||||
Q_INVOKABLE QString html2PlantText(const QString& html);
|
||||
|
||||
/**
|
||||
* @brief toLocalPath 获取文件路径,可以去掉windows系统下的file:///,macos下的file://
|
||||
* @param url
|
||||
* @return 返回文件路径
|
||||
*/
|
||||
Q_INVOKABLE QString toLocalPath(const QUrl& url);
|
||||
|
||||
/**
|
||||
* @brief deleteItem 销毁Item对象
|
||||
* @param p
|
||||
*/
|
||||
Q_INVOKABLE void deleteItem(QObject *p);
|
||||
|
||||
/**
|
||||
* @brief getFileNameByUrl
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE QString getFileNameByUrl(const QUrl& url);
|
||||
|
||||
/**
|
||||
* @brief getVirtualGeometry
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE QRect getVirtualGeometry();
|
||||
|
||||
/**
|
||||
* @brief getApplicationDirPath
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE QString getApplicationDirPath();
|
||||
|
||||
/**
|
||||
* @brief getUrlByFilePath
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE QUrl getUrlByFilePath(const QString& path);
|
||||
|
||||
/**
|
||||
* @brief colorAlpha
|
||||
* @param color
|
||||
* @param alpha
|
||||
* @return
|
||||
*/
|
||||
Q_INVOKABLE QColor colorAlpha(const QColor&,qreal alpha);
|
||||
|
||||
Q_INVOKABLE QString md5(QString text);
|
||||
Q_INVOKABLE QString sha256(QString text);
|
||||
Q_INVOKABLE QString toBase64(QString text);
|
||||
Q_INVOKABLE QString fromBase64(QString text);
|
||||
Q_INVOKABLE bool removeDir(QString dirPath);
|
||||
Q_INVOKABLE bool removeFile(QString filePath);
|
||||
Q_INVOKABLE void showFileInFolder(QString path);
|
||||
Q_INVOKABLE bool isSoftware();
|
||||
Q_INVOKABLE qint64 currentTimestamp();
|
||||
Q_INVOKABLE QPoint cursorPos();
|
||||
};
|
||||
|
||||
#endif // FLUTOOLS_H
|
||||
|
368
src/FluTreeModel.cpp
Normal file
368
src/FluTreeModel.cpp
Normal file
@ -0,0 +1,368 @@
|
||||
#include "FluTreeModel.h"
|
||||
|
||||
#include <QMetaEnum>
|
||||
|
||||
Node::Node(QObject *parent): QObject{parent}{
|
||||
}
|
||||
|
||||
FluTreeModel::FluTreeModel(QObject *parent): QAbstractItemModel{parent}{
|
||||
dataSourceSize(0);
|
||||
}
|
||||
|
||||
QModelIndex FluTreeModel::parent(const QModelIndex &child) const{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex FluTreeModel::index(int row, int column,const QModelIndex &parent) const{
|
||||
if (!hasIndex(row, column, parent) || parent.isValid())
|
||||
return QModelIndex();
|
||||
return createIndex(row, column, _rows.at(row));
|
||||
}
|
||||
|
||||
int FluTreeModel::rowCount(const QModelIndex &parent) const {
|
||||
return _rows.count();
|
||||
};
|
||||
|
||||
int FluTreeModel::columnCount(const QModelIndex &parent) const {
|
||||
return 1;;
|
||||
};
|
||||
|
||||
QVariant FluTreeModel::data(const QModelIndex &index, int role) const {
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
return QVariant::fromValue(_rows.at(index.row()));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QVariant();
|
||||
};
|
||||
|
||||
QHash<int, QByteArray> FluTreeModel::roleNames() const {
|
||||
return { {Qt::DisplayRole, "modelData"} };
|
||||
};
|
||||
|
||||
void FluTreeModel::setData(QList<Node*> data){
|
||||
beginResetModel();
|
||||
_rows = data;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void FluTreeModel::removeRows(int row,int count){
|
||||
if (row < 0 || row + count > _rows.size() || count==0)
|
||||
return;
|
||||
beginRemoveRows(QModelIndex(),row, row + count - 1);
|
||||
QList<Node*> firstPart = _rows.mid(0,row);
|
||||
QList<Node*> secondPart = _rows.mid(row + count);
|
||||
_rows.clear();
|
||||
_rows.append(firstPart);
|
||||
_rows.append(secondPart);
|
||||
endRemoveRows();
|
||||
}
|
||||
|
||||
void FluTreeModel::insertRows(int row,QList<Node*> data){
|
||||
if (row < 0 || row > _rows.size() || data.size() == 0)
|
||||
return;;
|
||||
beginInsertRows(QModelIndex(), row, row + data.size() - 1);
|
||||
QList<Node*> firstPart = _rows.mid(0, row);
|
||||
QList<Node*> secondPart = _rows.mid(row);
|
||||
_rows.clear();
|
||||
_rows.append(firstPart);
|
||||
_rows.append(data);
|
||||
_rows.append(secondPart);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
QObject* FluTreeModel::getRow(int row){
|
||||
return _rows.at(row);
|
||||
}
|
||||
|
||||
void FluTreeModel::checkRow(int row,bool chekced){
|
||||
auto itemData = _rows.at(row);
|
||||
if(itemData->hasChildren()){
|
||||
QList<Node*> stack = itemData->_children;
|
||||
std::reverse(stack.begin(), stack.end());
|
||||
while (stack.count() > 0) {
|
||||
auto item = stack.at(stack.count()-1);
|
||||
stack.pop_back();
|
||||
if(!item->hasChildren()){
|
||||
item->_checked = chekced;
|
||||
}
|
||||
QList<Node*> children = item->_children;
|
||||
if(!children.isEmpty()){
|
||||
std::reverse(children.begin(), children.end());
|
||||
foreach (auto c, children) {
|
||||
stack.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(itemData->_checked == chekced){
|
||||
return;
|
||||
}
|
||||
itemData->_checked = chekced;
|
||||
}
|
||||
Q_EMIT layoutChanged(QList<QPersistentModelIndex>(),QAbstractItemModel::VerticalSortHint);
|
||||
QList<Node*> data;
|
||||
foreach (auto item, _dataSource) {
|
||||
if(!item->hasChildren()){
|
||||
if(item->_checked){
|
||||
data.append(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
selectionModel(data);
|
||||
}
|
||||
|
||||
void FluTreeModel::setDataSource(QList<QMap<QString,QVariant>> data){
|
||||
_dataSource.clear();
|
||||
if(_root){
|
||||
delete _root;
|
||||
_root = nullptr;
|
||||
}
|
||||
_root = new Node(this);
|
||||
std::reverse(data.begin(), data.end());
|
||||
while (data.count() > 0) {
|
||||
auto item = data.at(data.count()-1);
|
||||
data.pop_back();
|
||||
Node* node = new Node(this);
|
||||
node->_title = item.value("title").toString();
|
||||
node->_key = item.value("key").toString();
|
||||
node->_depth = item.value("__depth").toInt();
|
||||
node->_parent = item.value("__parent").value<Node*>();
|
||||
node->_isExpanded = true;
|
||||
if(node->_parent){
|
||||
node->_parent->_children.append(node);
|
||||
}else{
|
||||
node->_parent = _root;
|
||||
_root->_children.append(node);
|
||||
}
|
||||
_dataSource.append(node);
|
||||
if (item.contains("children")) {
|
||||
QList<QVariant> children = item.value("children").toList();
|
||||
if(!children.isEmpty()){
|
||||
std::reverse(children.begin(), children.end());
|
||||
for (int i = 0; i < children.count(); ++i) {
|
||||
auto child = children.at(i).toMap();
|
||||
child.insert("__depth",item.value("__depth").toInt(0)+1);
|
||||
child.insert("__parent",QVariant::fromValue(node));
|
||||
data.append(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
beginResetModel();
|
||||
_rows = _dataSource;
|
||||
endResetModel();
|
||||
dataSourceSize(_dataSource.size());
|
||||
}
|
||||
|
||||
void FluTreeModel::collapse(int row){
|
||||
if(!_rows.at(row)->_isExpanded){
|
||||
return;
|
||||
}
|
||||
_rows.at(row)->_isExpanded = false;
|
||||
Q_EMIT dataChanged(index(row,0),index(row,0));
|
||||
auto modelData = _rows.at(row);
|
||||
int removeCount = 0;
|
||||
for(int i=row+1;i<_rows.count();i++){
|
||||
auto obj = _rows[i];
|
||||
if(obj->_depth<=modelData->_depth){
|
||||
break;
|
||||
}
|
||||
removeCount = removeCount + 1;
|
||||
}
|
||||
removeRows(row+1,removeCount);
|
||||
}
|
||||
|
||||
void FluTreeModel::expand(int row){
|
||||
if(_rows.at(row)->_isExpanded){
|
||||
return;
|
||||
}
|
||||
_rows.at(row)->_isExpanded = true;
|
||||
Q_EMIT dataChanged(index(row,0),index(row,0));
|
||||
auto modelData = _rows.at(row);
|
||||
QList<Node*> insertData;
|
||||
QList<Node*> stack = modelData->_children;
|
||||
std::reverse(stack.begin(), stack.end());
|
||||
while (stack.count() > 0) {
|
||||
auto item = stack.at(stack.count()-1);
|
||||
stack.pop_back();
|
||||
if(item->isShown()){
|
||||
insertData.append(item);
|
||||
}
|
||||
QList<Node*> children = item->_children;
|
||||
if(!children.isEmpty()){
|
||||
std::reverse(children.begin(), children.end());
|
||||
foreach (auto c, children) {
|
||||
stack.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
insertRows(row+1,insertData);
|
||||
}
|
||||
|
||||
void FluTreeModel::dragAnddrop(int dragIndex,int dropIndex,bool isDropTopArea){
|
||||
if(dropIndex>_rows.count() || dropIndex<0){
|
||||
return;
|
||||
}
|
||||
auto dragItem = _rows[dragIndex];
|
||||
auto dropItem = _rows[dropIndex];
|
||||
int targetIndex;
|
||||
if(dropIndex > dragIndex){
|
||||
if(isDropTopArea){
|
||||
targetIndex = dropIndex;
|
||||
}else{
|
||||
targetIndex = dropIndex+1;
|
||||
}
|
||||
}else{
|
||||
if(isDropTopArea){
|
||||
targetIndex = dropIndex;
|
||||
}else{
|
||||
targetIndex = dropIndex+1;
|
||||
}
|
||||
}
|
||||
if (!beginMoveRows(QModelIndex(), dragIndex, dragIndex, QModelIndex(), targetIndex)) {
|
||||
return;
|
||||
}
|
||||
if(dropIndex > dragIndex){
|
||||
if(isDropTopArea){
|
||||
targetIndex = dropIndex-1;
|
||||
}else{
|
||||
targetIndex = dropIndex;
|
||||
}
|
||||
}else{
|
||||
if(isDropTopArea){
|
||||
targetIndex = dropIndex;
|
||||
}else{
|
||||
targetIndex = dropIndex+1;
|
||||
}
|
||||
}
|
||||
_rows.move(dragIndex,targetIndex);
|
||||
endMoveRows();
|
||||
|
||||
Q_EMIT layoutAboutToBeChanged();
|
||||
if(dragItem->_parent == dropItem->_parent){
|
||||
QList<Node*>* children = &(dragItem->_parent->_children);
|
||||
int srcIndex = children->indexOf(dragItem);
|
||||
int destIndex = children->indexOf(dropItem);
|
||||
if(dropIndex > dragIndex){
|
||||
if(isDropTopArea){
|
||||
targetIndex = destIndex-1;
|
||||
}else{
|
||||
targetIndex = destIndex;
|
||||
}
|
||||
}else{
|
||||
if(isDropTopArea){
|
||||
targetIndex = destIndex;
|
||||
}else{
|
||||
targetIndex = destIndex+1;
|
||||
}
|
||||
}
|
||||
children->move(srcIndex,targetIndex);
|
||||
}else{
|
||||
QList<Node*>* srcChildren = &(dragItem->_parent->_children);
|
||||
QList<Node*>* destChildren = &(dropItem->_parent->_children);
|
||||
int srcIndex = srcChildren->indexOf(dragItem);
|
||||
int destIndex = destChildren->indexOf(dropItem);
|
||||
dragItem->_depth = dropItem->_depth;
|
||||
dragItem->_parent = dropItem->_parent;
|
||||
if(dragItem->hasChildren()){
|
||||
QList<Node*> stack = dragItem->_children;
|
||||
foreach (auto node, stack) {
|
||||
node->_depth = dragItem->_depth+1;
|
||||
}
|
||||
std::reverse(stack.begin(), stack.end());
|
||||
while (stack.count() > 0) {
|
||||
auto item = stack.at(stack.count()-1);
|
||||
stack.pop_back();
|
||||
QList<Node*> children = item->_children;
|
||||
if(!children.isEmpty()){
|
||||
std::reverse(children.begin(), children.end());
|
||||
foreach (auto c, children) {
|
||||
c->_depth = item->_depth+1;
|
||||
stack.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
srcChildren->removeAt(srcIndex);
|
||||
if(dropIndex > dragIndex){
|
||||
if(isDropTopArea){
|
||||
targetIndex = destIndex;
|
||||
}else{
|
||||
targetIndex = destIndex + 1;
|
||||
}
|
||||
}else{
|
||||
if(isDropTopArea){
|
||||
targetIndex = destIndex;
|
||||
}else{
|
||||
targetIndex = destIndex + 1;
|
||||
}
|
||||
}
|
||||
destChildren->insert(targetIndex,dragItem);
|
||||
}
|
||||
changePersistentIndex(index(qMin(dragIndex,dropIndex),0),index(qMax(dragIndex,dropIndex),0));
|
||||
Q_EMIT layoutChanged(QList<QPersistentModelIndex>(),QAbstractItemModel::VerticalSortHint);
|
||||
|
||||
}
|
||||
|
||||
bool FluTreeModel::hitHasChildrenExpanded(int row){
|
||||
auto itemData = _rows.at(row);
|
||||
if(itemData->hasChildren() && itemData->_isExpanded){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FluTreeModel::refreshNode(int row){
|
||||
Q_EMIT dataChanged(index(row,0),index(row,0));
|
||||
};
|
||||
|
||||
Node* FluTreeModel::getNode(int row){
|
||||
return _rows.at(row);
|
||||
}
|
||||
|
||||
void FluTreeModel::allExpand(){
|
||||
beginResetModel();
|
||||
QList<Node*> data;
|
||||
QList<Node*> stack = _root->_children;
|
||||
std::reverse(stack.begin(), stack.end());
|
||||
while (stack.count() > 0) {
|
||||
auto item = stack.at(stack.count()-1);
|
||||
stack.pop_back();
|
||||
if(item->hasChildren()){
|
||||
item->_isExpanded = true;
|
||||
}
|
||||
data.append(item);
|
||||
QList<Node*> children = item->_children;
|
||||
if(!children.isEmpty()){
|
||||
std::reverse(children.begin(), children.end());
|
||||
foreach (auto c, children) {
|
||||
stack.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
_rows = data;
|
||||
endResetModel();
|
||||
}
|
||||
void FluTreeModel::allCollapse(){
|
||||
beginResetModel();
|
||||
QList<Node*> stack = _root->_children;
|
||||
std::reverse(stack.begin(), stack.end());
|
||||
while (stack.count() > 0) {
|
||||
auto item = stack.at(stack.count()-1);
|
||||
stack.pop_back();
|
||||
if(item->hasChildren()){
|
||||
item->_isExpanded = false;
|
||||
}
|
||||
QList<Node*> children = item->_children;
|
||||
if(!children.isEmpty()){
|
||||
std::reverse(children.begin(), children.end());
|
||||
foreach (auto c, children) {
|
||||
stack.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
_rows = _root->_children;
|
||||
endResetModel();
|
||||
}
|
115
src/FluTreeModel.h
Normal file
115
src/FluTreeModel.h
Normal file
@ -0,0 +1,115 @@
|
||||
#ifndef FLUTREEMODEL_H
|
||||
#define FLUTREEMODEL_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QAbstractTableModel>
|
||||
#include <QJsonArray>
|
||||
#include <QtQml/qqml.h>
|
||||
#include "stdafx.h"
|
||||
|
||||
class Node : public QObject{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString key READ key CONSTANT)
|
||||
Q_PROPERTY(QString title READ title CONSTANT)
|
||||
Q_PROPERTY(int depth READ depth CONSTANT)
|
||||
Q_PROPERTY(bool isExpanded READ isExpanded CONSTANT)
|
||||
Q_PROPERTY(bool checked READ checked CONSTANT)
|
||||
public:
|
||||
explicit Node(QObject *parent = nullptr);
|
||||
Q_INVOKABLE QString key(){return _key;};
|
||||
Q_INVOKABLE QString title(){return _title;};
|
||||
Q_INVOKABLE int depth(){return _depth;};
|
||||
Q_INVOKABLE bool isExpanded(){return _isExpanded;};
|
||||
Q_INVOKABLE bool hasChildren(){ return !_children.isEmpty();};
|
||||
Q_INVOKABLE bool hasNextNodeByIndex(int index){
|
||||
Node* p = this;
|
||||
for(int i=0;i<(_depth - index -1);i++){
|
||||
p = p->_parent;
|
||||
}
|
||||
if(p->_parent->_children.indexOf(p) == p->_parent->_children.count()-1){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Q_INVOKABLE bool checked(){
|
||||
if(!hasChildren()){
|
||||
return _checked;
|
||||
}
|
||||
foreach (auto item, _children) {
|
||||
if(!item->checked()){
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
Q_INVOKABLE bool hideLineFooter(){
|
||||
if(_parent){
|
||||
auto childIndex = _parent->_children.indexOf(this);
|
||||
if(childIndex==_parent->_children.count()-1){
|
||||
return true;
|
||||
}
|
||||
if(_parent->_children.at(childIndex+1)->hasChildren()){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
bool isShown(){
|
||||
auto p = _parent;
|
||||
while (p) {
|
||||
if(!p->_isExpanded){
|
||||
return false;
|
||||
}
|
||||
p = p->_parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public:
|
||||
QString _key="";
|
||||
QString _title="";
|
||||
int _depth=0;
|
||||
bool _checked = false;
|
||||
bool _isExpanded=true;
|
||||
QList<Node*> _children;
|
||||
Node* _parent = nullptr;
|
||||
};
|
||||
|
||||
class FluTreeModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY_AUTO(int,dataSourceSize)
|
||||
Q_PROPERTY_AUTO(QList<Node*>,selectionModel)
|
||||
QML_NAMED_ELEMENT(FluTreeModel)
|
||||
QML_ADDED_IN_MINOR_VERSION(1)
|
||||
public:
|
||||
explicit FluTreeModel(QObject *parent = nullptr);
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
QModelIndex parent(const QModelIndex &child) const override;
|
||||
QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
Q_INVOKABLE void removeRows(int row,int count);
|
||||
Q_INVOKABLE void insertRows(int row,QList<Node*> data);
|
||||
Q_INVOKABLE QObject* getRow(int row);
|
||||
Q_INVOKABLE void setData(QList<Node*> data);
|
||||
Q_INVOKABLE void setDataSource(QList<QMap<QString,QVariant>> data);
|
||||
Q_INVOKABLE void collapse(int row);
|
||||
Q_INVOKABLE void expand(int row);
|
||||
Q_INVOKABLE void dragAnddrop(int dragIndex,int dropIndex,bool isDropTopArea);
|
||||
Q_INVOKABLE Node* getNode(int row);
|
||||
Q_INVOKABLE void refreshNode(int row);
|
||||
Q_INVOKABLE void checkRow(int row,bool chekced);
|
||||
Q_INVOKABLE bool hitHasChildrenExpanded(int row);
|
||||
Q_INVOKABLE void allExpand();
|
||||
Q_INVOKABLE void allCollapse();
|
||||
private:
|
||||
QList<Node*> _rows;
|
||||
QList<Node*> _dataSource;
|
||||
Node* _root = nullptr;
|
||||
};
|
||||
|
||||
#endif // FLUTREEMODEL_H
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user