Compare commits

...

41 Commits
1.5.6 ... 1.5.9

Author SHA1 Message Date
7a1776407f update 2023-09-23 15:55:29 +08:00
f88b330f8e update 2023-09-22 17:35:02 +08:00
67ef7f13aa update 2023-09-22 09:48:54 +08:00
23ec52ce6a update 2023-09-22 09:31:47 +08:00
5b7fdab1d9 update 2023-09-22 09:21:17 +08:00
4c1a96c03e update 2023-09-22 01:00:32 +08:00
ab4090ea9b update 2023-09-22 00:40:09 +08:00
8fb2ef723e update 2023-09-22 00:31:25 +08:00
77d9b4bde9 update 2023-09-22 00:11:58 +08:00
a96191b2af update 2023-09-21 18:29:09 +08:00
28e1799ca4 update 2023-09-20 18:38:15 +08:00
8337e278ff update 2023-09-19 23:41:56 +08:00
7ad8c969da update 2023-09-19 18:31:29 +08:00
66ae37a023 update 2023-09-19 00:31:49 +08:00
b27a88d261 update 2023-09-18 18:10:27 +08:00
257f3a7b3d update 2023-09-18 00:12:39 +08:00
4710379324 update 2023-09-17 21:34:02 +08:00
8fc74fe43b update 2023-09-17 20:36:33 +08:00
be194e7624 update 2023-09-15 19:11:55 +08:00
e6d9de34ea update 2023-09-15 01:28:03 +08:00
c47fa5ebc7 update 2023-09-14 18:50:36 +08:00
af74f35e43 update 2023-09-13 21:24:02 +08:00
3b61985cfe update 2023-09-13 18:12:58 +08:00
eb96cf5b47 update 2023-09-13 17:27:09 +08:00
d48ad16ae3 update 2023-09-13 16:08:54 +08:00
39fb4d1b1a update 2023-09-13 15:43:31 +08:00
674de3f881 update 2023-09-13 15:26:21 +08:00
0c2b3173eb update 2023-09-13 15:21:07 +08:00
b2471bcf0d update 2023-09-13 15:11:22 +08:00
79a7c97fe8 update 2023-09-12 22:55:42 +08:00
fd30819393 update 2023-09-12 18:45:15 +08:00
05040ec4a9 update 2023-09-11 18:10:50 +08:00
0297445218 update 2023-09-10 17:05:36 +08:00
3990c95489 update 2023-09-10 16:58:23 +08:00
618b21854f Compatible with static build 2023-09-09 20:09:20 +08:00
ef40e3b109 update 2023-09-09 10:19:47 +08:00
f2b67af58a update 2023-09-08 23:01:31 +08:00
c0f15060af update 2023-09-08 22:43:52 +08:00
24f3cb1027 update 2023-09-08 22:33:23 +08:00
4b01fcf2b4 update 2023-09-08 19:14:45 +08:00
752fe8cfba update 2023-09-08 17:39:10 +08:00
194 changed files with 6391 additions and 2716 deletions

View File

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

View File

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

View File

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

85
.github/workflows/windows-qt5.yml vendored Normal file
View 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

View File

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

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

Binary file not shown.

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

Binary file not shown.

View File

@ -288,3 +288,28 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
************************************************************************************
qml-colorpicker
MIT License
Copyright (c) 2022 Ruslan Shestopalyuk
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -53,7 +53,7 @@ endforeach(filepath)
if(QT_VERSION VERSION_GREATER_EQUAL "6.2") if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
#遍历所有qml文件 #遍历所有qml文件
file(GLOB_RECURSE QML_PATHS *.qml qmldir) file(GLOB_RECURSE QML_PATHS *.qml)
foreach(filepath ${QML_PATHS}) foreach(filepath ${QML_PATHS})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
if(${filepath} MATCHES "Qt${QT_VERSION_MAJOR}/") if(${filepath} MATCHES "Qt${QT_VERSION_MAJOR}/")
@ -64,7 +64,7 @@ if(QT_VERSION VERSION_GREATER_EQUAL "6.2")
endforeach(filepath) 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}) foreach(filepath ${RES_PATHS})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
list(APPEND resource_files ${filename}) list(APPEND resource_files ${filename})
@ -93,12 +93,17 @@ else ()
) )
endif () endif ()
#复制动态库到可执行文件同级目录下
if(WIN32) 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") if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(DLLPATH ${CMAKE_SOURCE_DIR}/3rdparty/msvc/*.dll) set(DLLPATH ${3RDPARTY_ARCH_DIR}/msvc/*.dll)
else() else()
set(DLLPATH ${CMAKE_SOURCE_DIR}/3rdparty/mingw/*.dll) set(DLLPATH ${3RDPARTY_ARCH_DIR}/mingw/*.dll)
endif() endif()
string(REPLACE "/" ${PATH_SEPARATOR} DLLPATH "${DLLPATH}") string(REPLACE "/" ${PATH_SEPARATOR} DLLPATH "${DLLPATH}")
file(GLOB DLL_FILES ${DLLPATH}) file(GLOB DLL_FILES ${DLLPATH})
@ -122,7 +127,7 @@ else()
target_include_directories(example PRIVATE target_include_directories(example PRIVATE
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
) )
target_sources(example PRIVATE resource.qrc) target_sources(example PRIVATE example.qrc)
endif() endif()
#导入component头文件,不然通过QML_NAMED_ELEMENT生成的c++类会找不到头文件报错 #导入component头文件,不然通过QML_NAMED_ELEMENT生成的c++类会找不到头文件报错
@ -130,6 +135,13 @@ target_include_directories(example PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/component ${CMAKE_CURRENT_SOURCE_DIR}/src/component
) )
#如何是静态库则需要手动注册插件导入FluentUI.h头文件
if(FLUENTUI_BUILD_STATIC_LIB)
target_include_directories(example PRIVATE
${CMAKE_SOURCE_DIR}/src
)
endif()
#设置属性 #设置属性
set_target_properties(example PROPERTIES set_target_properties(example PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
@ -139,18 +151,6 @@ set_target_properties(example PROPERTIES
WIN32_EXECUTABLE TRUE WIN32_EXECUTABLE TRUE
) )
#链接库
if (FLUENTUI_BUILD_STATIC_LIB)
target_link_libraries(example PRIVATE
Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::Svg
Qt${QT_VERSION_MAJOR}::Network
fluentui
fluentuiplugin
FramelessHelper::Core
FramelessHelper::Quick
)
else()
target_link_libraries(example PRIVATE target_link_libraries(example PRIVATE
Qt${QT_VERSION_MAJOR}::Quick Qt${QT_VERSION_MAJOR}::Quick
Qt${QT_VERSION_MAJOR}::Svg Qt${QT_VERSION_MAJOR}::Svg
@ -159,7 +159,6 @@ else()
FramelessHelper::Core FramelessHelper::Core
FramelessHelper::Quick FramelessHelper::Quick
) )
endif()
#安装 #安装
install(TARGETS example install(TARGETS example

View File

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

View File

@ -8,6 +8,13 @@ Window {
id: app id: app
flags: Qt.SplashScreen flags: Qt.SplashScreen
Connections{
target: FluTheme
function onDarkModeChanged(){
SettingsHelper.saveDarkMode(FluTheme.darkMode)
}
}
FluHttpInterceptor{ FluHttpInterceptor{
id:interceptor id:interceptor
function onIntercept(request){ function onIntercept(request){
@ -26,7 +33,7 @@ Window {
Component.onCompleted: { Component.onCompleted: {
FluApp.init(app) FluApp.init(app)
FluTheme.darkMode = FluThemeType.System FluTheme.darkMode = SettingsHelper.getDarkMode()
FluTheme.enableAnimation = true FluTheme.enableAnimation = true
FluApp.routes = { FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml", "/":"qrc:/example/qml/window/MainWindow.qml",

View File

@ -80,7 +80,6 @@ FluExpander{
"FluIcon", "FluIcon",
"FluIconButton", "FluIconButton",
"FluInfoBar", "FluInfoBar",
"FluItem",
"FluMediaPlayer", "FluMediaPlayer",
"FluMenu", "FluMenu",
"FluMenuItem", "FluMenuItem",
@ -139,7 +138,8 @@ FluExpander{
"FluChart", "FluChart",
"FluRangeSlider", "FluRangeSlider",
"FluStaggeredView", "FluStaggeredView",
"FluProgressButton" "FluProgressButton",
"FluLoadingButton"
]; ];
code = code.replace(/\n/g, "<br>"); code = code.replace(/\n/g, "<br>");
code = code.replace(/ /g, "&nbsp;"); code = code.replace(/ /g, "&nbsp;");

View File

@ -477,6 +477,9 @@ FluObject{
} }
function getSearchData(){ function getSearchData(){
if(!navigationView){
return
}
var arr = [] var arr = []
var items = navigationView.getItems(); var items = navigationView.getItems();
for(var i=0;i<items.length;i++){ for(var i=0;i<items.length;i++){

View File

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

View File

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

View File

@ -47,10 +47,10 @@ FluScrollablePage{
height: 1200/4+20 height: 1200/4+20
paddings: 10 paddings: 10
Layout.topMargin: 10 Layout.topMargin: 10
FluRectangle{ FluClip{
width: 1920/4 width: 1920/4
height: 1200/4 height: 1200/4
radius:[15,15,15,15] radius:[8,8,8,8]
Image { Image {
id:image id:image
asynchronous: true asynchronous: true

View File

@ -213,6 +213,45 @@ FluScrollablePage{
}' }'
} }
FluArea{
Layout.fillWidth: true
height: 68
Layout.topMargin: 20
paddings: 10
FluLoadingButton{
id:btn_loading
loading:loading_button_switch.checked
text:"Loading Button"
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: {
}
}
FluToggleSwitch{
id:loading_button_switch
checked: true
anchors{
right: parent.right
verticalCenter: parent.verticalCenter
}
text:"Loading"
}
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluLoadingButton{
text:"Loading Button"
onClicked: {
}
}'
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true

View File

@ -9,10 +9,10 @@ FluScrollablePage{
title:"Captcha" title:"Captcha"
FluCaptcha{ FluCaptcha{
id:captcha id:captcha
Layout.topMargin: 20 Layout.topMargin: 20
ignoreCase:switch_case.checked
MouseArea{ MouseArea{
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@ -30,6 +30,13 @@ FluScrollablePage{
} }
} }
FluToggleSwitch{
id:switch_case
text:"Ignore Case"
checked: true
Layout.topMargin: 10
}
RowLayout{ RowLayout{
spacing: 10 spacing: 10
Layout.topMargin: 10 Layout.topMargin: 10
@ -49,6 +56,4 @@ FluScrollablePage{
} }
} }
} }
} }

View File

@ -36,8 +36,14 @@ FluScrollablePage{
FluText{ FluText{
text:"轮播图支持无限轮播无限滑动用ListView实现的组件" text:"轮播图支持无限轮播无限滑动用ListView实现的组件"
} }
Item{
width: 400
height: 300
FluShadow{
radius: 8
}
FluCarousel{ FluCarousel{
radius:[5,5,5,5] anchors.fill: parent
delegate: Component{ delegate: Component{
Image { Image {
anchors.fill: parent anchors.fill: parent
@ -54,6 +60,7 @@ FluScrollablePage{
} }
} }
} }
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
@ -66,8 +73,14 @@ FluScrollablePage{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
left:parent.left left:parent.left
} }
Item{
width: 400
height: 300
FluShadow{
radius: 8
}
FluCarousel{ FluCarousel{
radius:[15,15,15,15] anchors.fill: parent
loopTime:1500 loopTime:1500
indicatorGravity: Qt.AlignHCenter | Qt.AlignTop indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
indicatorMarginTop:15 indicatorMarginTop:15
@ -107,6 +120,8 @@ FluScrollablePage{
} }
} }
} }
}
} }
CodeExpander{ CodeExpander{

View File

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

View File

@ -27,6 +27,7 @@ FluScrollablePage{
id: bg id: bg
fillMode:Image.PreserveAspectCrop fillMode:Image.PreserveAspectCrop
anchors.fill: parent anchors.fill: parent
asynchronous: true
verticalAlignment: Qt.AlignTop verticalAlignment: Qt.AlignTop
sourceSize: Qt.size(960,640) sourceSize: Qt.size(960,640)
source: "qrc:/example/res/image/bg_home_header.png" source: "qrc:/example/res/image/bg_home_header.png"
@ -49,33 +50,19 @@ FluScrollablePage{
} }
} }
ListView{ Component{
id: list id:com_grallery
anchors{ Item{
left: parent.left
right: parent.right
bottom: parent.bottom
}
orientation: ListView.Horizontal
height: 240
model: model_header
header: Item{height: 10;width: 10}
footer: Item{height: 10;width: 10}
ScrollBar.horizontal: FluScrollBar{
id: scrollbar_header
}
clip: false
delegate:Item{
id: control id: control
width: 220 width: 220
height: 240 height: 240
FluShadow{ FluShadow{
radius:8 radius:5
anchors.fill: item_content anchors.fill: item_content
} }
FluItem{ FluClip{
id:item_content id:item_content
radius: [8,8,8,8] radius: [5,5,5,5]
width: 200 width: 200
height: 220 height: 220
anchors.centerIn: parent anchors.centerIn: parent
@ -89,20 +76,15 @@ FluScrollablePage{
} }
Rectangle{ Rectangle{
anchors.fill: parent anchors.fill: parent
radius: 8 radius: 5
color:{ color:FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
if(FluTheme.dark){ visible: item_mouse.containsMouse
if(item_mouse.containsMouse){
return Qt.rgba(1,1,1,0.03)
}
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)
}
} }
Rectangle{
anchors.fill: parent
radius: 5
color:Qt.rgba(0,0,0,0.0)
visible: !item_mouse.containsMouse
} }
ColumnLayout{ ColumnLayout{
Image { Image {
@ -154,11 +136,31 @@ FluScrollablePage{
} }
} }
} }
ListView{
id: list
anchors{
left: parent.left
right: parent.right
bottom: parent.bottom
}
orientation: ListView.Horizontal
height: 240
model: model_header
header: Item{height: 10;width: 10}
footer: Item{height: 10;width: 10}
ScrollBar.horizontal: FluScrollBar{
id: scrollbar_header
}
clip: false
delegate: com_grallery
}
} }
Component{ Component{
id:com_item id:com_item
Item{ Item{
property string desc: modelData.desc
width: 320 width: 320
height: 120 height: 120
FluArea{ FluArea{
@ -194,7 +196,6 @@ FluScrollablePage{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
} }
FluText{ FluText{
id:item_title id:item_title
text:modelData.title text:modelData.title
@ -205,10 +206,9 @@ FluScrollablePage{
top: item_icon.top top: item_icon.top
} }
} }
FluText{ FluText{
id:item_desc id:item_desc
text:modelData.desc text:desc
color:FluColors.Grey120 color:FluColors.Grey120
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
elide: Text.ElideRight elide: Text.ElideRight

View File

@ -11,6 +11,7 @@ FluContentPage{
title:"Http" title:"Http"
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http" property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
property bool isDownCompleted: false
FluHttp{ FluHttp{
id:http id:http
@ -171,6 +172,10 @@ FluContentPage{
} }
onSuccess: onSuccess:
(result)=>{ (result)=>{
if(!isDownCompleted){
tour.open()
isDownCompleted = true
}
showSuccess(result) showSuccess(result)
} }
onDownloadProgress: onDownloadProgress:
@ -281,6 +286,13 @@ FluContentPage{
} }
} }
FluTour{
id:tour
steps:[
{title:"友情提示",description: "下载已完成,左击这里可以打开文件所在路径,右击可以弹出菜单删除文件!",target:()=>btn_breakpoint_download}
]
}
HttpCallable{ HttpCallable{
id:callable_upload id:callable_upload
onStart: { onStart: {

View File

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

View File

@ -3,7 +3,6 @@ import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Window import QtQuick.Window
import FluentUI import FluentUI
import Qt5Compat.GraphicalEffects
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
FluScrollablePage{ FluScrollablePage{

View File

@ -68,7 +68,7 @@ FluScrollablePage{
} }
RowLayout{ RowLayout{
spacing: 14 spacing: 14
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[25,0,25,25] radius:[25,0,25,25]
@ -79,7 +79,7 @@ FluScrollablePage{
sourceSize: Qt.size(width,height) sourceSize: Qt.size(width,height)
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[10,10,10,10] radius:[10,10,10,10]
@ -90,7 +90,7 @@ FluScrollablePage{
source: "qrc:/example/res/svg/avatar_2.svg" source: "qrc:/example/res/svg/avatar_2.svg"
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[25,25,25,25] radius:[25,25,25,25]
@ -101,7 +101,7 @@ FluScrollablePage{
source: "qrc:/example/res/svg/avatar_3.svg" source: "qrc:/example/res/svg/avatar_3.svg"
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[0,25,25,25] radius:[0,25,25,25]
@ -113,10 +113,10 @@ FluScrollablePage{
} }
} }
} }
FluRectangle{ FluClip{
width: 1920/5 width: 1920/5
height: 1200/5 height: 1200/5
radius:[15,15,15,15] radius:[8,8,8,8]
Image { Image {
asynchronous: true asynchronous: true
source: "qrc:/example/res/image/banner_1.jpg" source: "qrc:/example/res/image/banner_1.jpg"

View File

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

View File

@ -5,11 +5,90 @@ import QtQuick.Controls
import FluentUI import FluentUI
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/viewmodel"
FluScrollablePage{ FluScrollablePage{
title:"Settings" title:"Settings"
SettingsViewModel{
id:viewmodel_settings
}
FluEvent{
id:event_checkupdate_finish
name: "checkUpdateFinish"
onTriggered: {
btn_checkupdate.loading = false
}
}
Component.onCompleted: {
FluEventBus.registerEvent(event_checkupdate_finish)
}
Component.onDestruction: {
FluEventBus.unRegisterEvent(event_checkupdate_finish)
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 60
paddings: 10
Row{
spacing: 20
anchors.verticalCenter: parent.verticalCenter
FluText{
text:"当前版本 v%1".arg(AppInfo.version)
font: FluTextStyle.Body
anchors.verticalCenter: parent.verticalCenter
}
FluLoadingButton{
id:btn_checkupdate
text:"检查更新"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
loading = true
FluEventBus.post("checkUpdate")
}
}
}
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 50
paddings: 10
FluCheckBox{
text:"Software Render"
checked: SettingsHelper.getReander() === "software"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
if(SettingsHelper.getReander() === "software"){
SettingsHelper.saveRender("")
}else{
SettingsHelper.saveRender("software")
}
dialog_render.open()
}
}
}
FluContentDialog{
id:dialog_render
title:"友情提示"
message:"此操作需要重启才能生效,是否重新启动?"
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText: "取消"
positiveText:"确定"
onPositiveClicked:{
window.deleteWindow()
AppInfo.restart()
}
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
@ -60,10 +139,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}] model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked : MainEvent.displayMode===modelData.mode checked : viewmodel_settings.displayMode===modelData.mode
text:modelData.title text:modelData.title
clickListener:function(){ clickListener:function(){
MainEvent.displayMode = modelData.mode viewmodel_settings.displayMode = modelData.mode
} }
} }
} }
@ -94,10 +173,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: ["Zh","En"] model: ["Zh","En"]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked: appInfo.lang.objectName === modelData checked: AppInfo.lang.objectName === modelData
text:modelData text:modelData
clickListener:function(){ clickListener:function(){
appInfo.changeLang(modelData) AppInfo.changeLang(modelData)
} }
} }
} }

View File

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

View File

@ -5,152 +5,127 @@ import QtQuick.Controls
import FluentUI import FluentUI
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
FluScrollablePage { FluContentPage {
title:"TreeView" title:"TreeView"
function randomName() { function treeData(){
var names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十"] const dig = (path = '0', level = 4) => {
return names[Math.floor(Math.random() * names.length)] const list = [];
for (let i = 0; i < 6; i += 1) {
const key = `${path}-${i}`;
const treeNode = {
title: key,
key,
};
if (level > 0) {
treeNode.children = dig(key, level - 1);
}
list.push(treeNode);
}
return list;
};
return dig();
} }
function randomCompany() { Column{
var companies = ["阿里巴巴", "腾讯", "百度", "京东", "华为", "小米", "字节跳动", "美团", "滴滴"] id:layout_column
return companies[Math.floor(Math.random() * companies.length)] spacing: 12
} width: 300
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())
}
return tree_view.createItem(randomDepartment(), true, employees)
}
function createOrg(numLevels, numSubtrees, numEmployees) {
if (numLevels === 0) {
return []
}
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
}
}
}
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(","))
}
}
}
}
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 10
paddings: 10
height: 400
FluTreeView{
id:tree_view
width:240
anchors{ anchors{
topMargin: 20
top:parent.top top:parent.top
left: parent.left left: parent.left
leftMargin: 10
bottom:parent.bottom bottom:parent.bottom
} bottomMargin: 20
onItemClicked:
(model)=>{
showSuccess(model.text)
} }
Component.onCompleted: { FluText{
var org = createOrg(3, 3, 3) text:"共计%1条数据当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount())
createItem()
updateData(org)
}
}
} }
CodeExpander{ FluText{
Layout.fillWidth: true text:"共计选中%1条数据".arg(tree_view.selectionModel().length)
Layout.topMargin: -1 }
code:'FluTreeView{
RowLayout{
spacing: 10
FluText{
text:"cellHeight:"
Layout.alignment: Qt.AlignVCenter
}
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{
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 id:tree_view
width:240 anchors.fill: parent
height:600 cellHeight: slider_cell_height.value
draggable:switch_draggable.checked
showLine: switch_showline.checked
checkable:switch_checkable.checked
depthPadding: slider_depth_padding.value
Component.onCompleted: { Component.onCompleted: {
var datas = [] var data = treeData()
datas.push(createItem("Node1",false)) dataSource = data
datas.push(createItem("Node2",false))
datas.push(createItem("Node2",true,[createItem("Node2-1",false),createItem("Node2-2",false)]))
updateData(datas)
} }
} }
'
} }
} }

View File

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

View File

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

View File

@ -35,7 +35,7 @@ CustomWindow {
} }
} }
FluText{ FluText{
text:"v%1".arg(appInfo.version) text:"v%1".arg(AppInfo.version)
font: FluTextStyle.Body font: FluTextStyle.Body
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
} }

View File

@ -7,6 +7,7 @@ import FluentUI
import example import example
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/viewmodel"
CustomWindow { CustomWindow {
@ -20,15 +21,32 @@ CustomWindow {
appBarVisible: false appBarVisible: false
launchMode: FluWindowType.SingleTask launchMode: FluWindowType.SingleTask
SettingsViewModel{
id:viewmodel_settings
}
closeFunc:function(event){ closeFunc:function(event){
dialog_close.open() dialog_close.open()
event.accepted = false event.accepted = false
} }
FluEvent{
id:event_checkupdate
name: "checkUpdate"
onTriggered: {
checkUpdate(false)
}
}
Component.onCompleted: { Component.onCompleted: {
FluTools.setQuitOnLastWindowClosed(false) FluTools.setQuitOnLastWindowClosed(false)
tour.open() tour.open()
checkUpdate() checkUpdate(true)
FluEventBus.registerEvent(event_checkupdate)
}
Component.onDestruction: {
FluEventBus.unRegisterEvent(event_checkupdate)
} }
SystemTrayIcon { SystemTrayIcon {
@ -164,13 +182,13 @@ CustomWindow {
height: parent.height height: parent.height
z:999 z:999
//Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式 //Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式
pageMode: FluNavigationViewType.Stack // pageMode: FluNavigationViewType.Stack
//NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐 //NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存可以配合FluViewModel保存页面数据(推荐)
// pageMode: FluNavigationViewType.NoStack pageMode: FluNavigationViewType.NoStack
items: ItemsOriginal items: ItemsOriginal
footerItems:ItemsFooter footerItems:ItemsFooter
topPadding:FluTools.isMacos() ? 20 : 0 topPadding:FluTools.isMacos() ? 20 : 0
displayMode:MainEvent.displayMode displayMode:viewmodel_settings.displayMode
logo: "qrc:/example/res/image/favicon.ico" logo: "qrc:/example/res/image/favicon.ico"
title:"FluentUI" title:"FluentUI"
onLogoClicked:{ onLogoClicked:{
@ -300,7 +318,7 @@ CustomWindow {
property string body property string body
id:dialog_update id:dialog_update
title:"升级提示" title:"升级提示"
message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+appInfo.version+" \n现在是否去下载新版本\n\n更新内容\n"+body message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+AppInfo.version+" \n现在是否去下载新版本\n\n更新内容\n"+body
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText: "取消" negativeText: "取消"
positiveText:"确定" positiveText:"确定"
@ -311,30 +329,40 @@ CustomWindow {
HttpCallable{ HttpCallable{
id:callable id:callable
property bool silent: true
onStart: { onStart: {
console.debug("satrt check update...") console.debug("satrt check update...")
} }
onFinish: { onFinish: {
console.debug("check update finish") console.debug("check update finish")
FluEventBus.post("checkUpdateFinish");
} }
onSuccess: onSuccess:
(result)=>{ (result)=>{
var data = JSON.parse(result) var data = JSON.parse(result)
console.debug("current version "+appInfo.version) console.debug("current version "+AppInfo.version)
console.debug("new version "+data.tag_name) console.debug("new version "+data.tag_name)
if(data.tag_name !== appInfo.version){ if(data.tag_name !== AppInfo.version){
dialog_update.newVerson = data.tag_name dialog_update.newVerson = data.tag_name
dialog_update.body = data.body dialog_update.body = data.body
dialog_update.open() dialog_update.open()
}else{
if(!silent){
showInfo("当前版本已经是最新版")
}
} }
} }
onError: onError:
(status,errorString)=>{ (status,errorString)=>{
if(!silent){
showError("网络异常!")
}
console.debug(status+";"+errorString) console.debug(status+";"+errorString)
} }
} }
function checkUpdate(){ function checkUpdate(silent){
callable.silent = silent
var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest") var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest")
http.get(request,callable); http.get(request,callable);
} }

View File

@ -8,6 +8,13 @@ Window {
id: app id: app
flags: Qt.SplashScreen flags: Qt.SplashScreen
Connections{
target: FluTheme
function onDarkModeChanged(){
SettingsHelper.saveDarkMode(FluTheme.darkMode)
}
}
FluHttpInterceptor{ FluHttpInterceptor{
id:interceptor id:interceptor
function onIntercept(request){ function onIntercept(request){
@ -26,7 +33,7 @@ Window {
Component.onCompleted: { Component.onCompleted: {
FluApp.init(app) FluApp.init(app)
FluTheme.darkMode = FluThemeType.System FluTheme.darkMode = SettingsHelper.getDarkMode()
FluTheme.enableAnimation = true FluTheme.enableAnimation = true
FluApp.routes = { FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml", "/":"qrc:/example/qml/window/MainWindow.qml",

View File

@ -80,7 +80,6 @@ FluExpander{
"FluIcon", "FluIcon",
"FluIconButton", "FluIconButton",
"FluInfoBar", "FluInfoBar",
"FluItem",
"FluMediaPlayer", "FluMediaPlayer",
"FluMenu", "FluMenu",
"FluMenuItem", "FluMenuItem",
@ -139,7 +138,8 @@ FluExpander{
"FluChart", "FluChart",
"FluRangeSlider", "FluRangeSlider",
"FluStaggeredView", "FluStaggeredView",
"FluProgressButton" "FluProgressButton",
"FluLoadingButton"
]; ];
code = code.replace(/\n/g, "<br>"); code = code.replace(/\n/g, "<br>");
code = code.replace(/ /g, "&nbsp;"); code = code.replace(/ /g, "&nbsp;");

View File

@ -477,6 +477,9 @@ FluObject{
} }
function getSearchData(){ function getSearchData(){
if(!navigationView){
return
}
var arr = [] var arr = []
var items = navigationView.getItems(); var items = navigationView.getItems();
for(var i=0;i<items.length;i++){ for(var i=0;i<items.length;i++){

View File

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

View File

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

View File

@ -48,10 +48,10 @@ FluScrollablePage{
height: 1200/4+20 height: 1200/4+20
paddings: 10 paddings: 10
Layout.topMargin: 10 Layout.topMargin: 10
FluRectangle{ FluClip{
width: 1920/4 width: 1920/4
height: 1200/4 height: 1200/4
radius:[15,15,15,15] radius:[8,8,8,8]
Image { Image {
id:image id:image
asynchronous: true asynchronous: true

View File

@ -213,6 +213,45 @@ FluScrollablePage{
}' }'
} }
FluArea{
Layout.fillWidth: true
height: 68
Layout.topMargin: 20
paddings: 10
FluLoadingButton{
id:btn_loading
loading:loading_button_switch.checked
text:"Loading Button"
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
onClicked: {
}
}
FluToggleSwitch{
id:loading_button_switch
checked: true
anchors{
right: parent.right
verticalCenter: parent.verticalCenter
}
text:"Loading"
}
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluLoadingButton{
text:"Loading Button"
onClicked: {
}
}'
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true

View File

@ -10,10 +10,10 @@ FluScrollablePage{
title:"Captcha" title:"Captcha"
FluCaptcha{ FluCaptcha{
id:captcha id:captcha
Layout.topMargin: 20 Layout.topMargin: 20
ignoreCase:switch_case.checked
MouseArea{ MouseArea{
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@ -31,6 +31,13 @@ FluScrollablePage{
} }
} }
FluToggleSwitch{
id:switch_case
text:"Ignore Case"
checked: true
Layout.topMargin: 10
}
RowLayout{ RowLayout{
spacing: 10 spacing: 10
Layout.topMargin: 10 Layout.topMargin: 10
@ -50,6 +57,4 @@ FluScrollablePage{
} }
} }
} }
} }

View File

@ -37,8 +37,14 @@ FluScrollablePage{
FluText{ FluText{
text:"轮播图支持无限轮播无限滑动用ListView实现的组件" text:"轮播图支持无限轮播无限滑动用ListView实现的组件"
} }
Item{
width: 400
height: 300
FluShadow{
radius: 8
}
FluCarousel{ FluCarousel{
radius:[5,5,5,5] anchors.fill: parent
delegate: Component{ delegate: Component{
Image { Image {
anchors.fill: parent anchors.fill: parent
@ -55,6 +61,7 @@ FluScrollablePage{
} }
} }
} }
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
@ -67,8 +74,14 @@ FluScrollablePage{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
left:parent.left left:parent.left
} }
Item{
width: 400
height: 300
FluShadow{
radius: 8
}
FluCarousel{ FluCarousel{
radius:[15,15,15,15] anchors.fill: parent
loopTime:1500 loopTime:1500
indicatorGravity: Qt.AlignHCenter | Qt.AlignTop indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
indicatorMarginTop:15 indicatorMarginTop:15
@ -108,6 +121,8 @@ FluScrollablePage{
} }
} }
} }
}
} }
CodeExpander{ CodeExpander{

View File

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

View File

@ -2,6 +2,7 @@ import QtQuick 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtGraphicalEffects 1.0
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import FluentUI 1.0 import FluentUI 1.0
@ -50,33 +51,19 @@ FluScrollablePage{
} }
} }
ListView{ Component{
id: list id:com_grallery
anchors{ Item{
left: parent.left
right: parent.right
bottom: parent.bottom
}
orientation: ListView.Horizontal
height: 240
model: model_header
header: Item{height: 10;width: 10}
footer: Item{height: 10;width: 10}
ScrollBar.horizontal: FluScrollBar{
id: scrollbar_header
}
clip: false
delegate:Item{
id: control id: control
width: 220 width: 220
height: 240 height: 240
FluShadow{ FluShadow{
radius:8 radius:5
anchors.fill: item_content anchors.fill: item_content
} }
FluItem{ FluClip{
id:item_content id:item_content
radius: [8,8,8,8] radius: [5,5,5,5]
width: 200 width: 200
height: 220 height: 220
anchors.centerIn: parent anchors.centerIn: parent
@ -90,20 +77,15 @@ FluScrollablePage{
} }
Rectangle{ Rectangle{
anchors.fill: parent anchors.fill: parent
radius: 8 radius: 5
color:{ color:FluTheme.dark ? Qt.rgba(1,1,1,0.03) : Qt.rgba(0,0,0,0.03)
if(FluTheme.dark){ visible: item_mouse.containsMouse
if(item_mouse.containsMouse){
return Qt.rgba(1,1,1,0.03)
}
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)
}
} }
Rectangle{
anchors.fill: parent
radius: 5
color:Qt.rgba(0,0,0,0.0)
visible: !item_mouse.containsMouse
} }
ColumnLayout{ ColumnLayout{
Image { Image {
@ -155,11 +137,31 @@ FluScrollablePage{
} }
} }
} }
ListView{
id: list
anchors{
left: parent.left
right: parent.right
bottom: parent.bottom
}
orientation: ListView.Horizontal
height: 240
model: model_header
header: Item{height: 10;width: 10}
footer: Item{height: 10;width: 10}
ScrollBar.horizontal: FluScrollBar{
id: scrollbar_header
}
clip: false
delegate: com_grallery
}
} }
Component{ Component{
id:com_item id:com_item
Item{ Item{
property string desc: modelData.desc
width: 320 width: 320
height: 120 height: 120
FluArea{ FluArea{
@ -195,7 +197,6 @@ FluScrollablePage{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
} }
} }
FluText{ FluText{
id:item_title id:item_title
text:modelData.title text:modelData.title
@ -206,10 +207,9 @@ FluScrollablePage{
top: item_icon.top top: item_icon.top
} }
} }
FluText{ FluText{
id:item_desc id:item_desc
text:modelData.desc text:desc
color:FluColors.Grey120 color:FluColors.Grey120
wrapMode: Text.WrapAnywhere wrapMode: Text.WrapAnywhere
elide: Text.ElideRight elide: Text.ElideRight

View File

@ -12,6 +12,7 @@ FluContentPage{
title:"Http" title:"Http"
property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http" property string cacheDirPath: FluTools.getApplicationDirPath() + "/cache/http"
property bool isDownCompleted: false
FluHttp{ FluHttp{
id:http id:http
@ -172,6 +173,10 @@ FluContentPage{
} }
onSuccess: onSuccess:
(result)=>{ (result)=>{
if(!isDownCompleted){
tour.open()
isDownCompleted = true
}
showSuccess(result) showSuccess(result)
} }
onDownloadProgress: onDownloadProgress:
@ -282,6 +287,13 @@ FluContentPage{
} }
} }
FluTour{
id:tour
steps:[
{title:"友情提示",description: "下载已完成,左击这里可以打开文件所在路径,右击可以弹出菜单删除文件!",target:()=>btn_breakpoint_download}
]
}
HttpCallable{ HttpCallable{
id:callable_upload id:callable_upload
onStart: { onStart: {

View File

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

View File

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

View File

@ -3,7 +3,6 @@ import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import QtQuick.Window 2.15 import QtQuick.Window 2.15
import FluentUI 1.0 import FluentUI 1.0
import QtGraphicalEffects 1.15
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "../component" import "../component"

View File

@ -69,7 +69,7 @@ FluScrollablePage{
} }
RowLayout{ RowLayout{
spacing: 14 spacing: 14
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[25,0,25,25] radius:[25,0,25,25]
@ -80,7 +80,7 @@ FluScrollablePage{
sourceSize: Qt.size(width,height) sourceSize: Qt.size(width,height)
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[10,10,10,10] radius:[10,10,10,10]
@ -91,7 +91,7 @@ FluScrollablePage{
source: "qrc:/example/res/svg/avatar_2.svg" source: "qrc:/example/res/svg/avatar_2.svg"
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[25,25,25,25] radius:[25,25,25,25]
@ -102,7 +102,7 @@ FluScrollablePage{
source: "qrc:/example/res/svg/avatar_3.svg" source: "qrc:/example/res/svg/avatar_3.svg"
} }
} }
FluRectangle{ FluClip{
width: 50 width: 50
height: 50 height: 50
radius:[0,25,25,25] radius:[0,25,25,25]
@ -114,10 +114,10 @@ FluScrollablePage{
} }
} }
} }
FluRectangle{ FluClip{
width: 1920/5 width: 1920/5
height: 1200/5 height: 1200/5
radius:[15,15,15,15] radius:[8,8,8,8]
Image { Image {
asynchronous: true asynchronous: true
source: "qrc:/example/res/image/banner_1.jpg" source: "qrc:/example/res/image/banner_1.jpg"

View File

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

View File

@ -5,12 +5,93 @@ import QtQuick.Controls 2.15
import FluentUI 1.0 import FluentUI 1.0
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "qrc:///example/qml/viewmodel"
import "../component" import "../component"
import "../viewmodel"
import "../global"
FluScrollablePage{ FluScrollablePage{
title:"Settings" title:"Settings"
SettingsViewModel{
id:viewmodel_settings
}
FluEvent{
id:event_checkupdate_finish
name: "checkUpdateFinish"
onTriggered: {
btn_checkupdate.loading = false
}
}
Component.onCompleted: {
FluEventBus.registerEvent(event_checkupdate_finish)
}
Component.onDestruction: {
FluEventBus.unRegisterEvent(event_checkupdate_finish)
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 60
paddings: 10
Row{
spacing: 20
anchors.verticalCenter: parent.verticalCenter
FluText{
text:"当前版本 v%1".arg(AppInfo.version)
font: FluTextStyle.Body
anchors.verticalCenter: parent.verticalCenter
}
FluLoadingButton{
id:btn_checkupdate
text:"检查更新"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
loading = true
FluEventBus.post("checkUpdate")
}
}
}
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 50
paddings: 10
FluCheckBox{
text:"Software Render"
checked: SettingsHelper.getReander() === "software"
anchors.verticalCenter: parent.verticalCenter
onClicked: {
if(SettingsHelper.getReander() === "software"){
SettingsHelper.saveRender("")
}else{
SettingsHelper.saveRender("software")
}
dialog_render.open()
}
}
}
FluContentDialog{
id:dialog_render
title:"友情提示"
message:"此操作需要重启才能生效,是否重新启动?"
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText: "取消"
positiveText:"确定"
onPositiveClicked:{
window.deleteWindow()
AppInfo.restart()
}
}
FluArea{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 Layout.topMargin: 20
@ -61,10 +142,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}] model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked : MainEvent.displayMode===modelData.mode checked : viewmodel_settings.displayMode===modelData.mode
text:modelData.title text:modelData.title
clickListener:function(){ clickListener:function(){
MainEvent.displayMode = modelData.mode viewmodel_settings.displayMode = modelData.mode
} }
} }
} }
@ -95,10 +176,10 @@ FluScrollablePage{
Repeater{ Repeater{
model: ["Zh","En"] model: ["Zh","En"]
delegate: FluRadioButton{ delegate: FluRadioButton{
checked: appInfo.lang.objectName === modelData checked: AppInfo.lang.objectName === modelData
text:modelData text:modelData
clickListener:function(){ clickListener:function(){
appInfo.changeLang(modelData) AppInfo.changeLang(modelData)
} }
} }
} }

View File

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

View File

@ -6,152 +6,127 @@ import FluentUI 1.0
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "../component" import "../component"
FluScrollablePage { FluContentPage {
title:"TreeView" title:"TreeView"
function randomName() { function treeData(){
var names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十"] const dig = (path = '0', level = 4) => {
return names[Math.floor(Math.random() * names.length)] const list = [];
for (let i = 0; i < 6; i += 1) {
const key = `${path}-${i}`;
const treeNode = {
title: key,
key,
};
if (level > 0) {
treeNode.children = dig(key, level - 1);
}
list.push(treeNode);
}
return list;
};
return dig();
} }
function randomCompany() { Column{
var companies = ["阿里巴巴", "腾讯", "百度", "京东", "华为", "小米", "字节跳动", "美团", "滴滴"] id:layout_column
return companies[Math.floor(Math.random() * companies.length)] spacing: 12
} width: 300
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())
}
return tree_view.createItem(randomDepartment(), true, employees)
}
function createOrg(numLevels, numSubtrees, numEmployees) {
if (numLevels === 0) {
return []
}
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
}
}
}
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(","))
}
}
}
}
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 10
paddings: 10
height: 400
FluTreeView{
id:tree_view
width:240
anchors{ anchors{
topMargin: 20
top:parent.top top:parent.top
left: parent.left left: parent.left
leftMargin: 10
bottom:parent.bottom bottom:parent.bottom
} bottomMargin: 20
onItemClicked:
(model)=>{
showSuccess(model.text)
} }
Component.onCompleted: { FluText{
var org = createOrg(3, 3, 3) text:"共计%1条数据当前显示的%2条数据".arg(tree_view.count()).arg(tree_view.visibleCount())
createItem()
updateData(org)
}
}
} }
CodeExpander{ FluText{
Layout.fillWidth: true text:"共计选中%1条数据".arg(tree_view.selectionModel().length)
Layout.topMargin: -1 }
code:'FluTreeView{
RowLayout{
spacing: 10
FluText{
text:"cellHeight:"
Layout.alignment: Qt.AlignVCenter
}
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{
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 id:tree_view
width:240 anchors.fill: parent
height:600 cellHeight: slider_cell_height.value
draggable:switch_draggable.checked
showLine: switch_showline.checked
checkable:switch_checkable.checked
depthPadding: slider_depth_padding.value
Component.onCompleted: { Component.onCompleted: {
var datas = [] var data = treeData()
datas.push(createItem("Node1",false)) dataSource = data
datas.push(createItem("Node2",false))
datas.push(createItem("Node2",true,[createItem("Node2-1",false),createItem("Node2-2",false)]))
updateData(datas)
} }
} }
'
} }
} }

View File

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

View File

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

View File

@ -36,7 +36,7 @@ CustomWindow {
} }
} }
FluText{ FluText{
text:"v%1".arg(appInfo.version) text:"v%1".arg(AppInfo.version)
font: FluTextStyle.Body font: FluTextStyle.Body
Layout.alignment: Qt.AlignBottom Layout.alignment: Qt.AlignBottom
} }

View File

@ -6,8 +6,11 @@ import Qt.labs.platform 1.1
import FluentUI 1.0 import FluentUI 1.0
import example 1.0 import example 1.0
import "qrc:///example/qml/component" import "qrc:///example/qml/component"
import "../component"
import "qrc:///example/qml/global" import "qrc:///example/qml/global"
import "qrc:///example/qml/viewmodel"
import "../component"
import "../viewmodel"
import "../global"
CustomWindow { CustomWindow {
@ -21,15 +24,32 @@ CustomWindow {
appBarVisible: false appBarVisible: false
launchMode: FluWindowType.SingleTask launchMode: FluWindowType.SingleTask
SettingsViewModel{
id:viewmodel_settings
}
closeFunc:function(event){ closeFunc:function(event){
dialog_close.open() dialog_close.open()
event.accepted = false event.accepted = false
} }
FluEvent{
id:event_checkupdate
name: "checkUpdate"
onTriggered: {
checkUpdate(false)
}
}
Component.onCompleted: { Component.onCompleted: {
FluTools.setQuitOnLastWindowClosed(false) FluTools.setQuitOnLastWindowClosed(false)
tour.open() tour.open()
checkUpdate() checkUpdate(true)
FluEventBus.registerEvent(event_checkupdate)
}
Component.onDestruction: {
FluEventBus.unRegisterEvent(event_checkupdate)
} }
SystemTrayIcon { SystemTrayIcon {
@ -139,7 +159,7 @@ CustomWindow {
id:loader id:loader
lazy: true lazy: true
anchors.fill: parent anchors.fill: parent
source: "https://zhu-zichu.gitee.io/Qt6_156_LieflatPage.qml" source: "https://zhu-zichu.gitee.io/Qt5_156_LieflatPage.qml"
} }
} }
front: Item{ front: Item{
@ -165,13 +185,13 @@ CustomWindow {
height: parent.height height: parent.height
z:999 z:999
//Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式 //Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式
pageMode: FluNavigationViewType.Stack // pageMode: FluNavigationViewType.Stack
//NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐 //NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存可以配合FluViewModel保存页面数据(推荐)
// pageMode: FluNavigationViewType.NoStack pageMode: FluNavigationViewType.NoStack
items: ItemsOriginal items: ItemsOriginal
footerItems:ItemsFooter footerItems:ItemsFooter
topPadding:FluTools.isMacos() ? 20 : 0 topPadding:FluTools.isMacos() ? 20 : 0
displayMode:MainEvent.displayMode displayMode:viewmodel_settings.displayMode
logo: "qrc:/example/res/image/favicon.ico" logo: "qrc:/example/res/image/favicon.ico"
title:"FluentUI" title:"FluentUI"
onLogoClicked:{ onLogoClicked:{
@ -301,7 +321,7 @@ CustomWindow {
property string body property string body
id:dialog_update id:dialog_update
title:"升级提示" title:"升级提示"
message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+appInfo.version+" \n现在是否去下载新版本\n\n更新内容\n"+body message:"FluentUI目前最新版本 "+ newVerson +" -- 当前应用版本 "+AppInfo.version+" \n现在是否去下载新版本\n\n更新内容\n"+body
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText: "取消" negativeText: "取消"
positiveText:"确定" positiveText:"确定"
@ -312,30 +332,40 @@ CustomWindow {
HttpCallable{ HttpCallable{
id:callable id:callable
property bool silent: true
onStart: { onStart: {
console.debug("satrt check update...") console.debug("satrt check update...")
} }
onFinish: { onFinish: {
console.debug("check update finish") console.debug("check update finish")
FluEventBus.post("checkUpdateFinish");
} }
onSuccess: onSuccess:
(result)=>{ (result)=>{
var data = JSON.parse(result) var data = JSON.parse(result)
console.debug("current version "+appInfo.version) console.debug("current version "+AppInfo.version)
console.debug("new version "+data.tag_name) console.debug("new version "+data.tag_name)
if(data.tag_name !== appInfo.version){ if(data.tag_name !== AppInfo.version){
dialog_update.newVerson = data.tag_name dialog_update.newVerson = data.tag_name
dialog_update.body = data.body dialog_update.body = data.body
dialog_update.open() dialog_update.open()
}else{
if(!silent){
showInfo("当前版本已经是最新版")
}
} }
} }
onError: onError:
(status,errorString)=>{ (status,errorString)=>{
if(!silent){
showError("网络异常!")
}
console.debug(status+";"+errorString) console.debug(status+";"+errorString)
} }
} }
function checkUpdate(){ function checkUpdate(silent){
callable.silent = silent
var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest") var request = http.newRequest("https://api.github.com/repos/zhuzichu520/FluentUI/releases/latest")
http.get(request,callable); http.get(request,callable);
} }

View File

@ -2,6 +2,7 @@
#include <QQmlContext> #include <QQmlContext>
#include <QDebug> #include <QDebug>
#include <QGuiApplication>
#include "lang/En.h" #include "lang/En.h"
#include "lang/Zh.h" #include "lang/Zh.h"
#include "Version.h" #include "Version.h"
@ -20,7 +21,6 @@ void AppInfo::init(QQmlApplicationEngine *engine){
QObject::connect(this,&AppInfo::langChanged,this,[=]{ QObject::connect(this,&AppInfo::langChanged,this,[=]{
context->setContextProperty("lang",this->lang()); context->setContextProperty("lang",this->lang());
}); });
context->setContextProperty("appInfo",this);
} }
void AppInfo::changeLang(const QString& locale){ void AppInfo::changeLang(const QString& locale){
@ -35,3 +35,7 @@ void AppInfo::changeLang(const QString& locale){
lang(new En()); lang(new En());
} }
} }
void AppInfo::restart(){
qApp->exit(931);
}

View File

@ -5,16 +5,20 @@
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include "lang/Lang.h" #include "lang/Lang.h"
#include "stdafx.h" #include "stdafx.h"
#include "singleton.h"
class AppInfo : public QObject class AppInfo : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY_AUTO(QString,version) Q_PROPERTY_AUTO(QString,version)
Q_PROPERTY_AUTO(Lang*,lang) Q_PROPERTY_AUTO(Lang*,lang)
public: private:
explicit AppInfo(QObject *parent = nullptr); explicit AppInfo(QObject *parent = nullptr);
public:
SINGLETONG(AppInfo)
void init(QQmlApplicationEngine *engine); void init(QQmlApplicationEngine *engine);
Q_INVOKABLE void changeLang(const QString& locale); Q_INVOKABLE void changeLang(const QString& locale);
Q_INVOKABLE void restart();
}; };
#endif // APPINFO_H #endif // APPINFO_H

View File

@ -0,0 +1,40 @@
#include "SettingsHelper.h"
#include <QDataStream>
SettingsHelper::SettingsHelper(QObject *parent) : QObject(parent)
{
}
SettingsHelper::~SettingsHelper() = default;
void SettingsHelper::save(const QString& key,QVariant val)
{
QByteArray data = {};
QDataStream stream(&data, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_6);
stream << val;
m_settings->setValue(key, data);
}
QVariant SettingsHelper::get(const QString& key){
const QByteArray data = m_settings->value(key).toByteArray();
if (data.isEmpty()) {
return {};
}
QDataStream stream(data);
stream.setVersion(QDataStream::Qt_5_6);
QVariant val;
stream >> val;
return val;
}
void SettingsHelper::init(char *argv[]){
auto applicationPath = QString::fromStdString(argv[0]);
const QFileInfo fileInfo(applicationPath);
const QString iniFileName = fileInfo.completeBaseName() + ".ini";
const QString iniFilePath = fileInfo.dir().path() + "/" + iniFileName;
qDebug()<<iniFilePath;
m_settings.reset(new QSettings(iniFilePath, QSettings::IniFormat));
}

View File

@ -0,0 +1,33 @@
#ifndef SETTINGSHELPER_H
#define SETTINGSHELPER_H
#include <QtCore/qobject.h>
#include <QtQml/qqml.h>
#include <QSettings>
#include <QScopedPointer>
#include <QFileInfo>
#include <QCoreApplication>
#include <QDir>
#include "src/singleton.h"
class SettingsHelper : public QObject
{
Q_OBJECT
private:
explicit SettingsHelper(QObject* parent = nullptr);
public:
SINGLETONG(SettingsHelper)
~SettingsHelper() override;
void init(char *argv[]);
Q_INVOKABLE void saveRender(const QString& render){save("render",render);}
Q_INVOKABLE QString getReander(){return get("render").toString();}
Q_INVOKABLE void saveDarkMode(int darkModel){save("darkMode",darkModel);}
Q_INVOKABLE int getDarkMode(){return get("darkMode").toInt(0);}
private:
void save(const QString& key,QVariant val);
QVariant get(const QString& key);
private:
QScopedPointer<QSettings> m_settings;
};
#endif // SETTINGSHELPER_H

View File

@ -8,15 +8,25 @@
#include <QProcess> #include <QProcess>
#include <FramelessHelper/Quick/framelessquickmodule.h> #include <FramelessHelper/Quick/framelessquickmodule.h>
#include <FramelessHelper/Core/private/framelessconfig_p.h> #include <FramelessHelper/Core/private/framelessconfig_p.h>
#include <QtQml/qqmlextensionplugin.h>
#include "AppInfo.h" #include "AppInfo.h"
#include "src/component/CircularReveal.h" #include "src/component/CircularReveal.h"
#include "src/component/FileWatcher.h" #include "src/component/FileWatcher.h"
#include "src/component/FpsItem.h" #include "src/component/FpsItem.h"
#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 FRAMELESSHELPER_USE_NAMESPACE
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
SettingsHelper::getInstance()->init(argv);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
@ -24,34 +34,42 @@ int main(int argc, char *argv[])
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif #endif
#endif #endif
//将样式设置为Basic不然会导致组件显示异常
qputenv("QT_QUICK_CONTROLS_STYLE","Basic"); qputenv("QT_QUICK_CONTROLS_STYLE","Basic");
FramelessHelper::Quick::initialize(); FramelessHelper::Quick::initialize();
QGuiApplication::setOrganizationName("ZhuZiChu"); QGuiApplication::setOrganizationName("ZhuZiChu");
QGuiApplication::setOrganizationDomain("https://zhuzichu520.github.io"); QGuiApplication::setOrganizationDomain("https://zhuzichu520.github.io");
QGuiApplication::setApplicationName("FluentUI"); QGuiApplication::setApplicationName("FluentUI");
if(SettingsHelper::getInstance()->getReander()=="software"){
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
#elif (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
#endif
}
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow); FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow);
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur); FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur);
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
#ifdef Q_OS_WIN // 此设置仅在Windows下生效 #ifdef Q_OS_WIN
FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder); FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder);
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow,false); FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow,false);
#endif #endif
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur,false); FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur,false);
#endif #endif
AppInfo* appInfo = new AppInfo();
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
AppInfo::getInstance()->init(&engine);
engine.rootContext()->setContextProperty("AppInfo",AppInfo::getInstance());
engine.rootContext()->setContextProperty("SettingsHelper",SettingsHelper::getInstance());
FramelessHelper::Quick::registerTypes(&engine); FramelessHelper::Quick::registerTypes(&engine);
#ifdef FLUENTUI_BUILD_STATIC_LIB #ifdef FLUENTUI_BUILD_STATIC_LIB
engine.addImportPath("qrc:/"); // 让静态资源可以被QML引擎搜索到 FluentUI::getInstance()->registerTypes(&engine);
#endif #endif
qDebug()<<engine.importPathList();
qmlRegisterType<CircularReveal>("example", 1, 0, "CircularReveal"); qmlRegisterType<CircularReveal>("example", 1, 0, "CircularReveal");
qmlRegisterType<FileWatcher>("example", 1, 0, "FileWatcher"); qmlRegisterType<FileWatcher>("example", 1, 0, "FileWatcher");
qmlRegisterType<FpsItem>("example", 1, 0, "FpsItem"); qmlRegisterType<FpsItem>("example", 1, 0, "FpsItem");
appInfo->init(&engine);
const QUrl url(QStringLiteral("qrc:/example/qml/App.qml")); const QUrl url(QStringLiteral("qrc:/example/qml/App.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) { &app, [url](QObject *obj, const QUrl &objUrl) {
@ -59,5 +77,9 @@ int main(int argc, char *argv[])
QCoreApplication::exit(-1); QCoreApplication::exit(-1);
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
engine.load(url); engine.load(url);
return app.exec(); const int exec = QGuiApplication::exec();
if (exec == 931) {
QProcess::startDetached(qApp->applicationFilePath(), QStringList());
}
return exec;
} }

47
example/src/singleton.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef SINGLETON_H
#define SINGLETON_H
#include <QMutex>
#include <QScopedPointer>
#include <memory>
#include <mutex>
template <typename T>
class Singleton {
public:
static T* getInstance();
Singleton(const Singleton& other) = delete;
Singleton<T>& operator=(const Singleton& other) = delete;
private:
static std::mutex mutex;
static T* instance;
};
template <typename T>
std::mutex Singleton<T>::mutex;
template <typename T>
T* Singleton<T>::instance;
template <typename T>
T* Singleton<T>::getInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> locker(mutex);
if (instance == nullptr) {
instance = new T();
}
}
return instance;
}
#define SINGLETONG(Class) \
private: \
friend class Singleton<Class>; \
friend struct QScopedPointerDeleter<Class>; \
\
public: \
static Class* getInstance() { \
return Singleton<Class>::getInstance(); \
}
#endif // SINGLETON_H

View File

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

View File

@ -4,19 +4,22 @@
#include <QObject> #include <QObject>
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
namespace FluViewModelType {
Q_NAMESPACE
enum Scope {
Window = 0x0000,
Application = 0x0001
};
Q_ENUM_NS(Scope)
QML_NAMED_ELEMENT(FluViewModelType)
}
namespace FluHttpType { namespace FluHttpType {
Q_NAMESPACE Q_NAMESPACE
enum CacheMode { enum CacheMode {
/** 不使用缓存 */
NoCache = 0x0000, NoCache = 0x0000,
/** 请求网络失败后,读取缓存 */
RequestFailedReadCache = 0x0001, RequestFailedReadCache = 0x0001,
/** 如果缓存不存在才请求网络,否则使用缓存 */
IfNoneCacheRequest = 0x0002, IfNoneCacheRequest = 0x0002,
/** 先使用缓存,不管是否存在,仍然请求网络 */
FirstCacheThenRequest = 0x0004, FirstCacheThenRequest = 0x0004,
}; };
Q_ENUM_NS(CacheMode) Q_ENUM_NS(CacheMode)

View File

@ -10,19 +10,7 @@
#include <QClipboard> #include <QClipboard>
#include "Def.h" #include "Def.h"
FluApp* FluApp::m_instance = nullptr; FluApp::FluApp(QObject *parent):QObject{parent}{
FluApp *FluApp::getInstance()
{
if(FluApp::m_instance == nullptr){
FluApp::m_instance = new FluApp;
}
return FluApp::m_instance;
}
FluApp::FluApp(QObject *parent)
: QObject{parent}
{
httpInterceptor(nullptr); httpInterceptor(nullptr);
} }
@ -83,8 +71,7 @@ void FluApp::navigate(const QString& route,const QJsonObject& argument,FluRegist
view->setColor(QColor(Qt::transparent)); view->setColor(QColor(Qt::transparent));
} }
QJsonArray FluApp::awesomelist(const QString& keyword) QJsonArray FluApp::awesomelist(const QString& keyword){
{
QJsonArray arr; QJsonArray arr;
QMetaEnum enumType = Fluent_Awesome::staticMetaObject.enumerator(Fluent_Awesome::staticMetaObject.indexOfEnumerator("Fluent_AwesomeType")); QMetaEnum enumType = Fluent_Awesome::staticMetaObject.enumerator(Fluent_Awesome::staticMetaObject.indexOfEnumerator("Fluent_AwesomeType"));
for(int i=0; i < enumType.keyCount(); ++i){ for(int i=0; i < enumType.keyCount(); ++i){

View File

@ -11,6 +11,7 @@
#include "FluRegister.h" #include "FluRegister.h"
#include "FluHttpInterceptor.h" #include "FluHttpInterceptor.h"
#include "stdafx.h" #include "stdafx.h"
#include "singleton.h"
/** /**
* @brief The FluApp class * @brief The FluApp class
@ -18,80 +19,26 @@
class FluApp : public QObject class FluApp : public QObject
{ {
Q_OBJECT Q_OBJECT
/**
* @brief initialRoute 初始路由
*/
Q_PROPERTY_AUTO(QString,initialRoute); Q_PROPERTY_AUTO(QString,initialRoute);
/**
* @brief routes 路由表
*/
Q_PROPERTY_AUTO(QJsonObject,routes); Q_PROPERTY_AUTO(QJsonObject,routes);
/**
* @brief http拦截器
*/
Q_PROPERTY_AUTO(FluHttpInterceptor*,httpInterceptor); Q_PROPERTY_AUTO(FluHttpInterceptor*,httpInterceptor);
QML_NAMED_ELEMENT(FluApp) QML_NAMED_ELEMENT(FluApp)
QML_SINGLETON QML_SINGLETON
private: private:
/**
* @brief FluApp 将默认构造函数设置为私有则qml创建单例就会走create工厂方法创建单例
* @param parent
*/
explicit FluApp(QObject *parent = nullptr); explicit FluApp(QObject *parent = nullptr);
public:
~FluApp(); ~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: public:
/** SINGLETONG(FluApp)
* @brief wnds 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; QMap<quint64, QQuickWindow*> wnds;
private: private:
static FluApp* m_instance;
/**
* @brief appWindow
*/
QWindow *appWindow; QWindow *appWindow;
}; };

View File

@ -5,17 +5,18 @@
#include <QRandomGenerator> #include <QRandomGenerator>
#include <qmath.h> #include <qmath.h>
FluCaptcha::FluCaptcha(QQuickItem *parent) FluCaptcha::FluCaptcha(QQuickItem *parent):QQuickPaintedItem(parent){
: QQuickPaintedItem(parent) ignoreCase(true);
{ QFont fontStype;
font(QFont("楷体",25,QFont::Bold,true)); fontStype.setPixelSize(28);
fontStype.setBold(true);
font(fontStype);
setWidth(180); setWidth(180);
setHeight(80); setHeight(80);
refresh(); refresh();
} }
void FluCaptcha::paint(QPainter* painter) void FluCaptcha::paint(QPainter* painter){
{
painter->save(); painter->save();
painter->fillRect(boundingRect().toRect(),QColor(255,255,255,255)); painter->fillRect(boundingRect().toRect(),QColor(255,255,255,255));
QPen pen; QPen pen;
@ -69,5 +70,8 @@ void FluCaptcha::refresh(){
} }
bool FluCaptcha::verify(const QString& code){ bool FluCaptcha::verify(const QString& code){
if(_ignoreCase){
return this->_code.toUpper() == code.toUpper();
}
return this->_code == code; return this->_code == code;
} }

View File

@ -10,15 +10,17 @@ class FluCaptcha : public QQuickPaintedItem
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY_AUTO(QFont,font); Q_PROPERTY_AUTO(QFont,font);
Q_PROPERTY_AUTO(bool,ignoreCase);
QML_NAMED_ELEMENT(FluCaptcha) QML_NAMED_ELEMENT(FluCaptcha)
private: private:
int _generaNumber(int number); int _generaNumber(int number);
QString _code;
public: public:
explicit FluCaptcha(QQuickItem *parent = nullptr); explicit FluCaptcha(QQuickItem *parent = nullptr);
void paint(QPainter* painter) override; void paint(QPainter* painter) override;
Q_INVOKABLE void refresh(); Q_INVOKABLE void refresh();
Q_INVOKABLE bool verify(const QString& code); Q_INVOKABLE bool verify(const QString& code);
private:
QString _code;
}; };
#endif // FLUCAPTCHA_H #endif // FLUCAPTCHA_H

View File

@ -1,7 +1,4 @@
#include "FluColorSet.h" #include "FluColorSet.h"
FluColorSet::FluColorSet(QObject *parent) FluColorSet::FluColorSet(QObject *parent):QObject{parent}{
: QObject{parent}
{
} }

View File

@ -1,18 +1,6 @@
#include "FluColors.h" #include "FluColors.h"
FluColors* FluColors::m_instance = nullptr; FluColors::FluColors(QObject *parent):QObject{parent}{
FluColors *FluColors::getInstance()
{
if(FluColors::m_instance == nullptr){
FluColors::m_instance = new FluColors;
}
return FluColors::m_instance;
}
FluColors::FluColors(QObject *parent)
: QObject{parent}
{
Transparent("#00000000"); Transparent("#00000000");
Black("#000000"); Black("#000000");
White("#ffffff"); White("#ffffff");

View File

@ -5,6 +5,7 @@
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
#include "FluColorSet.h" #include "FluColorSet.h"
#include "stdafx.h" #include "stdafx.h"
#include "singleton.h"
/** /**
* @brief The FluColors class * @brief The FluColors class
@ -49,13 +50,9 @@ class FluColors : public QObject
QML_SINGLETON QML_SINGLETON
private: private:
explicit FluColors(QObject *parent = nullptr); explicit FluColors(QObject *parent = nullptr);
static FluColors* m_instance;
public: public:
static FluColors *getInstance(); SINGLETONG(FluColors)
static FluColors *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine) static FluColors *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
{
return getInstance();
}
}; };
#endif // FLUCOLORS_H #endif // FLUCOLORS_H

23
src/FluEventBus.cpp Normal file
View 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
View 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

View File

@ -14,9 +14,7 @@
#include "FluApp.h" #include "FluApp.h"
#include "FluTools.h" #include "FluTools.h"
HttpRequest::HttpRequest(QObject *parent) HttpRequest::HttpRequest(QObject *parent):QObject{parent}{
: QObject{parent}
{
} }
QMap<QString, QVariant> HttpRequest::toMap(){ QMap<QString, QVariant> HttpRequest::toMap(){
@ -55,14 +53,10 @@ QString HttpRequest::httpId(){
return FluTools::getInstance()->sha256(QJsonDocument::fromVariant(QVariant(toMap())).toJson(QJsonDocument::Compact)); return FluTools::getInstance()->sha256(QJsonDocument::fromVariant(QVariant(toMap())).toJson(QJsonDocument::Compact));
} }
HttpCallable::HttpCallable(QObject *parent) HttpCallable::HttpCallable(QObject *parent):QObject{parent}{
: QObject{parent}
{
} }
FluHttp::FluHttp(QObject *parent) FluHttp::FluHttp(QObject *parent):QObject{parent}{
: QObject{parent}
{
retry(3); retry(3);
timeout(15000); timeout(15000);
cacheMode(FluHttpType::CacheMode::NoCache); cacheMode(FluHttpType::CacheMode::NoCache);
@ -82,7 +76,9 @@ void FluHttp::cancel(){
} }
} }
void FluHttp::post(HttpRequest* request,HttpCallable* callable){ void FluHttp::post(HttpRequest* r,HttpCallable* c){
auto request = QPointer(r);
auto callable = QPointer(c);
request->method("post"); request->method("post");
auto requestMap = request->toMap(); auto requestMap = request->toMap();
auto httpId = request->httpId(); auto httpId = request->httpId();
@ -123,7 +119,8 @@ void FluHttp::post(HttpRequest* request,HttpCallable* callable){
QString result = QString::fromUtf8(reply->readAll()); QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString(); QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError; QNetworkReply::NetworkError error = reply->error();
bool isSuccess = error == QNetworkReply::NoError;
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if (isSuccess) { if (isSuccess) {
@ -138,12 +135,17 @@ void FluHttp::post(HttpRequest* request,HttpCallable* callable){
onError(callable,status,errorString,result); onError(callable,status,errorString,result);
} }
} }
if(error == QNetworkReply::OperationCanceledError){
break;
}
} }
onFinish(callable,request); onFinish(callable,request);
}); });
} }
void FluHttp::postString(HttpRequest* request,HttpCallable* callable){ void FluHttp::postString(HttpRequest* r,HttpCallable* c){
auto request = QPointer(r);
auto callable = QPointer(c);
request->method("postString"); request->method("postString");
auto requestMap = request->toMap(); auto requestMap = request->toMap();
auto httpId = request->httpId(); auto httpId = request->httpId();
@ -176,7 +178,8 @@ void FluHttp::postString(HttpRequest* request,HttpCallable* callable){
QString result = QString::fromUtf8(reply->readAll()); QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString(); QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError; QNetworkReply::NetworkError error = reply->error();
bool isSuccess = error == QNetworkReply::NoError;
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if (isSuccess) { if (isSuccess) {
@ -191,12 +194,17 @@ void FluHttp::postString(HttpRequest* request,HttpCallable* callable){
onError(callable,status,errorString,result); onError(callable,status,errorString,result);
} }
} }
if(error == QNetworkReply::OperationCanceledError){
break;
}
} }
onFinish(callable,request); onFinish(callable,request);
}); });
} }
void FluHttp::postJson(HttpRequest* request,HttpCallable* callable){ void FluHttp::postJson(HttpRequest* r,HttpCallable* c){
auto request = QPointer(r);
auto callable = QPointer(c);
request->method("postJson"); request->method("postJson");
auto requestMap = request->toMap(); auto requestMap = request->toMap();
auto httpId = request->httpId(); auto httpId = request->httpId();
@ -228,7 +236,8 @@ void FluHttp::postJson(HttpRequest* request,HttpCallable* callable){
QString result = QString::fromUtf8(reply->readAll()); QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString(); QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError; QNetworkReply::NetworkError error = reply->error();
bool isSuccess = error == QNetworkReply::NoError;
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if (isSuccess) { if (isSuccess) {
@ -243,12 +252,17 @@ void FluHttp::postJson(HttpRequest* request,HttpCallable* callable){
onError(callable,status,errorString,result); onError(callable,status,errorString,result);
} }
} }
if(error == QNetworkReply::OperationCanceledError){
break;
}
} }
onFinish(callable,request); onFinish(callable,request);
}); });
} }
void FluHttp::get(HttpRequest* request,HttpCallable* callable){ void FluHttp::get(HttpRequest* r,HttpCallable* c){
auto request = QPointer(r);
auto callable = QPointer(c);
request->method("get"); request->method("get");
auto requestMap = request->toMap(); auto requestMap = request->toMap();
auto httpId = request->httpId(); auto httpId = request->httpId();
@ -271,14 +285,15 @@ void FluHttp::get(HttpRequest* request,HttpCallable* callable){
QNetworkRequest req(url); QNetworkRequest req(url);
addHeaders(&req,data["headers"].toMap()); addHeaders(&req,data["headers"].toMap());
QEventLoop loop; QEventLoop loop;
QNetworkReply* reply = manager.get(req); auto reply = QPointer(manager.get(req));
_cacheReply.append(reply); _cacheReply.append(reply);
connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();}); connect(&manager,&QNetworkAccessManager::finished,&manager,[&loop](QNetworkReply *reply){loop.quit();});
connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();}); connect(qApp,&QGuiApplication::aboutToQuit,&manager, [&loop](){loop.quit();});
loop.exec(); loop.exec();
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString(); QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError; QNetworkReply::NetworkError error = reply->error();
bool isSuccess = error == QNetworkReply::NoError;
QString result = QString::fromUtf8(reply->readAll()); QString result = QString::fromUtf8(reply->readAll());
if (isSuccess) { if (isSuccess) {
handleCache(httpId,result); handleCache(httpId,result);
@ -294,12 +309,17 @@ void FluHttp::get(HttpRequest* request,HttpCallable* callable){
} }
reply->deleteLater(); reply->deleteLater();
reply = nullptr; reply = nullptr;
if(error == QNetworkReply::OperationCanceledError){
break;
}
} }
onFinish(callable,request); onFinish(callable,request);
}); });
} }
void FluHttp::download(HttpRequest* request,HttpCallable* callable){ void FluHttp::download(HttpRequest* r,HttpCallable* c){
auto request = QPointer(r);
auto callable = QPointer(c);
request->method("download"); request->method("download");
auto requestMap = request->toMap(); auto requestMap = request->toMap();
auto httpId = request->httpId(); auto httpId = request->httpId();
@ -535,11 +555,11 @@ void FluHttp::onStart(QPointer<HttpCallable> callable){
} }
} }
void FluHttp::onFinish(QPointer<HttpCallable> callable,HttpRequest* request){ void FluHttp::onFinish(QPointer<HttpCallable> callable,QPointer<HttpRequest> request){
if(callable){ if(callable){
Q_EMIT callable->finish(); Q_EMIT callable->finish();
} }
if(request->parent()->inherits("FluHttp")){ if(request&&request->parent()->inherits("FluHttp")){
request->deleteLater(); request->deleteLater();
} }
} }

View File

@ -53,7 +53,7 @@ private:
bool cacheExists(const QString& httpId); bool cacheExists(const QString& httpId);
QString getCacheFilePath(const QString& httpId); QString getCacheFilePath(const QString& httpId);
void onStart(QPointer<HttpCallable> callable); void onStart(QPointer<HttpCallable> callable);
void onFinish(QPointer<HttpCallable> callable,HttpRequest* request); void onFinish(QPointer<HttpCallable> callable,QPointer<HttpRequest> request);
void onError(QPointer<HttpCallable> callable,int status,QString errorString,QString result); void onError(QPointer<HttpCallable> callable,int status,QString errorString,QString result);
void onSuccess(QPointer<HttpCallable> callable,QString result); void onSuccess(QPointer<HttpCallable> callable,QString result);
void onCache(QPointer<HttpCallable> callable,QString result); void onCache(QPointer<HttpCallable> callable,QString result);

View File

@ -1,7 +1,4 @@
#include "FluHttpInterceptor.h" #include "FluHttpInterceptor.h"
FluHttpInterceptor::FluHttpInterceptor(QObject *parent) FluHttpInterceptor::FluHttpInterceptor(QObject *parent):QObject{parent}{
: QObject{parent}
{
} }

View File

@ -10,9 +10,6 @@ class FluHttpInterceptor : public QObject
QML_NAMED_ELEMENT(FluHttpInterceptor) QML_NAMED_ELEMENT(FluHttpInterceptor)
public: public:
explicit FluHttpInterceptor(QObject *parent = nullptr); explicit FluHttpInterceptor(QObject *parent = nullptr);
signals:
}; };
#endif // FLUHTTPINTERCEPTOR_H #endif // FLUHTTPINTERCEPTOR_H

28
src/FluRectangle.cpp Normal file
View File

@ -0,0 +1,28 @@
#include "FluRectangle.h"
#include <QPainterPath>
FluRectangle::FluRectangle(QQuickItem* parent) : QQuickPaintedItem(parent){
setFlag(ItemHasContents, true);
color(QColor(255,255,255,255));
radius({0,0,0,0});
connect(this,&FluRectangle::colorChanged,this,[=]{update();});
connect(this,&FluRectangle::radiusChanged,this,[=]{update();});
}
void FluRectangle::paint(QPainter* painter){
painter->save();
painter->setRenderHint(QPainter::Antialiasing);
QPainterPath path;
QRectF rect = boundingRect();
path.moveTo(rect.bottomRight() - QPointF(0, _radius[2]));
path.lineTo(rect.topRight() + QPointF(0, _radius[1]));
path.arcTo(QRectF(QPointF(rect.topRight() - QPointF(_radius[1] * 2, 0)), QSize(_radius[1] * 2, _radius[1] * 2)), 0, 90);
path.lineTo(rect.topLeft() + QPointF(_radius[0], 0));
path.arcTo(QRectF(QPointF(rect.topLeft()), QSize(_radius[0] * 2, _radius[0] * 2)), 90, 90);
path.lineTo(rect.bottomLeft() - QPointF(0, _radius[3]));
path.arcTo(QRectF(QPointF(rect.bottomLeft() - QPointF(0, _radius[3] * 2)), QSize(_radius[3] * 2, _radius[3] * 2)), 180, 90);
path.lineTo(rect.bottomRight() - QPointF(_radius[2], 0));
path.arcTo(QRectF(QPointF(rect.bottomRight() - QPointF(_radius[2] * 2, _radius[2] * 2)), QSize(_radius[2] * 2, _radius[2] * 2)), 270, 90);
painter->fillPath(path,_color);
painter->restore();
}

20
src/FluRectangle.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef FLURECTANGLE_H
#define FLURECTANGLE_H
#include <QQuickItem>
#include <QQuickPaintedItem>
#include <QPainter>
#include "stdafx.h"
class FluRectangle : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY_AUTO(QColor,color)
Q_PROPERTY_AUTO(QList<int>,radius)
QML_NAMED_ELEMENT(FluRectangle)
public:
explicit FluRectangle(QQuickItem *parent = nullptr);
void paint(QPainter* painter) override;
};
#endif // FLURECTANGLE_H

View File

@ -3,9 +3,7 @@
#include "FluApp.h" #include "FluApp.h"
#include <QCoreApplication> #include <QCoreApplication>
FluRegister::FluRegister(QObject *parent) FluRegister::FluRegister(QObject *parent):QObject{parent}{
: QObject{parent}
{
from(nullptr); from(nullptr);
to(nullptr); to(nullptr);
path(""); path("");

View File

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

View File

@ -1,19 +1,6 @@
#include "FluTextStyle.h" #include "FluTextStyle.h"
FluTextStyle* FluTextStyle::m_instance = nullptr; FluTextStyle::FluTextStyle(QObject *parent):QObject{parent}{
FluTextStyle *FluTextStyle::getInstance()
{
if(FluTextStyle::m_instance == nullptr){
FluTextStyle::m_instance = new FluTextStyle;
}
return FluTextStyle::m_instance;
}
FluTextStyle::FluTextStyle(QObject *parent)
: QObject{parent}
{
QFont caption; QFont caption;
caption.setPixelSize(12); caption.setPixelSize(12);
Caption(caption); Caption(caption);

View File

@ -5,6 +5,7 @@
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
#include <QFont> #include <QFont>
#include "stdafx.h" #include "stdafx.h"
#include "singleton.h"
class FluTextStyle : public QObject class FluTextStyle : public QObject
{ {
@ -21,13 +22,9 @@ public:
QML_SINGLETON QML_SINGLETON
private: private:
explicit FluTextStyle(QObject *parent = nullptr); explicit FluTextStyle(QObject *parent = nullptr);
static FluTextStyle* m_instance;
public: public:
static FluTextStyle *getInstance(); SINGLETONG(FluTextStyle)
static FluTextStyle *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine) static FluTextStyle *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
{
return getInstance();
}
}; };
#endif // FLUTEXTSTYLE_H #endif // FLUTEXTSTYLE_H

View File

@ -1,7 +1,6 @@
#include "FluTheme.h" #include "FluTheme.h"
#include "Def.h" #include <QGuiApplication>
#include "FluColors.h"
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
#include <QStyleHints> #include <QStyleHints>
#elif ((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1))) #elif ((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)))
@ -10,22 +9,10 @@
#else #else
#include <QPalette> #include <QPalette>
#endif #endif
#include "Def.h"
#include "FluColors.h"
#include <QGuiApplication> FluTheme::FluTheme(QObject *parent):QObject{parent}{
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}
{
connect(this,&FluTheme::darkModeChanged,this,[=]{ connect(this,&FluTheme::darkModeChanged,this,[=]{
Q_EMIT darkChanged(); Q_EMIT darkChanged();
}); });
@ -37,8 +24,7 @@ FluTheme::FluTheme(QObject *parent)
qApp->installEventFilter(this); qApp->installEventFilter(this);
} }
bool FluTheme::eventFilter(QObject *obj, QEvent *event) bool FluTheme::eventFilter(QObject *obj, QEvent *event){
{
Q_UNUSED(obj); Q_UNUSED(obj);
if (event->type() == QEvent::ApplicationPaletteChange || event->type() == QEvent::ThemeChange) if (event->type() == QEvent::ApplicationPaletteChange || event->type() == QEvent::ThemeChange)
{ {
@ -50,8 +36,7 @@ bool FluTheme::eventFilter(QObject *obj, QEvent *event)
return false; return false;
} }
bool FluTheme::systemDark() bool FluTheme::systemDark(){
{
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
return (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark); return (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark);
#elif ((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1))) #elif ((QT_VERSION >= QT_VERSION_CHECK(6, 2, 1)))

View File

@ -5,6 +5,7 @@
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
#include "FluColorSet.h" #include "FluColorSet.h"
#include "stdafx.h" #include "stdafx.h"
#include "singleton.h"
/** /**
* @brief The FluTheme class * @brief The FluTheme class
@ -12,49 +13,25 @@
class FluTheme : public QObject class FluTheme : public QObject
{ {
Q_OBJECT Q_OBJECT
/**
* @brief dark 改变窗口夜间样式只读属性可以通过darkMode切换
*/
Q_PROPERTY(bool dark READ dark NOTIFY darkChanged) Q_PROPERTY(bool dark READ dark NOTIFY darkChanged)
/**
* @brief primaryColor 主题颜色
*/
Q_PROPERTY_AUTO(FluColorSet*,primaryColor) Q_PROPERTY_AUTO(FluColorSet*,primaryColor)
/**
* @brief darkMode 夜间模式支持System=0、Light=1、Dark=2
*/
Q_PROPERTY_AUTO(int,darkMode); Q_PROPERTY_AUTO(int,darkMode);
/**
* @brief nativeText 本地渲染文本
*/
Q_PROPERTY_AUTO(bool,nativeText); Q_PROPERTY_AUTO(bool,nativeText);
/**
* @brief 是否开启动画效果
*/
Q_PROPERTY_AUTO(bool,enableAnimation); Q_PROPERTY_AUTO(bool,enableAnimation);
QML_NAMED_ELEMENT(FluTheme) QML_NAMED_ELEMENT(FluTheme)
QML_SINGLETON QML_SINGLETON
private: private:
static FluTheme* m_instance;
explicit FluTheme(QObject *parent = nullptr); explicit FluTheme(QObject *parent = nullptr);
bool eventFilter(QObject *obj, QEvent *event);
bool systemDark();
public: public:
static FluTheme *getInstance(); SINGLETONG(FluTheme)
static FluTheme *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine) static FluTheme *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
{
return getInstance();
}
bool dark(); bool dark();
Q_SIGNAL void darkChanged(); Q_SIGNAL void darkChanged();
private: private:
bool _dark; bool _dark;
bool _systemDark; bool _systemDark;
bool eventFilter(QObject *obj, QEvent *event);
bool systemDark();
}; };
#endif // FLUTHEME_H #endif // FLUTHEME_H

View File

@ -1,4 +1,5 @@
#include "FluTools.h" #include "FluTools.h"
#include <QGuiApplication> #include <QGuiApplication>
#include <QClipboard> #include <QClipboard>
#include <QUuid> #include <QUuid>
@ -8,23 +9,13 @@
#include <QFileInfo> #include <QFileInfo>
#include <QProcess> #include <QProcess>
#include <QDir> #include <QDir>
#include <QOpenGLContext>
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QTextDocument> #include <QTextDocument>
#include <QQuickWindow>
#include <QDateTime>
FluTools* FluTools::m_instance = nullptr; FluTools::FluTools(QObject *parent):QObject{parent}{
FluTools *FluTools::getInstance()
{
if(FluTools::m_instance == nullptr){
FluTools::m_instance = new FluTools;
}
return FluTools::m_instance;
}
FluTools::FluTools(QObject *parent)
: QObject{parent}
{
} }
@ -36,8 +27,7 @@ QString FluTools::uuid(){
return QUuid::createUuid().toString(); return QUuid::createUuid().toString();
} }
QString FluTools::readFile(const QString &fileName) QString FluTools::readFile(const QString &fileName){
{
QString content; QString content;
QFile file(fileName); QFile file(fileName);
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly)) {
@ -132,18 +122,15 @@ QColor FluTools::colorAlpha(const QColor& color,qreal alpha){
return QColor(color.red(),color.green(),color.blue(),255*alpha); return QColor(color.red(),color.green(),color.blue(),255*alpha);
} }
QString FluTools::md5(QString text) QString FluTools::md5(QString text){
{
return QCryptographicHash::hash(text.toUtf8(), QCryptographicHash::Md5).toHex(); return QCryptographicHash::hash(text.toUtf8(), QCryptographicHash::Md5).toHex();
} }
QString FluTools::toBase64(QString text) QString FluTools::toBase64(QString text){
{
return text.toUtf8().toBase64(); return text.toUtf8().toBase64();
} }
QString FluTools::fromBase64(QString text) QString FluTools::fromBase64(QString text){
{
return QByteArray::fromBase64(text.toUtf8()); return QByteArray::fromBase64(text.toUtf8());
} }
@ -176,3 +163,15 @@ void FluTools::showFileInFolder(QString path){
QProcess::execute("/usr/bin/osascript", {"-e", "tell application \"Finder\" to activate"}); QProcess::execute("/usr/bin/osascript", {"-e", "tell application \"Finder\" to activate"});
#endif #endif
} }
bool FluTools::isSoftware(){
return QQuickWindow::sceneGraphBackend() == "software";
}
QPoint FluTools::cursorPos(){
return QCursor::pos();
}
qint64 FluTools::currentTimestamp(){
return QDateTime::currentMSecsSinceEpoch();
}

View File

@ -5,6 +5,7 @@
#include <QFile> #include <QFile>
#include <QColor> #include <QColor>
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
#include "singleton.h"
/** /**
* @brief The FluTools class * @brief The FluTools class
@ -12,187 +13,42 @@
class FluTools : public QObject class FluTools : public QObject
{ {
Q_OBJECT Q_OBJECT
QML_NAMED_ELEMENT(FluTools) QML_NAMED_ELEMENT(FluTools)
QML_SINGLETON QML_SINGLETON
private: private:
explicit FluTools(QObject *parent = nullptr); explicit FluTools(QObject *parent = nullptr);
static FluTools* m_instance;
public: public:
static FluTools *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine) SINGLETONG(FluTools)
{ static FluTools *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine){return getInstance();}
return getInstance();
}
static FluTools *getInstance();
/**
* @brief qtMajor Qt Major版本
* @return
*/
Q_INVOKABLE int qtMajor(); Q_INVOKABLE int qtMajor();
/**
* @brief qtMajor Qt Minor版本
* @return
*/
Q_INVOKABLE int qtMinor(); Q_INVOKABLE int qtMinor();
/**
* @brief isMacos 是否是Macos系统
* @return
*/
Q_INVOKABLE bool isMacos(); Q_INVOKABLE bool isMacos();
/**
* @brief isLinux 是否是Linux系统
* @return
*/
Q_INVOKABLE bool isLinux(); Q_INVOKABLE bool isLinux();
/**
* @brief isWin 是否是Windows系统
* @return
*/
Q_INVOKABLE bool isWin(); Q_INVOKABLE bool isWin();
/**
* @brief clipText 将字符串添加到剪切板
* @param text
*/
Q_INVOKABLE void clipText(const QString& text); Q_INVOKABLE void clipText(const QString& text);
/**
* @brief uuid 获取uuid
* @return
*/
Q_INVOKABLE QString uuid(); Q_INVOKABLE QString uuid();
/**
* @brief readFile 读取文件内容
* @param fileName
* @return
*/
Q_INVOKABLE QString readFile(const QString& fileName); Q_INVOKABLE QString readFile(const QString& fileName);
/**
* @brief setQuitOnLastWindowClosed 设置关闭最后一个窗口是否退出程序
* @param val
*/
Q_INVOKABLE void setQuitOnLastWindowClosed(bool val); Q_INVOKABLE void setQuitOnLastWindowClosed(bool val);
/**
* @brief setOverrideCursor 设置全局鼠标样式
* @param shape
*/
Q_INVOKABLE void setOverrideCursor(Qt::CursorShape shape); Q_INVOKABLE void setOverrideCursor(Qt::CursorShape shape);
/**
* @brief restoreOverrideCursor 还原全局鼠标样式
*/
Q_INVOKABLE void restoreOverrideCursor(); Q_INVOKABLE void restoreOverrideCursor();
/**
* @brief html2PlantText 将html转换成纯文本
* @param html
*/
Q_INVOKABLE QString html2PlantText(const QString& html); Q_INVOKABLE QString html2PlantText(const QString& html);
/**
* @brief toLocalPath 获取文件路径可以去掉windows系统下的file:///macos下的file://
* @param url
* @return 返回文件路径
*/
Q_INVOKABLE QString toLocalPath(const QUrl& url); Q_INVOKABLE QString toLocalPath(const QUrl& url);
/**
* @brief deleteItem 销毁Item对象
* @param p
*/
Q_INVOKABLE void deleteItem(QObject *p); Q_INVOKABLE void deleteItem(QObject *p);
/**
* @brief getFileNameByUrl
* @param url
* @return
*/
Q_INVOKABLE QString getFileNameByUrl(const QUrl& url); Q_INVOKABLE QString getFileNameByUrl(const QUrl& url);
/**
* @brief getVirtualGeometry
* @return
*/
Q_INVOKABLE QRect getVirtualGeometry(); Q_INVOKABLE QRect getVirtualGeometry();
/**
* @brief getApplicationDirPath
* @return
*/
Q_INVOKABLE QString getApplicationDirPath(); Q_INVOKABLE QString getApplicationDirPath();
/**
* @brief getUrlByFilePath
* @param path
* @return
*/
Q_INVOKABLE QUrl getUrlByFilePath(const QString& path); Q_INVOKABLE QUrl getUrlByFilePath(const QString& path);
/**
* @brief colorAlpha
* @param color
* @param alpha
* @return
*/
Q_INVOKABLE QColor colorAlpha(const QColor&,qreal alpha); Q_INVOKABLE QColor colorAlpha(const QColor&,qreal alpha);
/**
* @brief md5
* @param text
* @return
*/
Q_INVOKABLE QString md5(QString text); Q_INVOKABLE QString md5(QString text);
/**
* @brief sha256
* @param text
* @return
*/
Q_INVOKABLE QString sha256(QString text); Q_INVOKABLE QString sha256(QString text);
/**
* @brief toBase64
* @param text
* @return
*/
Q_INVOKABLE QString toBase64(QString text); Q_INVOKABLE QString toBase64(QString text);
/**
* @brief fromBase64
* @param text
* @return
*/
Q_INVOKABLE QString fromBase64(QString text); Q_INVOKABLE QString fromBase64(QString text);
/**
* @brief removeDir
* @param dirPath
* @return
*/
Q_INVOKABLE bool removeDir(QString dirPath); Q_INVOKABLE bool removeDir(QString dirPath);
/**
* @brief removeFile
* @param filePath
* @return
*/
Q_INVOKABLE bool removeFile(QString filePath); Q_INVOKABLE bool removeFile(QString filePath);
/**
* @brief showFileInFolder
* @param path
*/
Q_INVOKABLE void showFileInFolder(QString path); Q_INVOKABLE void showFileInFolder(QString path);
Q_INVOKABLE bool isSoftware();
Q_INVOKABLE qint64 currentTimestamp();
Q_INVOKABLE QPoint cursorPos();
}; };
#endif // FLUTOOLS_H #endif // FLUTOOLS_H

368
src/FluTreeModel.cpp Normal file
View File

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

115
src/FluTreeModel.h Normal file
View File

@ -0,0 +1,115 @@
#ifndef FLUTREEMODEL_H
#define FLUTREEMODEL_H
#include <QObject>
#include <QAbstractTableModel>
#include <QJsonArray>
#include <QtQml/qqml.h>
#include "stdafx.h"
class Node : public QObject{
Q_OBJECT
Q_PROPERTY(QString key READ key CONSTANT)
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(int depth READ depth CONSTANT)
Q_PROPERTY(bool isExpanded READ isExpanded CONSTANT)
Q_PROPERTY(bool checked READ checked CONSTANT)
public:
explicit Node(QObject *parent = nullptr);
Q_INVOKABLE QString key(){return _key;};
Q_INVOKABLE QString title(){return _title;};
Q_INVOKABLE int depth(){return _depth;};
Q_INVOKABLE bool isExpanded(){return _isExpanded;};
Q_INVOKABLE bool hasChildren(){ return !_children.isEmpty();};
Q_INVOKABLE bool hasNextNodeByIndex(int index){
Node* p = this;
for(int i=0;i<(_depth - index -1);i++){
p = p->_parent;
}
if(p->_parent->_children.indexOf(p) == p->_parent->_children.count()-1){
return false;
}
return true;
}
Q_INVOKABLE bool checked(){
if(!hasChildren()){
return _checked;
}
foreach (auto item, _children) {
if(!item->checked()){
return false;
}
}
return true;
};
Q_INVOKABLE bool hideLineFooter(){
if(_parent){
auto childIndex = _parent->_children.indexOf(this);
if(childIndex==_parent->_children.count()-1){
return true;
}
if(_parent->_children.at(childIndex+1)->hasChildren()){
return true;
}
return false;
}
return false;
};
bool isShown(){
auto p = _parent;
while (p) {
if(!p->_isExpanded){
return false;
}
p = p->_parent;
}
return true;
}
public:
QString _key="";
QString _title="";
int _depth=0;
bool _checked = false;
bool _isExpanded=true;
QList<Node*> _children;
Node* _parent = nullptr;
};
class FluTreeModel : public QAbstractItemModel
{
Q_OBJECT
Q_PROPERTY_AUTO(int,dataSourceSize)
Q_PROPERTY_AUTO(QList<Node*>,selectionModel)
QML_NAMED_ELEMENT(FluTreeModel)
QML_ADDED_IN_MINOR_VERSION(1)
public:
explicit FluTreeModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
QModelIndex parent(const QModelIndex &child) const override;
QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE void removeRows(int row,int count);
Q_INVOKABLE void insertRows(int row,QList<Node*> data);
Q_INVOKABLE QObject* getRow(int row);
Q_INVOKABLE void setData(QList<Node*> data);
Q_INVOKABLE void setDataSource(QList<QMap<QString,QVariant>> data);
Q_INVOKABLE void collapse(int row);
Q_INVOKABLE void expand(int row);
Q_INVOKABLE void dragAnddrop(int dragIndex,int dropIndex,bool isDropTopArea);
Q_INVOKABLE Node* getNode(int row);
Q_INVOKABLE void refreshNode(int row);
Q_INVOKABLE void checkRow(int row,bool chekced);
Q_INVOKABLE bool hitHasChildrenExpanded(int row);
Q_INVOKABLE void allExpand();
Q_INVOKABLE void allCollapse();
private:
QList<Node*> _rows;
QList<Node*> _dataSource;
Node* _root = nullptr;
};
#endif // FLUTREEMODEL_H

Some files were not shown because too many files have changed in this diff Show More