Compare commits

..

50 Commits

Author SHA1 Message Date
855305f197 update 2023-08-11 22:47:36 +08:00
6fe3081d23 update 2023-08-11 14:31:23 +08:00
b1a2266c5e update 2023-08-10 20:05:38 +08:00
048abb5dbd update 2023-08-10 18:59:34 +08:00
991ea383e7 update 2023-08-10 18:29:43 +08:00
73518fb831 update 2023-08-10 16:22:43 +08:00
312aebae73 update 2023-08-10 16:08:27 +08:00
3c349da98f update 2023-08-08 21:59:18 +08:00
dba7332d89 update 2023-08-08 21:57:08 +08:00
3fefb08140 update 2023-08-08 21:33:16 +08:00
1c69d5daab update 2023-08-08 21:31:33 +08:00
ec76240aed update 2023-08-08 21:03:15 +08:00
0a967ca47b update 2023-08-08 20:21:32 +08:00
8dbbf4e547 update 2023-08-08 19:59:06 +08:00
ebad24e809 update 2023-08-08 15:44:10 +08:00
b7acae1470 update 2023-08-07 22:11:08 +08:00
f8340bdf59 update 2023-08-07 21:46:54 +08:00
e4fb9989d0 update 2023-08-07 18:18:04 +08:00
9bd98c68e4 Merge pull request #232 from FeJQ/FluNavigationView-pr
给FluPaneItemSeparator增加spacing和size属性
2023-08-04 12:05:15 +08:00
7cd9b7c6bc 给FluPaneItemSeparator增加spacing和size属性 2023-08-03 18:14:52 +08:00
dde817923c update 2023-07-28 16:22:15 +08:00
ceaae1276e update 2023-07-28 16:18:49 +08:00
29c57bcdc3 update 2023-07-28 16:08:58 +08:00
7ba60ee570 update 2023-07-27 18:13:50 +08:00
f531f5b138 update 2023-07-27 17:46:18 +08:00
4f27ff41b9 update 2023-07-26 22:06:55 +08:00
42f058ca69 update 2023-07-26 17:48:35 +08:00
502044ebd2 update 2023-07-26 11:02:33 +08:00
5d902dc66e update 2023-07-25 21:24:45 +08:00
b22e79148b update 2023-07-25 17:57:37 +08:00
7122407b0b update 2023-07-24 21:49:32 +08:00
be7b2dcc34 update 2023-07-24 18:23:26 +08:00
db805ef85d update 2023-07-24 16:48:45 +08:00
d0e283747e Merge pull request #214 from mentalfl0w/dev
examlple: Make the items in searchbox more readable.
2023-07-24 09:38:34 +08:00
2d8a05f310 examlple: Make the items in searchbox more readable. 2023-07-24 01:20:23 +08:00
2d855e1aaa update 2023-07-23 21:19:07 +08:00
56188cfa51 update 2023-07-23 21:06:46 +08:00
169ca17a6a update 2023-07-23 20:56:04 +08:00
e4908e409b Merge branch 'main' of https://github.com/zhuzichu520/FluentUI 2023-07-23 20:44:47 +08:00
f141af154a update 2023-07-23 20:44:43 +08:00
10da873701 Merge pull request #210 from mentalfl0w/dev
Fix the FolderDialog and macOS download bug.
2023-07-23 15:42:59 +08:00
c23c0b5f42 Fix the FolderDialog and macOS download bug.
1. fix the "FolderDialog is not a type" error in Qt 6.2.4 and below.
2. fix download error in macOS.
2023-07-22 12:27:55 +08:00
4d78262277 update 2023-07-21 18:58:09 +08:00
2b88634c2f Merge branch 'main' of https://github.com/zhuzichu520/FluentUI 2023-07-21 11:19:33 +08:00
59f9495f31 update 2023-07-21 11:19:30 +08:00
29329d10df update 2023-07-20 21:54:45 +08:00
d014997d52 update 2023-07-20 18:26:47 +08:00
98e0aafb44 update 2023-07-18 18:24:06 +08:00
4b3548563b update 2023-07-18 14:07:07 +08:00
47c84ed60e update 2023-07-17 11:05:14 +08:00
107 changed files with 24706 additions and 1353 deletions

View File

@ -0,0 +1,308 @@
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
function(_git_find_closest_git_dir _start_dir _git_dir_var)
set(cur_dir "${_start_dir}")
set(git_dir "${_start_dir}/.git")
while(NOT EXISTS "${git_dir}")
set(git_previous_parent "${cur_dir}")
get_filename_component(cur_dir ${cur_dir} DIRECTORY)
if(cur_dir STREQUAL git_previous_parent)
set(${_git_dir_var}
""
PARENT_SCOPE)
return()
endif()
set(git_dir "${cur_dir}/.git")
endwhile()
set(${_git_dir_var}
"${git_dir}"
PARENT_SCOPE)
endfunction()
function(get_git_head_revision _refspecvar _hashvar)
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE)
else()
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE)
endif()
if(NOT "${GIT_DIR}" STREQUAL "")
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
"${GIT_DIR}")
if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
set(GIT_DIR "")
endif()
endif()
if("${GIT_DIR}" STREQUAL "")
set(${_refspecvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
set(${_hashvar}
"GITDIR-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT IS_DIRECTORY ${GIT_DIR})
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-parse
--show-superproject-working-tree
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT "${out}" STREQUAL "")
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
${submodule})
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
ABSOLUTE)
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
else()
file(READ ${GIT_DIR} worktree_ref)
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
${worktree_ref})
string(STRIP ${git_worktree_dir} git_worktree_dir)
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
endif()
else()
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${HEAD_SOURCE_FILE}")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake" @ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar}
"${HEAD_REF}"
PARENT_SCOPE)
set(${_hashvar}
"${HEAD_HASH}"
PARENT_SCOPE)
endfunction()
function(git_latest_tag _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --abbrev=0 --tag
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "GIT-TAG-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_commit_counts _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" rev-list HEAD --count
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "GIT-TAG-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_release_version _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" symbolic-ref --short -q HEAD
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
string(FIND ${out} "release/" found})
if(${out} MATCHES "^release/.+$")
string(REPLACE "release/" "" tmp_out ${out})
set(${_var} "${tmp_out}" PARENT_SCOPE)
else()
set(${_var} "" PARENT_SCOPE)
endif()
endfunction()
function(git_describe_working_tree _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var}
"${out}"
PARENT_SCOPE)
endfunction()
function(git_local_changes _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var}
"GIT-NOTFOUND"
PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var}
"HEAD-HASH-NOTFOUND"
PARENT_SCOPE)
return()
endif()
execute_process(
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE res
OUTPUT_VARIABLE out
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(res EQUAL 0)
set(${_var}
"CLEAN"
PARENT_SCOPE)
else()
set(${_var}
"DIRTY"
PARENT_SCOPE)
endif()
endfunction()
git_release_version(GIT_TAG)
git_get_exact_tag(GIT_EXACT_TAG)
if(GIT_TAG STREQUAL "")
git_latest_tag(GIT_TAG)
endif()
if(GIT_TAG STREQUAL "GIT-TAG-NOTFOUND")
set(GIT_TAG "1.0.0")
endif ()
git_describe(GIT_DESCRIBE)
git_commit_counts(GIT_COMMIT_COUNT)
string(REPLACE "." "," GIT_TAG_WITH_COMMA ${GIT_TAG})
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" GIT_SEMVER "${GIT_TAG}")
string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" SEMVER_SPLITED "${GIT_SEMVER}")
set(MAJOR_VERSION ${CMAKE_MATCH_1})
set(MINOR_VERSION ${CMAKE_MATCH_2})
set(PATCH_VERSION ${CMAKE_MATCH_3})
MATH(EXPR VERSION_COUNTER "${MAJOR_VERSION} * 10000 + ${MINOR_VERSION} * 100 + ${PATCH_VERSION}")
message(STATUS "Current git tag: ${GIT_TAG}, commit count: ${GIT_COMMIT_COUNT}, describe: ${GIT_DESCRIBE}")
message(STATUS "Current semver: major: ${MAJOR_VERSION}, minor: ${MINOR_VERSION}, patch: ${PATCH_VERSION}, counter: ${VERSION_COUNTER}")

View File

@ -0,0 +1,43 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright 2009-2012, Iowa State University
# Copyright 2011-2015, Contributors
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
else()
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
set(HEAD_HASH "${CMAKE_MATCH_1}")
endif()
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

12
.cmake/Version.h.in Normal file
View File

@ -0,0 +1,12 @@
// 应用程序版本信息
// 请勿修改此头文件,因为这个文件是自动生成的
#ifndef VERSION_H
#define VERSION_H
#include <QtGlobal>
#define APPLICATION_VERSION "${GIT_SEMVER}.${GIT_COMMIT_COUNT}"
#define VERSION_COUNTER ${GIT_COMMIT_COUNT}
#define COMMIT_HASH "${GIT_DESCRIBE}"
#endif // VERSION_H

32
.cmake/version_dll.rc.in Normal file
View File

@ -0,0 +1,32 @@
1 VERSIONINFO
FILEVERSION ${GIT_TAG_WITH_COMMA},${GIT_COMMIT_COUNT}
PRODUCTVERSION ${GIT_TAG_WITH_COMMA},${GIT_COMMIT_COUNT}
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080404b0"
BEGIN
VALUE "CompanyName", "ZhuZiChu"
VALUE "FileDescription", "${PROJECT_DESCRIPTION}"
VALUE "FileVersion", "${GIT_SEMVER}.${GIT_COMMIT_COUNT}"
VALUE "InternalName", "${PROJECT_NAME}.dll"
VALUE "LegalCopyright", "Copyright (C) 2023 ZhuZiChu. All rights reserved."
VALUE "OriginalFilename", "${PROJECT_NAME}.dll"
VALUE "ProductName", "${PROJECT_NAME}"
VALUE "ProductVersion", "${GIT_SEMVER}.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x804, 1200
END
END

34
.cmake/version_exe.rc.in Normal file
View File

@ -0,0 +1,34 @@
1 VERSIONINFO
FILEVERSION ${GIT_TAG_WITH_COMMA},${GIT_COMMIT_COUNT}
PRODUCTVERSION ${GIT_TAG_WITH_COMMA},${GIT_COMMIT_COUNT}
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080404b0"
BEGIN
VALUE "CompanyName", "ZhuZiChu"
VALUE "FileDescription", "${PROJECT_DESCRIPTION}"
VALUE "FileVersion", "${GIT_SEMVER}.${GIT_COMMIT_COUNT}"
VALUE "InternalName", "${PROJECT_NAME}.exe"
VALUE "LegalCopyright", "Copyright (C) 2023 ZhuZiChu. All rights reserved."
VALUE "OriginalFilename", "${PROJECT_NAME}.exe"
VALUE "ProductName", "${PROJECT_NAME}"
VALUE "ProductVersion", "${GIT_SEMVER}.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x804, 1200
END
END
IDI_ICON1 ICON DISCARDABLE "${CMAKE_SOURCE_DIR}/example/favicon.ico"

5
.gitignore vendored
View File

@ -31,9 +31,10 @@ target_wrapper.*
# QtCreator CMake
CMakeLists.txt.user*
src/build-preset/plugins.qmltypes
bin
.DS_Store
build
cmake-build-*
.idea
.idea
example/Version.h

5
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "framelesshelper"]
path = framelesshelper
url = https://github.com/zhuzichu520/framelesshelper.git
url = https://github.com/zhuzichu520/framelesshelper.git
[submodule "zxing-cpp"]
path = zxing-cpp
url = https://github.com/zhuzichu520/zxing-cpp.git

View File

@ -2,6 +2,10 @@ cmake_minimum_required(VERSION 3.20)
project(FluentUI VERSION 0.1 LANGUAGES CXX)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/.cmake/")
include(GetGitRevisionDescription)
option(FLUENTUI_BUILD_EXAMPLES "Build FluentUI demo applications." ON)
option(FLUENTUI_BUILD_FRAMELESSHEPLER "Build FramelessHelper." ON)
option(FLUENTUI_BUILD_STATIC_LIB "Build static library." OFF)
@ -13,6 +17,7 @@ if(NOT FLUENTUI_QML_PLUGIN_DIRECTORY)
endif()
add_subdirectory(src)
add_subdirectory(zxing-cpp)
if (FLUENTUI_BUILD_EXAMPLES)
add_subdirectory(example)
@ -23,6 +28,8 @@ if (FLUENTUI_BUILD_FRAMELESSHEPLER)
set(FRAMELESSHELPER_NO_SUMMARY OFF)
set(FRAMELESSHELPER_NO_DEBUG_OUTPUT OFF)
set(FRAMELESSHELPER_BUILD_WIDGETS OFF)
add_definitions(-DFRAMELESSHELPER_CORE_NO_DEBUG_OUTPUT)
add_definitions(-DFRAMELESSHELPER_QUICK_NO_DEBUG_OUTPUT)
add_subdirectory(framelesshelper)
endif ()

View File

@ -8,6 +8,9 @@ if(APPLE)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
#导入exmaple的QML位置不然import example有时候会爆红
set(QML_IMPORT_PATH ${CMAKE_BINARY_DIR}/example CACHE STRING "Qt Creator extra QML import paths" FORCE)
#判断FluentUI库类型
if(FLUENTUI_BUILD_STATIC_LIB)
add_definitions(-DFLUENTUI_BUILD_STATIC_LIB)
@ -23,10 +26,7 @@ endif()
#获取文件路径分隔符(解决执行命令的时候有些平台会报错)
file(TO_CMAKE_PATH "/" PATH_SEPARATOR)
#设置版本号
add_definitions(-DVERSION=1,3,7,4)
find_package(Qt6 REQUIRED COMPONENTS Quick Svg)
find_package(Qt6 REQUIRED COMPONENTS Quick Svg Network)
if(QT_VERSION VERSION_GREATER_EQUAL "6.3")
qt_standard_project_setup()
@ -36,6 +36,13 @@ else()
set(CMAKE_AUTOUIC ON)
endif()
##生成版本信息头文件
set(HEADER_FILE_VERSION_PATH ${CMAKE_SOURCE_DIR}/example/Version.h)
configure_file(
${CMAKE_SOURCE_DIR}/.cmake/Version.h.in
${HEADER_FILE_VERSION_PATH}
)
#遍历所有Cpp文件
file(GLOB_RECURSE CPP_FILES *.cpp *.h)
foreach(filepath ${CPP_FILES})
@ -57,11 +64,21 @@ foreach(filepath ${RES_PATHS})
list(APPEND resource_files ${filename})
endforeach(filepath)
#如果是Windows平台则生成rc文件
set(EXAMPLE_VERSION_RC_PATH "")
if(WIN32)
set(EXAMPLE_VERSION_RC_PATH ${CMAKE_BINARY_DIR}/version_${PROJECT_NAME}.rc)
configure_file(
${CMAKE_SOURCE_DIR}/.cmake/version_exe.rc.in
${EXAMPLE_VERSION_RC_PATH}
)
endif()
#添加可执行文件
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
if (WIN32)
qt_add_executable(example
${sources_files}
example.rc
${EXAMPLE_VERSION_RC_PATH}
)
else ()
qt_add_executable(example
@ -87,12 +104,17 @@ endif()
#添加qml模块
qt_add_qml_module(example
URI example
URI "example"
VERSION 1.0
QML_FILES ${qml_files}
RESOURCES ${resource_files}
)
#导入component头文件,不然通过QML_NAMED_ELEMENT生成的c++类会找不到头文件报错
target_include_directories(example PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/component
)
#设置属性
set_target_properties(example PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
@ -106,7 +128,8 @@ set_target_properties(example PROPERTIES
if (FLUENTUI_BUILD_STATIC_LIB)
target_link_libraries(example PRIVATE
Qt6::Quick
Qt::Svg
Qt6::Svg
Qt6::Network
fluentui
fluentuiplugin
FramelessHelper::Core
@ -115,7 +138,8 @@ if (FLUENTUI_BUILD_STATIC_LIB)
else()
target_link_libraries(example PRIVATE
Qt6::Quick
Qt::Svg
Qt6::Svg
Qt6::Network
fluentuiplugin
FramelessHelper::Core
FramelessHelper::Quick

View File

@ -1,42 +0,0 @@
#include <windows.h>
IDI_ICON1 ICON "favicon.ico"
#define STR(x) #x
#define VER_JOIN(a,b,c,d) STR(a.b.c.d)
#define VER_JOIN_(x) VER_JOIN x
#define VER_STR VER_JOIN_((VERSION))
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION
PRODUCTVERSION VERSION
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "Built by the FluentUI."
VALUE "CompanyName", "zhuzichu"
VALUE "FileDescription", "example"
VALUE "FileVersion", VER_STR
VALUE "InternalName", ""
VALUE "LegalCopyright", "Copyright (C) 2023"
VALUE "OriginalFilename", ""
VALUE "ProductName", "example"
VALUE "ProductVersion", VER_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@ -7,9 +7,26 @@ import FluentUI
Window {
id: app
flags: Qt.SplashScreen
FluHttpInterceptor{
id:interceptor
function onIntercept(request){
if(request.method === "get"){
request.params["method"] = "get"
}
if(request.method === "post"){
request.params["method"] = "post"
}
request.headers["token"] ="yyds"
request.headers["os"] ="pc"
console.debug(JSON.stringify(request))
return request
}
}
Component.onCompleted: {
FluApp.init(app)
FluTheme.darkMode = FluDarkMode.System
FluTheme.darkMode = FluThemeType.System
FluTheme.enableAnimation = true
FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml",
@ -21,6 +38,7 @@ Window {
"/singleInstanceWindow":"qrc:/example/qml/window/SingleInstanceWindow.qml"
}
FluApp.initialRoute = "/"
FluApp.httpInterceptor = interceptor
FluApp.run()
}
}

View File

@ -38,7 +38,7 @@ FluExpander{
topMargin: 5
}
onClicked:{
FluTools.clipText(content.text)
FluTools.clipText(FluTools.html2PlantText(content.text))
showSuccess("复制成功")
}
}
@ -130,7 +130,13 @@ FluExpander{
"FluPagination",
"FluRadioButtons",
"FluImage",
"FluSpinBox"
"FluSpinBox",
"FluHttp",
"FluWatermark",
"FluTour",
"FluQRCode",
"FluTimeline",
"FluChart"
];
code = code.replace(/\n/g, "<br>");
code = code.replace(/ /g, "&nbsp;");

View File

@ -4,14 +4,11 @@ import FluentUI
import org.wangwenx190.FramelessHelper
FluWindow {
id:window
property bool fixSize
property alias titleVisible: title_bar.titleVisible
property bool appBarVisible: true
default property alias content: container.data
FluAppBar {
id: title_bar
title: window.title
@ -23,7 +20,6 @@ FluWindow {
}
darkText: lang.dark_mode
}
Item{
id:container
anchors{
@ -34,7 +30,6 @@ FluWindow {
}
clip: true
}
FramelessHelper{
id:framless_helper
onReady: {
@ -46,7 +41,7 @@ FluWindow {
setWindowFixedSize(fixSize)
title_bar.maximizeButton.visible = !fixSize
if (blurBehindWindowEnabled)
window.backgroundVisible = false
window.background = undefined
window.show()
}
}
@ -59,13 +54,10 @@ FluWindow {
FramelessUtils.systemTheme = FramelessHelperConstants.Light
}
}
function setHitTestVisible(com){
framless_helper.setHitTestVisible(com)
}
function setTitleBarItem(com){
framless_helper.setTitleBarItem(com)
}
}

View File

@ -181,6 +181,12 @@ FluObject{
navigationView.push("qrc:/example/qml/page/T_Expander.qml")
}
}
FluPaneItem{
title:"Watermark"
onTap:{
navigationView.push("qrc:/example/qml/page/T_Watermark.qml")
}
}
}
FluPaneItemExpander{
@ -317,9 +323,44 @@ FluObject{
}
}
FluPaneItemSeparator{
spacing:20
size:1
}
FluPaneItemExpander{
title:lang.other
icon:FluentIcons.Shop
FluPaneItem{
title:"QRCode"
onTap:{
navigationView.push("qrc:/example/qml/page/T_QRCode.qml")
}
}
FluPaneItem{
title:"Tour"
onTap:{
navigationView.push("qrc:/example/qml/page/T_Tour.qml")
}
}
FluPaneItem{
title:"Timeline"
onTap:{
navigationView.push("qrc:/example/qml/page/T_Timeline.qml")
}
}
FluPaneItem{
title:"Chart"
onTap:{
navigationView.push("qrc:/example/qml/page/T_Chart.qml")
}
}
FluPaneItem{
title:"Http"
onTap:{
navigationView.push("qrc:/example/qml/page/T_Http.qml")
}
}
FluPaneItem{
id:item_other
title:"RemoteLoader"
@ -379,7 +420,12 @@ FluObject{
for(var i=0;i<items.length;i++){
var item = items[i]
if(item instanceof FluPaneItem){
arr.push({title:item.title,key:item.key})
if (item.parent instanceof FluPaneItemExpander)
{
arr.push({title:`${item.parent.title} -> ${item.title}`,key:item.key})
}
else
arr.push({title:item.title,key:item.key})
}
}
return arr

View File

@ -5,5 +5,5 @@ import QtQuick.Controls
import FluentUI
QtObject {
property int displayMode : FluNavigationView.Auto
property int displayMode : FluNavigationViewType.Auto
}

View File

@ -9,35 +9,84 @@ FluScrollablePage{
title:"Acrylic"
RowLayout{
spacing: 10
Layout.topMargin: 20
FluText{
text:"tintColor:"
Layout.alignment: Qt.AlignVCenter
}
FluColorPicker{
id:color_picker
}
}
RowLayout{
spacing: 10
FluText{
text:"tintOpacity:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_tint_opacity
value: 65
}
}
RowLayout{
spacing: 10
FluText{
text:"blurRadius:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_blur_radius
value: 32
}
}
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 1200/5+20
height: 1200/4+20
paddings: 10
Layout.topMargin: 10
FluRectangle{
width: 1920/5
height: 1200/5
width: 1920/4
height: 1200/4
radius:[15,15,15,15]
Image {
id:image
asynchronous: true
source: "qrc:/example/res/image/banner_3.jpg"
source: "qrc:/example/res/image/bg_scenic.png"
anchors.fill: parent
sourceSize: Qt.size(2*width,2*height)
}
FluAcrylic {
sourceItem:image
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 100
height: 100
id:acrylic
target: image
width: 200
height: 200
tintOpacity: slider_tint_opacity.value/100
tintColor: color_picker.colorValue
blurRadius: slider_blur_radius.value
x:(image.width-width)/2
y:(image.height-height)/2
FluText {
anchors.centerIn: parent
text: "Acrylic"
color: "#FFFFFF"
font.bold: true
}
MouseArea {
property point clickPos: Qt.point(0,0)
id:drag_area
anchors.fill: parent
onPressed: (mouse)=>{
clickPos = Qt.point(mouse.x, mouse.y)
}
onPositionChanged: (mouse)=>{
var delta = Qt.point(mouse.x - clickPos.x,mouse.y - clickPos.y)
acrylic.x = acrylic.x + delta.x
acrylic.y = acrylic.y + delta.y
}
}
}
Layout.topMargin: 20
}
@ -54,7 +103,7 @@ FluScrollablePage{
radius: 8
}
FluAcrylic{
sourceItem:image
target:image
width: 100
height: 100
anchors.centerIn: parent

View File

@ -9,6 +9,19 @@ FluScrollablePage{
title:"Carousel"
ListModel{
id:data_model
ListElement{
url:"qrc:/example/res/image/banner_1.jpg"
}
ListElement{
url:"qrc:/example/res/image/banner_2.jpg"
}
ListElement{
url:"qrc:/example/res/image/banner_3.jpg"
}
}
FluArea{
Layout.fillWidth: true
height: 370
@ -24,23 +37,95 @@ FluScrollablePage{
text:"轮播图支持无限轮播无限滑动用ListView实现的组件"
}
FluCarousel{
id:carousel
radius:[5,5,5,5]
delegate: Component{
Image {
anchors.fill: parent
source: model.url
asynchronous: true
fillMode:Image.PreserveAspectCrop
}
}
Layout.topMargin: 20
Layout.leftMargin: 5
Component.onCompleted: {
carousel.setData([{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}])
model = [{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}]
}
}
}
}
FluArea{
Layout.fillWidth: true
height: 340
paddings: 10
Layout.topMargin: 10
Column{
spacing: 15
anchors{
verticalCenter: parent.verticalCenter
left:parent.left
}
FluCarousel{
radius:[15,15,15,15]
loopTime:1500
indicatorGravity: Qt.AlignHCenter | Qt.AlignTop
indicatorMarginTop:15
delegate: Component{
Item{
anchors.fill: parent
Image {
anchors.fill: parent
source: model.url
asynchronous: true
fillMode:Image.PreserveAspectCrop
}
Rectangle{
height: 40
width: parent.width
anchors.bottom: parent.bottom
color: "#33000000"
FluText{
anchors.fill: parent
verticalAlignment: Qt.AlignVCenter
horizontalAlignment: Qt.AlignHCenter
text:model.title
color: FluColors.Grey10
font.pixelSize: 15
}
}
}
}
Layout.topMargin: 20
Layout.leftMargin: 5
Component.onCompleted: {
var arr = []
arr.push({url:"qrc:/example/res/image/banner_1.jpg",title:"共同应对全球性问题"})
arr.push({url:"qrc:/example/res/image/banner_2.jpg",title:"三小只全程没互动"})
arr.push({url:"qrc:/example/res/image/banner_3.jpg",title:"有效投资扩大 激发增长动能"})
model = arr
}
}
}
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluCarousel{
id:carousel
width: 400
height: 300
delegate: Component{
Image {
anchors.fill: parent
source: model.url
asynchronous: true
fillMode:Image.PreserveAspectCrop
}
}
Component.onCompleted: {
setData([{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}])
carousel.model = [{url:"qrc:/example/res/image/banner_1.jpg"},{url:"qrc:/example/res/image/banner_2.jpg"},{url:"qrc:/example/res/image/banner_3.jpg"}]
}
}'
}

View File

@ -0,0 +1,331 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Window
import QtQuick.Controls
import FluentUI
import "qrc:///example/qml/component"
FluScrollablePage{
title:"Chart"
function randomScalingFactor() {
return Math.random().toFixed(1);
}
FluArea{
height: 370
width: 500
paddings: 10
Layout.topMargin: 20
FluChart{
anchors.fill: parent
chartType: 'scatter'
chartData: {
return {
datasets: [{
label: 'My First dataset',
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-1',
borderColor: '#ff5555',
backgroundColor: 'rgba(255,192,192,0.3)',
data: [{
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}]
}, {
label: 'My Second dataset',
xAxisID: 'x-axis-1',
yAxisID: 'y-axis-2',
borderColor: '#5555ff',
backgroundColor: 'rgba(192,192,255,0.3)',
data: [{
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}, {
x: randomScalingFactor(),
y: randomScalingFactor(),
}]
}]
}}
chartOptions: {return {
maintainAspectRatio: false,
responsive: true,
hoverMode: 'nearest',
intersect: true,
title: {
display: true,
text: 'Chart.js Scatter Chart - Multi Axis'
},
scales: {
xAxes: [{
position: 'bottom',
gridLines: {
zeroLineColor: 'rgba(0,0,0,1)'
}
}],
yAxes: [{
type: 'linear', // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
display: true,
position: 'left',
id: 'y-axis-1',
}, {
type: 'linear', // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
display: true,
position: 'right',
reverse: true,
id: 'y-axis-2',
// grid line settings
gridLines: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
}],
}
}
}
}
}
FluArea{
width: 500
height: 370
paddings: 10
Layout.topMargin: 20
FluChart{
anchors.fill: parent
chartType: 'bar'
chartData: { return {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'Dataset 1',
backgroundColor: '#ff9999',
stack: 'Stack 0',
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
]
}, {
label: 'Dataset 2',
backgroundColor: '#9999ff',
stack: 'Stack 0',
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
]
}, {
label: 'Dataset 3',
backgroundColor: '#99ff99',
stack: 'Stack 1',
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
]
}]
}
}
chartOptions: { return {
maintainAspectRatio: false,
title: {
display: true,
text: 'Chart.js Bar Chart - Stacked'
},
tooltips: {
mode: 'index',
intersect: false
},
responsive: true,
scales: {
xAxes: [{
stacked: true,
}],
yAxes: [{
stacked: true
}]
}
}
}
}
}
FluArea{
width: 500
height: 370
paddings: 10
Layout.topMargin: 20
FluChart{
anchors.fill: parent
chartType: 'pie'
chartData: {return {
datasets: [{
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
],
backgroundColor: [
'#ffbbbb',
'#ffddaa',
'#ffffbb',
'#bbffbb',
'#bbbbff'
],
label: 'Dataset 1'
}],
labels: [
'Red',
'Orange',
'Yellow',
'Green',
'Blue'
]
}}
chartOptions: {return {maintainAspectRatio: false, responsive: true}}
}
}
FluArea{
width: 500
height: 370
paddings: 10
Layout.topMargin: 20
FluChart{
anchors.fill: parent
chartType: 'line'
chartData: { return {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
datasets: [{
label: 'Filled',
fill: true,
backgroundColor: 'rgba(192,222,255,0.3)',
borderColor: 'rgba(128,192,255,255)',
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
],
}, {
label: 'Dashed',
fill: false,
backgroundColor: 'rgba(0,0,0,0)',
borderColor: '#009900',
borderDash: [5, 5],
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
],
}, {
label: 'Filled',
backgroundColor: 'rgba(0,0,0,0)',
borderColor: '#990000',
data: [
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor(),
randomScalingFactor()
],
fill: false,
}]
}
}
chartOptions: {return {
maintainAspectRatio: false,
responsive: true,
title: {
display: true,
text: 'Chart.js Line Chart'
},
tooltips: {
mode: 'index',
intersect: false,
},
hover: {
mode: 'nearest',
intersect: true
},
scales: {
xAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Month'
}
}],
yAxes: [{
display: true,
scaleLabel: {
display: true,
labelString: 'Value'
}
}]
}
}
}
}
}
}

View File

@ -31,7 +31,7 @@ FluScrollablePage{
title:"友情提示"
message:"确定要退出程序么?"
negativeText:"取消"
buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
onNegativeClicked:{
showSuccess("点击取消按钮")
}
@ -47,7 +47,7 @@ FluScrollablePage{
id:double_btn_dialog
title:"友情提示"
message:"确定要退出程序么?"
buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText:"取消"
onNegativeClicked:{
showSuccess("点击取消按钮")
@ -80,7 +80,7 @@ FluScrollablePage{
title:"友情提示"
message:"确定要退出程序么?"
negativeText:"取消"
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText:"取消"
onNegativeClicked:{
showSuccess("点击取消按钮")
@ -101,7 +101,7 @@ FluScrollablePage{
id:triple_btn_dialog
title:"友情提示"
message:"确定要退出程序么?"
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText:"取消"
onNegativeClicked:{
showSuccess("点击取消按钮")

View File

@ -7,7 +7,7 @@ import FluentUI
FluScrollablePage{
launchMode: FluPage.SingleTask
launchMode: FluPageType.SingleTask
animDisabled: true
ListModel{
@ -68,19 +68,17 @@ FluScrollablePage{
id: control
width: 220
height: 240
FluArea{
radius: 8
FluItem{
radius: [8,8,8,8]
width: 200
height: 220
anchors.centerIn: parent
color: 'transparent'
FluAcrylic {
sourceItem:bg
FluAcrylic{
anchors.fill: parent
color: FluTheme.dark ? Window.active ? Qt.rgba(38/255,44/255,54/255,1) : Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
rectX: list.x-list.contentX+10+(control.width)*index
rectY: list.y+10
acrylicOpacity:0.8
tintColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
target: bg
blurRadius : 40
targetRect: Qt.rect(list.x-list.contentX+10+(control.width)*index,list.y+10,width,height)
}
Rectangle{
anchors.fill: parent
@ -88,18 +86,17 @@ FluScrollablePage{
color:{
if(FluTheme.dark){
if(item_mouse.containsMouse){
return Qt.rgba(1,1,1,0.03)
return Qt.rgba(1,1,1,0.06)
}
return Qt.rgba(0,0,0,0)
return Qt.rgba(0,0,0,0.03)
}else{
if(item_mouse.containsMouse){
return Qt.rgba(0,0,0,0.03)
return Qt.rgba(0,0,0,0.09)
}
return Qt.rgba(0,0,0,0)
return Qt.rgba(0,0,0,0.06)
}
}
}
ColumnLayout{
Image {
Layout.topMargin: 20

214
example/qml/page/T_Http.qml Normal file
View File

@ -0,0 +1,214 @@
import QtQuick
import Qt.labs.platform
import QtQuick.Layouts
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Dialogs
import FluentUI
import "qrc:///example/qml/component"
FluContentPage{
title:"Http"
FluHttp{
id:http
}
Flickable{
id:layout_flick
width: 160
clip: true
anchors{
top: parent.top
topMargin: 20
bottom: parent.bottom
left: parent.left
}
ScrollBar.vertical: FluScrollBar {}
contentHeight:layout_column.height
Column{
spacing: 2
id:layout_column
width: parent.width
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "Get请求"
onClicked: {
var callable = {}
callable.onStart = function(){
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
http.get("https://httpbingo.org/get",callable)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "Post表单请求"
onClicked: {
var callable = {}
callable.onStart = function(){
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
param.custemail = "zhuzichu520@gmail.com"
http.post("https://httpbingo.org/post",callable,param)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "Post Json请求"
onClicked: {
var callable = {}
callable.onStart = function(){
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = {}
param.custname = "朱子楚"
param.custtel = "1234567890"
param.custemail = "zhuzichu520@gmail.com"
http.postJson("https://httpbingo.org/post",callable,param)
}
}
FluButton{
implicitWidth: parent.width
implicitHeight: 36
text: "Post String请求"
onClicked: {
var callable = {}
callable.onStart = function(){
showLoading()
}
callable.onFinish = function(){
hideLoading()
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString){
console.debug(status+";"+errorString)
}
var param = "我命由我不由天"
http.postString("https://httpbingo.org/post",callable,param)
}
}
FluButton{
id:btn_download
implicitWidth: parent.width
implicitHeight: 36
text: "下载文件"
onClicked: {
file_dialog.open()
}
}
}
}
FolderDialog {
id: file_dialog
currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
onAccepted: {
var callable = {}
callable.onStart = function(){
btn_download.disabled = true
}
callable.onFinish = function(){
btn_download.disabled = false
btn_download.text = "下载文件"
layout_file_size.visible = false
text_file_size.text = ""
}
callable.onSuccess = function(result){
showSuccess(result)
}
callable.onError = function(status,errorString){
showError(errorString)
}
callable.onDownloadProgress = function(recv,total){
var locale = Qt.locale()
var precent = (recv/total * 100).toFixed(0) + "%"
btn_download.text = "下载中..."+precent
text_file_size.text = "%1/%2".arg(locale.formattedDataSize(recv)).arg(locale.formattedDataSize(total))
layout_file_size.visible = true
}
var path = FluTools.toLocalPath(currentFolder)+ "/big_buck_bunny.mp4"
http.download("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",callable,path)
}
}
FluArea{
anchors{
top: layout_flick.top
bottom: layout_flick.bottom
left: layout_flick.right
right: parent.right
leftMargin: 8
}
Flickable{
clip: true
id:scrollview
width: parent.width
height: parent.height
contentWidth: width
contentHeight: text_info.height
ScrollBar.vertical: FluScrollBar {}
FluText{
id:text_info
width: scrollview.width
wrapMode: Text.WrapAnywhere
padding: 14
}
}
}
FluRectangle{
id:layout_file_size
radius: [4,4,4,4]
height: 36
width: 160
visible: false
x:layout_flick.width
y: 173 - layout_flick.contentY
FluText{
id:text_file_size
anchors.centerIn: parent
}
}
}

View File

@ -93,9 +93,9 @@ FluScrollablePage{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluWindow{
//launchMode: FluWindow.Standard
//launchMode: FluWindow.SingleTask
launchMode: FluWindow.SingleInstance
//launchMode: FluWindowType.Standard
//launchMode: FluWindowType.SingleTask
launchMode: FluWindowType.SingleInstance
}
'
}

View File

@ -12,7 +12,7 @@ FluScrollablePage{
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 110
height: 130
paddings: 10
ColumnLayout{
@ -45,7 +45,7 @@ FluProgressRing{
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
height: 230
height: 286
paddings: 10
ColumnLayout{
@ -59,21 +59,24 @@ FluProgressRing{
}
FluProgressBar{
indeterminate: false
progress: slider.value/100
}
FluProgressRing{
indeterminate: false
progress: slider.value/100
value:slider.value/100
Layout.topMargin: 10
}
FluProgressBar{
indeterminate: false
value:slider.value/100
progressVisible: true
progress: slider.value/100
Layout.topMargin: 10
}
FluProgressRing{
indeterminate: false
value: slider.value/100
Layout.topMargin: 10
}
FluProgressRing{
progressVisible: true
progress: slider.value/100
indeterminate: false
value: slider.value/100
}
FluSlider{
id:slider

View File

@ -0,0 +1,75 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import FluentUI
import Qt5Compat.GraphicalEffects
import "qrc:///example/qml/component"
FluScrollablePage{
title:"QRCode"
FluQRCode{
id:qrcode
Layout.topMargin: 20
size:slider_size.value
text:text_box.text
color:color_picker.colorValue
Layout.preferredWidth: size
Layout.preferredHeight: size
}
RowLayout{
spacing: 10
Layout.topMargin: 20
FluText{
text:"text:"
Layout.alignment: Qt.AlignVCenter
}
FluTextBox{
id:text_box
text:"会磨刀的小猪"
}
}
RowLayout{
spacing: 10
Layout.topMargin: 10
FluText{
text:"color:"
Layout.alignment: Qt.AlignVCenter
}
FluColorPicker{
id:color_picker
Component.onCompleted: {
setColor(Qt.rgba(0,0,0,1))
}
}
}
RowLayout{
spacing: 10
FluText{
text:"size:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_size
from:60
to:260
value: 120
}
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: 20
code:'FluQRCode{
color:"red"
text:"会磨刀的小猪"
size:100
}'
}
}

View File

@ -6,7 +6,7 @@ import FluentUI
import "qrc:///example/qml/component"
FluPage{
launchMode: FluPage.SingleTop
launchMode: FluPageType.SingleTop
FluRemoteLoader{
anchors.fill: parent
source: "https://zhu-zichu.gitee.io/T_RemoteLoader.qml"

View File

@ -28,7 +28,7 @@ FluScrollablePage{
Layout.bottomMargin: 4
}
Repeater{
model: [{title:"System",mode:FluDarkMode.System},{title:"Light",mode:FluDarkMode.Light},{title:"Dark",mode:FluDarkMode.Dark}]
model: [{title:"System",mode:FluThemeType.System},{title:"Light",mode:FluThemeType.Light},{title:"Dark",mode:FluThemeType.Dark}]
delegate: FluRadioButton{
checked : FluTheme.darkMode === modelData.mode
text:modelData.title
@ -58,7 +58,7 @@ FluScrollablePage{
Layout.bottomMargin: 4
}
Repeater{
model: [{title:"Open",mode:FluNavigationView.Open},{title:"Compact",mode:FluNavigationView.Compact},{title:"Minimal",mode:FluNavigationView.Minimal},{title:"Auto",mode:FluNavigationView.Auto}]
model: [{title:"Open",mode:FluNavigationViewType.Open},{title:"Compact",mode:FluNavigationViewType.Compact},{title:"Minimal",mode:FluNavigationViewType.Minimal},{title:"Auto",mode:FluNavigationViewType.Auto}]
delegate: FluRadioButton{
checked : MainEvent.displayMode===modelData.mode
text:modelData.title

View File

@ -25,28 +25,28 @@ FluScrollablePage{
text:"Loading"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Loading
status_view.statusMode = FluStatusViewType.Loading
}
}
FluMenuItem{
text:"Empty"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Empty
status_view.statusMode = FluStatusViewType.Empty
}
}
FluMenuItem{
text:"Error"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Error
status_view.statusMode = FluStatusViewType.Error
}
}
FluMenuItem{
text:"Success"
onClicked: {
btn_status_mode.text = text
status_view.statusMode = FluStatusView.Success
status_view.statusMode = FluStatusViewType.Success
}
}
}
@ -75,7 +75,7 @@ FluScrollablePage{
Layout.topMargin: -1
code:'FluStatusView{
anchors.fill: parent
statusMode: FluStatusView.Loading
statusMode: FluStatusViewType.Loading
Rectangle{
anchors.fill: parent
color:FluTheme.primaryColor.dark

View File

@ -44,21 +44,21 @@ FluScrollablePage{
text:"Equal"
onClicked: {
btn_tab_width_behavior.text = text
tab_view.tabWidthBehavior = FluTabView.Equal
tab_view.tabWidthBehavior = FluTabViewType.Equal
}
}
FluMenuItem{
text:"SizeToContent"
onClicked: {
btn_tab_width_behavior.text = text
tab_view.tabWidthBehavior = FluTabView.SizeToContent
tab_view.tabWidthBehavior = FluTabViewType.SizeToContent
}
}
FluMenuItem{
text:"Compact"
onClicked: {
btn_tab_width_behavior.text = text
tab_view.tabWidthBehavior = FluTabView.Compact
tab_view.tabWidthBehavior = FluTabViewType.Compact
}
}
}
@ -70,21 +70,21 @@ FluScrollablePage{
text:"Nerver"
onClicked: {
btn_close_button_visibility.text = text
tab_view.closeButtonVisibility = FluTabView.Nerver
tab_view.closeButtonVisibility = FluTabViewType.Nerver
}
}
FluMenuItem{
text:"Always"
onClicked: {
btn_close_button_visibility.text = text
tab_view.closeButtonVisibility = FluTabView.Always
tab_view.closeButtonVisibility = FluTabViewType.Always
}
}
FluMenuItem{
text:"OnHover"
onClicked: {
btn_close_button_visibility.text = text
tab_view.closeButtonVisibility = FluTabView.OnHover
tab_view.closeButtonVisibility = FluTabViewType.OnHover
}
}
}

View File

@ -7,7 +7,7 @@ import "qrc:///example/qml/component"
FluScrollablePage{
launchMode: FluPage.SingleInstance
launchMode: FluPageType.SingleInstance
title:"TextBox"
FluArea{
@ -17,27 +17,22 @@ FluScrollablePage{
Layout.topMargin: 20
FluTextBox{
Layout.topMargin: 20
placeholderText: "单行输入框"
Layout.preferredWidth: 300
disabled:text_box_switch.checked
cleanEnabled: true
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
}
Row{
spacing: 5
FluToggleSwitch{
id:text_box_switch
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
}
FluToggleSwitch{
id:text_box_switch
Layout.alignment: Qt.AlignRight
text:"Disabled"
}
text:"Disabled"
}
}
CodeExpander{
@ -55,27 +50,20 @@ FluScrollablePage{
Layout.topMargin: 20
FluPasswordBox{
Layout.topMargin: 20
placeholderText: "请输入密码"
Layout.preferredWidth: 300
disabled:password_box_switch.checked
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
}
Row{
spacing: 5
FluToggleSwitch{
id:password_box_switch
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
}
FluToggleSwitch{
id:password_box_switch
Layout.alignment: Qt.AlignRight
text:"Disabled"
}
text:"Disabled"
}
}
CodeExpander{
@ -95,9 +83,7 @@ FluScrollablePage{
FluMultilineTextBox{
id:multiine_textbox
Layout.topMargin: 20
placeholderText: "多行输入框"
Layout.preferredWidth: 300
disabled:text_box_multi_switch.checked
anchors{
verticalCenter: parent.verticalCenter
@ -105,17 +91,13 @@ FluScrollablePage{
}
}
Row{
spacing: 5
FluToggleSwitch{
id:text_box_multi_switch
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
}
FluToggleSwitch{
id:text_box_multi_switch
Layout.alignment: Qt.AlignRight
text:"Disabled"
}
text:"Disabled"
}
}
CodeExpander{
@ -126,17 +108,13 @@ FluScrollablePage{
}'
}
FluArea{
Layout.fillWidth: true
height: 68
paddings: 10
Layout.topMargin: 20
FluAutoSuggestBox{
Layout.topMargin: 20
placeholderText: "AutoSuggestBox"
Layout.preferredWidth: 300
items:generateRandomNames(100)
disabled:text_box_suggest_switch.checked
anchors{
@ -144,18 +122,13 @@ FluScrollablePage{
left: parent.left
}
}
Row{
spacing: 5
FluToggleSwitch{
id:text_box_suggest_switch
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
}
FluToggleSwitch{
id:text_box_suggest_switch
Layout.alignment: Qt.AlignRight
text:"Disabled"
}
text:"Disabled"
}
}
CodeExpander{
@ -171,27 +144,20 @@ FluScrollablePage{
height: 68
paddings: 10
Layout.topMargin: 20
FluSpinBox{
Layout.topMargin: 20
disabled: spin_box_switch.checked
anchors{
verticalCenter: parent.verticalCenter
left: parent.left
}
}
Row{
spacing: 5
FluToggleSwitch{
id:spin_box_switch
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
}
FluToggleSwitch{
id:spin_box_switch
Layout.alignment: Qt.AlignRight
text:"Disabled"
}
text:"Disabled"
}
}
CodeExpander{

View File

@ -55,9 +55,9 @@ FluScrollablePage{
checked: FluTheme.dark
onClicked: {
if(FluTheme.dark){
FluTheme.darkMode = FluDarkMode.Light
FluTheme.darkMode = FluThemeType.Light
}else{
FluTheme.darkMode = FluDarkMode.Dark
FluTheme.darkMode = FluThemeType.Dark
}
}
}

View File

@ -8,7 +8,7 @@ import "qrc:///example/qml/component"
FluScrollablePage{
title:"TimePicker"
launchMode: FluPage.SingleInstance
launchMode: FluPageType.SingleInstance
FluArea{
Layout.fillWidth: true
Layout.topMargin: 20
@ -23,7 +23,7 @@ FluScrollablePage{
}
FluText{
text:"hourFormat=FluTimePicker.H"
text:"hourFormat=FluTimePickerType.H"
}
FluTimePicker{
@ -56,11 +56,11 @@ FluScrollablePage{
}
FluText{
text:"hourFormat=FluTimePicker.HH"
text:"hourFormat=FluTimePickerType.HH"
}
FluTimePicker{
hourFormat:FluTimePicker.HH
hourFormat:FluTimePickerType.HH
onCurrentChanged: {
showSuccess(current.toLocaleTimeString(Qt.locale("de_DE")))
}
@ -72,7 +72,7 @@ FluScrollablePage{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluTimePicker{
hourFormat:FluTimePicker.HH
hourFormat:FluTimePickerType.HH
}'
}

View File

@ -0,0 +1,157 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Window
import QtQuick.Controls
import FluentUI
import "qrc:///example/qml/component"
FluScrollablePage{
title:"Timeline"
Component{
id:com_dot
Rectangle{
width: 16
height: 16
radius: 8
border.width: 4
border.color: FluColors.Red.dark
}
}
Component{
id:com_lable
FluText{
wrapMode: Text.WrapAnywhere
horizontalAlignment: textAlignment
text: modelData.lable
color: FluTheme.primaryColor.dark
MouseArea{
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
showSuccess(modelData.lable)
}
}
}
}
Component{
id:com_text
FluText{
wrapMode: Text.WrapAnywhere
horizontalAlignment: textAlignment
text: modelData.text
font.bold: true
MouseArea{
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
showSuccess(modelData.text)
}
}
}
}
ListModel{
id:list_model
ListElement{
lable:"2013-09-01"
lableDelegate:()=>com_lable
textDelegate:()=>com_text
text:"考上家里蹲大学"
}
ListElement{
lable:"2017-07-01"
text:"大学毕业在寝室打了4年LOL没想到毕业还要找工作瞬间蒙蔽~害"
}
ListElement{
lable:"2017-09-01"
text:"开始找工作,毕业即失业!回农村老家躺平,继承三亩良田!!"
dot:()=>com_dot
}
ListElement{
lable:"2018-02-01"
text:"玩了一年没钱,惨,出去找工作上班"
}
ListElement{
lable:"2018-03-01"
text:"找到一份Android外包开发岗位开发了一个Android应用满满成就感前端、服务端、Flutter也都懂一丢丢什么都会什么都不精通钱途无望"
}
ListElement{
lable:"2020-06-01"
text:"由于某个项目紧急临时加入Qt项目组就因为大学学了点C++),本来是想进去打个酱油,到后面竟然成开发主力,坑啊"
}
ListElement{
lable:"2022-08-01"
text:"额,被老板卖到甲方公司,走时老板还问我想不想去,我说:'哪里工资高就去哪里?',老板:'无语'"
}
ListElement{
lable:"2023-02-28"
text:"开发FluentUI组件库"
}
}
RowLayout{
spacing: 20
Layout.topMargin: 20
FluTextBox{
id:text_box
text:"Technical testing 2015-09-01"
}
FluFilledButton{
text:"Append"
onClicked: {
list_model.append({text:text_box.text})
}
}
FluFilledButton{
text:"clear"
onClicked: {
list_model.clear()
}
}
}
RowLayout{
Layout.topMargin: 10
FluText{
text:"mode:"
}
FluDropDownButton{
id:btn_mode
Layout.preferredWidth: 100
text:"Alternate"
FluMenuItem{
text:"Left"
onClicked: {
btn_mode.text = text
time_line.mode = FluTimelineType.Left
}
}
FluMenuItem{
text:"Right"
onClicked: {
btn_mode.text = text
time_line.mode = FluTimelineType.Right
}
}
FluMenuItem{
text:"Alternate"
onClicked: {
btn_mode.text = text
time_line.mode = FluTimelineType.Alternate
}
}
}
}
FluTimeline{
id:time_line
Layout.fillWidth: true
mode: FluTimelineType.Alternate
model:list_model
}
}

View File

@ -0,0 +1,80 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Window
import QtQuick.Controls
import FluentUI
import "qrc:///example/qml/component"
FluScrollablePage{
title:"Tour"
FluTour{
id:tour
steps:[
{title:"Upload File",description: "Put your files here.",target:()=>btn_upload},
{title:"Save",description: "Save your changes.",target:()=>btn_save},
{title:"Other Actions",description: "Click to see other actions.",target:()=>btn_more}
]
}
FluArea{
Layout.fillWidth: true
height: 130
paddings: 10
Layout.topMargin: 20
FluFilledButton{
anchors{
top: parent.top
topMargin: 14
}
text:"Begin Tour"
onClicked: {
tour.open()
}
}
Row{
spacing: 20
anchors{
top: parent.top
topMargin: 60
}
FluButton{
id:btn_upload
text:"Upload"
onClicked: {
showInfo("Upload")
}
}
FluFilledButton{
id:btn_save
text:"Save"
onClicked: {
showInfo("Save")
}
}
FluIconButton{
id:btn_more
iconSource: FluentIcons.More
onClicked: {
showInfo("More")
}
}
}
}
CodeExpander{
Layout.fillWidth: true
Layout.topMargin: -1
code:'FluTour{
id:tour
steps:[
{title:"Upload File",description: "Put your files here.",target:()=>btn_upload},
{title:"Save",description: "Save your changes.",target:()=>btn_save},
{title:"Other Actions",description: "Click to see other actions.",target:()=>btn_more}
]
}'
}
}

View File

@ -64,38 +64,38 @@ FluScrollablePage {
text:"None"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabView.Equal
tree_view.selectionMode = FluTabViewType.Equal
}
}
FluMenuItem{
text:"Single"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabView.SizeToContent
tree_view.selectionMode = FluTabViewType.SizeToContent
}
}
FluMenuItem{
text:"Muiltple"
onClicked: {
btn_selection_model.text = text
tree_view.selectionMode = FluTabView.Compact
tree_view.selectionMode = FluTabViewType.Compact
}
}
}
FluFilledButton{
text:"获取选中的数据"
onClicked: {
if(tree_view.selectionMode === FluTreeView.None){
if(tree_view.selectionMode === FluTreeViewType.None){
showError("当前非选择模式,没有选中的数据")
}
if(tree_view.selectionMode === FluTreeView.Single){
if(tree_view.selectionMode === FluTreeViewType.Single){
if(!tree_view.signleData()){
showError("没有选中数据")
return
}
showSuccess(tree_view.signleData().text)
}
if(tree_view.selectionMode === FluTreeView.Multiple){
if(tree_view.selectionMode === FluTreeViewType.Multiple){
if(tree_view.multipData().length===0){
showError("没有选中数据")
return

View File

@ -0,0 +1,132 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import FluentUI
import "qrc:///example/qml/component"
FluContentPage{
title:"Watermark"
FluArea{
anchors.fill: parent
anchors.topMargin: 20
ColumnLayout{
anchors{
left: parent.left
leftMargin: 14
}
RowLayout{
spacing: 10
Layout.topMargin: 14
FluText{
text:"text:"
Layout.alignment: Qt.AlignVCenter
}
FluTextBox{
id:text_box
text:"会磨刀的小猪"
}
}
RowLayout{
spacing: 10
FluText{
text:"textSize:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_text_size
value: 20
from: 13
to:50
}
}
RowLayout{
spacing: 10
FluText{
text:"gapX:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_gap_x
value: 100
}
}
RowLayout{
spacing: 10
FluText{
text:"gapY:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_gap_y
value: 100
}
}
RowLayout{
spacing: 10
FluText{
text:"offsetX:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_offset_x
value: 50
}
}
RowLayout{
spacing: 10
FluText{
text:"offsetY:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_offset_y
value: 50
}
}
RowLayout{
spacing: 10
FluText{
text:"rotate:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_rotate
value: 22
from: 0
to:360
}
}
RowLayout{
spacing: 10
FluText{
text:"textColor:"
Layout.alignment: Qt.AlignVCenter
}
FluColorPicker{
id:color_picker
Component.onCompleted: {
setColor(Qt.rgba(0,0,0,0.1))
}
}
}
}
FluWatermark{
id:water_mark
anchors.fill: parent
text:text_box.text
textColor: color_picker.colorValue
textSize: slider_text_size.value
rotate: slider_rotate.value
gap:Qt.point(slider_gap_x.value,slider_gap_y.value)
offset: Qt.point(slider_offset_x.value,slider_offset_y.value)
}
}
}

View File

@ -11,7 +11,7 @@ CustomWindow {
width: 600
height: 600
fixSize: true
launchMode: FluWindow.SingleTask
launchMode: FluWindowType.SingleTask
ColumnLayout{
anchors{

View File

@ -13,7 +13,7 @@ CustomWindow {
height: 600
minimumWidth: 520
minimumHeight: 200
launchMode: FluWindow.SingleTask
launchMode: FluWindowType.SingleTask
FileWatcher{
id:watcher
onFileChanged: {
@ -25,7 +25,7 @@ CustomWindow {
FluRemoteLoader{
id:loader
anchors.fill: parent
statusMode: FluStatusView.Success
statusMode: FluStatusViewType.Success
lazy: true
errorItem: Item{
FluText{
@ -43,7 +43,7 @@ CustomWindow {
text:"拖入qml文件"
font.pixelSize: 26
anchors.centerIn: parent
visible: !loader.itemLodaer().item && loader.statusMode === FluStatusView.Success
visible: !loader.itemLodaer().item && loader.statusMode === FluStatusViewType.Success
}
Rectangle{
radius: 4

View File

@ -18,7 +18,7 @@ CustomWindow {
minimumWidth: 520
minimumHeight: 200
appBarVisible: false
launchMode: FluWindow.SingleTask
launchMode: FluWindowType.SingleTask
closeFunc:function(event){
dialog_close.open()
@ -27,15 +27,7 @@ CustomWindow {
Component.onCompleted: {
FluTools.setQuitOnLastWindowClosed(false)
}
Connections{
target: appInfo
function onActiveWindow(){
window.show()
window.raise()
window.requestActivate()
}
tour.open()
}
SystemTrayIcon {
@ -67,14 +59,13 @@ CustomWindow {
title:"退出"
message:"确定要退出程序吗?"
negativeText:"最小化"
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.NeutralButton | FluContentDialogType.PositiveButton
onNegativeClicked:{
window.hide()
system_tray.showMessage("友情提示","FluentUI已隐藏至托盘,点击托盘可再次激活窗口");
}
positiveText:"退出"
neutralText:"取消"
blurSource: nav_view
onPositiveClicked:{
window.deleteWindow()
FluApp.closeApp()
@ -156,6 +147,7 @@ CustomWindow {
visible: flipable.flipAngle !== 180
anchors.fill: flipable
FluAppBar {
id:app_bar_front
anchors {
top: parent.top
left: parent.left
@ -173,9 +165,9 @@ CustomWindow {
height: parent.height
z:999
//Stack模式每次切换都会将页面压入栈中随着栈的页面增多消耗的内存也越多内存消耗多就会卡顿这时候就需要按返回将页面pop掉释放内存。该模式可以配合FluPage中的launchMode属性设置页面的启动模式
// pageMode: FluNavigationView.Stack
pageMode: FluNavigationViewType.Stack
//NoStack模式每次切换都会销毁之前的页面然后创建一个新的页面只需消耗少量内存推荐
// pageMode: FluNavigationView.NoStack
// pageMode: FluNavigationViewType.NoStack
items: ItemsOriginal
footerItems:ItemsFooter
topPadding:FluTools.isMacos() ? 20 : 5
@ -184,7 +176,8 @@ CustomWindow {
title:"FluentUI"
onLogoClicked:{
clickCount += 1
if(clickCount === 1){
showSuccess("点击%1次".arg(clickCount))
if(clickCount === 5){
loader.reload()
flipable.flipped = true
clickCount = 0
@ -210,15 +203,27 @@ CustomWindow {
}
}
CircularReveal{
id:reveal
target:window.contentItem
anchors.fill: parent
onImageChanged: {
changeDark()
Component{
id:com_reveal
CircularReveal{
id:reveal
target:window.contentItem
anchors.fill: parent
onAnimationFinished:{
//动画结束后释放资源
loader_reveal.sourceComponent = undefined
}
onImageChanged: {
changeDark()
}
}
}
Loader{
id:loader_reveal
anchors.fill: parent
}
function distance(x1,y1,x2,y2){
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
}
@ -227,20 +232,22 @@ CustomWindow {
if(FluTools.isMacos() || !FluTheme.enableAnimation){
changeDark()
}else{
loader_reveal.sourceComponent = com_reveal
var target = window.contentItem
var pos = button.mapToItem(target,0,0)
var mouseX = pos.x
var mouseY = pos.y
var radius = Math.max(distance(mouseX,mouseY,0,0),distance(mouseX,mouseY,target.width,0),distance(mouseX,mouseY,0,target.height),distance(mouseX,mouseY,target.width,target.height))
var reveal = loader_reveal.item
reveal.start(reveal.width*Screen.devicePixelRatio,reveal.height*Screen.devicePixelRatio,Qt.point(mouseX,mouseY),radius)
}
}
function changeDark(){
if(FluTheme.dark){
FluTheme.darkMode = FluDarkMode.Light
FluTheme.darkMode = FluThemeType.Light
}else{
FluTheme.darkMode = FluDarkMode.Dark
FluTheme.darkMode = FluThemeType.Dark
}
}
@ -254,5 +261,20 @@ CustomWindow {
}
}
Shortcut {
sequence: "F6"
context: Qt.WindowShortcut
onActivated: {
tour.open()
}
}
FluTour{
id:tour
steps:[
{title:"夜间模式",description: "这里可以切换夜间模式.",target:()=>app_bar_front.darkButton()},
{title:"隐藏彩蛋",description: "多点几下试试!!",target:()=>nav_view.logoButton()}
]
}
}

View File

@ -11,7 +11,7 @@ CustomWindow {
width: 500
height: 600
fixSize: true
launchMode: FluWindow.SingleInstance
launchMode: FluWindowType.SingleInstance
FluTextBox{
anchors{

View File

@ -11,7 +11,7 @@ CustomWindow {
width: 500
height: 600
fixSize: true
launchMode: FluWindow.SingleTask
launchMode: FluWindowType.SingleTask
FluText{
anchors.centerIn: parent

View File

@ -11,7 +11,7 @@ CustomWindow {
width: 500
height: 600
fixSize: true
launchMode: FluWindow.Standard
launchMode: FluWindowType.Standard
FluMenuBar {
FluMenu {

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

View File

@ -4,16 +4,12 @@
#include <QDebug>
#include "lang/En.h"
#include "lang/Zh.h"
#define STR(x) #x
#define VER_JOIN(a,b,c,d) STR(a.b.c.d)
#define VER_JOIN_(x) VER_JOIN x
#define VER_STR VER_JOIN_((VERSION))
#include "Version.h"
AppInfo::AppInfo(QObject *parent)
: QObject{parent}
{
version(VER_STR);
version(APPLICATION_VERSION);
lang(new En());
}
@ -39,18 +35,3 @@ void AppInfo::changeLang(const QString& locale){
lang(new En());
}
}
bool AppInfo::isOwnerProcess(IPC *ipc){
QString activeWindowEvent = "activeWindow";
if(!ipc->isCurrentOwner()){
ipc->postEvent(activeWindowEvent,QString().toUtf8(),0);
return false;
}
if(ipc->isAttached()){
ipc->registerEventHandler(activeWindowEvent,[=](const QByteArray&){
Q_EMIT this->activeWindow();
return true;
});
}
return true;
}

View File

@ -3,7 +3,6 @@
#include <QObject>
#include <QQmlApplicationEngine>
#include "tool/IPC.h"
#include "lang/Lang.h"
#include "stdafx.h"
@ -15,9 +14,7 @@ class AppInfo : public QObject
public:
explicit AppInfo(QObject *parent = nullptr);
void init(QQmlApplicationEngine *engine);
bool isOwnerProcess(IPC *ipc);
Q_INVOKABLE void changeLang(const QString& locale);
Q_SIGNAL void activeWindow();
};
#endif // APPINFO_H

View File

@ -6,11 +6,12 @@
CircularReveal::CircularReveal(QQuickItem* parent) : QQuickPaintedItem(parent)
{
setVisible(false);
_anim = new QPropertyAnimation(this, "radius", this);
_anim->setDuration(333);
_anim->setEasingCurve(QEasingCurve::OutCubic);
connect(_anim, &QPropertyAnimation::finished,this,[=](){
_anim.setDuration(333);
_anim.setEasingCurve(QEasingCurve::OutCubic);
connect(&_anim, &QPropertyAnimation::finished,this,[=](){
update();
setVisible(false);
Q_EMIT animationFinished();
});
connect(this,&CircularReveal::radiusChanged,this,[=](){
update();
@ -31,8 +32,8 @@ void CircularReveal::paint(QPainter* painter)
}
void CircularReveal::start(int w,int h,const QPoint& center,int radius){
_anim->setStartValue(0);
_anim->setEndValue(radius);
_anim.setStartValue(0);
_anim.setEndValue(radius);
_center = center;
_grabResult = _target->grabToImage(QSize(w,h));
connect(_grabResult.data(), &QQuickItemGrabResult::ready, this, &CircularReveal::handleGrabResult);
@ -43,5 +44,5 @@ void CircularReveal::handleGrabResult(){
update();
setVisible(true);
Q_EMIT imageChanged();
_anim->start();
_anim.start();
}

View File

@ -12,15 +12,17 @@ class CircularReveal : public QQuickPaintedItem
Q_OBJECT
Q_PROPERTY_AUTO(QQuickItem*,target)
Q_PROPERTY_AUTO(int,radius)
QML_NAMED_ELEMENT(CircularReveal)
public:
CircularReveal(QQuickItem* parent = nullptr);
void paint(QPainter* painter) override;
Q_INVOKABLE void start(int w,int h,const QPoint& center,int radius);
Q_SIGNAL void imageChanged();
Q_SIGNAL void animationFinished();
Q_SLOT void handleGrabResult();
private:
QImage _source;
QPropertyAnimation* _anim;
QPropertyAnimation _anim = QPropertyAnimation(this, "radius", this);
QPoint _center;
QSharedPointer<QQuickItemGrabResult> _grabResult;
};

View File

@ -3,12 +3,14 @@
#include <QObject>
#include <QFileSystemWatcher>
#include <QtQml/qqml.h>
#include "src/stdafx.h"
class FileWatcher : public QObject
{
Q_OBJECT
Q_PROPERTY_AUTO(QString,path);
QML_NAMED_ELEMENT(FileWatcher)
public:
explicit FileWatcher(QObject *parent = nullptr);
Q_SIGNAL void fileChanged();

View File

@ -3,11 +3,11 @@
#include <QQmlContext>
#include <QDir>
#include <QQuickWindow>
#include <QNetworkProxy>
#include <QSslConfiguration>
#include <QProcess>
#include <FramelessHelper/Quick/framelessquickmodule.h>
#include <FramelessHelper/Core/private/framelessconfig_p.h>
#include "src/component/CircularReveal.h"
#include "src/component/FileWatcher.h"
#include "AppInfo.h"
FRAMELESSHELPER_USE_NAMESPACE
@ -21,28 +21,22 @@ FRAMELESSHELPER_USE_NAMESPACE
QGuiApplication::setOrganizationDomain("https://zhuzichu520.github.io");
QGuiApplication::setApplicationName("FluentUI");
QGuiApplication app(argc, argv);
#ifdef Q_OS_WIN // 此设置仅在Windows下生效
FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder);
#endif
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
FramelessConfig::instance()->set(Global::Option::CenterWindowBeforeShow);
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur);
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
#ifdef Q_OS_WIN // 此设置仅在Windows下生效
FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder);
#endif
#ifdef Q_OS_MACOS
FramelessConfig::instance()->set(Global::Option::ForceNonNativeBackgroundBlur,false);
#endif
AppInfo* appInfo = new AppInfo();
IPC ipc(0);
if(!appInfo->isOwnerProcess(&ipc)){
return 0;
}
QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes(&engine);
#ifdef FLUENTUI_BUILD_STATIC_LIB
engine.addImportPath("qrc:/"); // 让静态资源可以被QML引擎搜索到
#endif
qmlRegisterType<CircularReveal>("example", 1, 0, "CircularReveal");
qmlRegisterType<FileWatcher>("example", 1, 0, "FileWatcher");
appInfo->init(&engine);
const QUrl url(QStringLiteral("qrc:/example/qml/App.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,

View File

@ -1,250 +0,0 @@
#include "IPC.h"
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <ctime>
#include <random>
IPC::IPC(uint32_t profileId)
: profileId{profileId}
, globalMemory{"ipc-" IPC_PROTOCOL_VERSION}
{
qRegisterMetaType<IPCEventHandler>("IPCEventHandler");
timer.setInterval(EVENT_TIMER_MS);
timer.setSingleShot(true);
connect(&timer, &QTimer::timeout, this, &IPC::processEvents);
std::default_random_engine randEngine((std::random_device())());
std::uniform_int_distribution<uint64_t> distribution;
globalId = distribution(randEngine);
qDebug() << "Our global IPC ID is " << globalId;
if (globalMemory.create(sizeof(IPCMemory))) {
if (globalMemory.lock()) {
IPCMemory* mem = global();
memset(mem, 0, sizeof(IPCMemory));
mem->globalId = globalId;
mem->lastProcessed = time(nullptr);
globalMemory.unlock();
} else {
qWarning() << "Couldn't lock to take ownership";
}
} else if (globalMemory.attach()) {
qDebug() << "Attaching to the global shared memory";
} else {
qDebug() << "Failed to attach to the global shared memory, giving up. Error:"
<< globalMemory.error();
return;
}
processEvents();
}
IPC::~IPC()
{
if (!globalMemory.lock()) {
qWarning() << "Failed to lock in ~IPC";
return;
}
if (isCurrentOwnerNoLock()) {
global()->globalId = 0;
}
globalMemory.unlock();
}
time_t IPC::postEvent(const QString& name, const QByteArray& data, uint32_t dest)
{
QByteArray binName = name.toUtf8();
if (binName.length() > (int32_t)sizeof(IPCEvent::name)) {
return 0;
}
if (data.length() > (int32_t)sizeof(IPCEvent::data)) {
return 0;
}
if (!globalMemory.lock()) {
qDebug() << "Failed to lock in postEvent()";
return 0;
}
IPCEvent* evt = nullptr;
IPCMemory* mem = global();
time_t result = 0;
for (uint32_t i = 0; !evt && i < EVENT_QUEUE_SIZE; ++i) {
if (mem->events[i].posted == 0) {
evt = &mem->events[i];
}
}
if (evt) {
memset(evt, 0, sizeof(IPCEvent));
memcpy(evt->name, binName.constData(), binName.length());
memcpy(evt->data, data.constData(), data.length());
mem->lastEvent = evt->posted = result = qMax(mem->lastEvent + 1, time(nullptr));
evt->dest = dest;
evt->sender = qApp->applicationPid();
qDebug() << "postEvent " << name << "to" << dest;
}
globalMemory.unlock();
return result;
}
bool IPC::isCurrentOwner()
{
if (globalMemory.lock()) {
const bool isOwner = isCurrentOwnerNoLock();
globalMemory.unlock();
return isOwner;
} else {
qWarning() << "isCurrentOwner failed to lock, returning false";
return false;
}
}
void IPC::registerEventHandler(const QString& name, IPCEventHandler handler)
{
eventHandlers[name] = handler;
}
bool IPC::isEventAccepted(time_t time)
{
bool result = false;
if (!globalMemory.lock()) {
return result;
}
if (difftime(global()->lastProcessed, time) > 0) {
IPCMemory* mem = global();
for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) {
if (mem->events[i].posted == time && mem->events[i].processed) {
result = mem->events[i].accepted;
break;
}
}
}
globalMemory.unlock();
return result;
}
bool IPC::waitUntilAccepted(time_t postTime, int32_t timeout /*=-1*/)
{
bool result = false;
time_t start = time(nullptr);
forever
{
result = isEventAccepted(postTime);
if (result || (timeout > 0 && difftime(time(nullptr), start) >= timeout)) {
break;
}
qApp->processEvents();
QThread::msleep(0);
}
return result;
}
bool IPC::isAttached() const
{
return globalMemory.isAttached();
}
void IPC::setProfileId(uint32_t profileId)
{
this->profileId = profileId;
}
IPC::IPCEvent* IPC::fetchEvent()
{
IPCMemory* mem = global();
for (uint32_t i = 0; i < EVENT_QUEUE_SIZE; ++i) {
IPCEvent* evt = &mem->events[i];
if ((evt->processed && difftime(time(nullptr), evt->processed) > EVENT_GC_TIMEOUT)
|| (!evt->processed && difftime(time(nullptr), evt->posted) > EVENT_GC_TIMEOUT)) {
memset(evt, 0, sizeof(IPCEvent));
}
if (evt->posted && !evt->processed && evt->sender != qApp->applicationPid()
&& (evt->dest == profileId || (evt->dest == 0 && isCurrentOwnerNoLock()))) {
return evt;
}
}
return nullptr;
}
bool IPC::runEventHandler(IPCEventHandler handler, const QByteArray& arg)
{
bool result = false;
if (QThread::currentThread() == qApp->thread()) {
result = handler(arg);
} else {
QMetaObject::invokeMethod(this, "runEventHandler", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result), Q_ARG(IPCEventHandler, handler),
Q_ARG(const QByteArray&, arg));
}
return result;
}
void IPC::processEvents()
{
if (!globalMemory.lock()) {
timer.start();
return;
}
IPCMemory* mem = global();
if (mem->globalId == globalId) {
mem->lastProcessed = time(nullptr);
} else {
if (difftime(time(nullptr), mem->lastProcessed) >= OWNERSHIP_TIMEOUT_S) {
qDebug() << "Previous owner timed out, taking ownership" << mem->globalId << "->"
<< globalId;
memset(mem, 0, sizeof(IPCMemory));
mem->globalId = globalId;
mem->lastProcessed = time(nullptr);
}
}
while (IPCEvent* evt = fetchEvent()) {
QString name = QString::fromUtf8(evt->name);
auto it = eventHandlers.find(name);
if (it != eventHandlers.end()) {
evt->accepted = runEventHandler(it.value(), evt->data);
qDebug() << "Processed event:" << name << "posted:" << evt->posted
<< "accepted:" << evt->accepted;
if (evt->dest == 0) {
if (evt->accepted) {
evt->processed = time(nullptr);
}
} else {
evt->processed = time(nullptr);
}
} else {
qDebug() << "Received event:" << name << "without handler";
qDebug() << "Available handlers:" << eventHandlers.keys();
}
}
globalMemory.unlock();
timer.start();
}
bool IPC::isCurrentOwnerNoLock()
{
const void* const data = globalMemory.data();
if (!data) {
qWarning() << "isCurrentOwnerNoLock failed to access the memory, returning false";
return false;
}
return (*static_cast<const uint64_t*>(data) == globalId);
}
IPC::IPCMemory* IPC::global()
{
return static_cast<IPCMemory*>(globalMemory.data());
}

View File

@ -1,75 +0,0 @@
#ifndef IPC_H
#define IPC_H
#include <QMap>
#include <QObject>
#include <QSharedMemory>
#include <QTimer>
#include <ctime>
#include <functional>
using IPCEventHandler = std::function<bool(const QByteArray&)>;
#define IPC_PROTOCOL_VERSION "1"
class IPC : public QObject
{
Q_OBJECT
protected:
static const int EVENT_TIMER_MS = 1000;
static const int EVENT_GC_TIMEOUT = 5;
static const int EVENT_QUEUE_SIZE = 32;
static const int OWNERSHIP_TIMEOUT_S = 5;
public:
IPC(uint32_t profileId);
~IPC();
struct IPCEvent
{
uint32_t dest;
int32_t sender;
char name[16];
char data[128];
time_t posted;
time_t processed;
uint32_t flags;
bool accepted;
bool global;
};
struct IPCMemory
{
uint64_t globalId;
time_t lastEvent;
time_t lastProcessed;
IPCEvent events[IPC::EVENT_QUEUE_SIZE];
};
time_t postEvent(const QString& name, const QByteArray& data = QByteArray(), uint32_t dest = 0);
bool isCurrentOwner();
void registerEventHandler(const QString& name, IPCEventHandler handler);
bool isEventAccepted(time_t time);
bool waitUntilAccepted(time_t time, int32_t timeout = -1);
bool isAttached() const;
public slots:
void setProfileId(uint32_t profileId);
private:
IPCMemory* global();
bool runEventHandler(IPCEventHandler handler, const QByteArray& arg);
IPCEvent* fetchEvent();
void processEvents();
bool isCurrentOwnerNoLock();
private:
QTimer timer;
uint64_t globalId;
uint32_t profileId;
QSharedMemory globalMemory;
QMap<QString, IPCEventHandler> eventHandlers;
};
#endif // IPC_H

View File

@ -11,9 +11,6 @@ if(APPLE)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
#设置版本号
add_definitions(-DVERSION=1,3,7,4)
find_package(Qt6 REQUIRED COMPONENTS Core Quick Qml)
if(QT_VERSION VERSION_GREATER_EQUAL "6.3")
@ -39,7 +36,7 @@ foreach(filepath ${QML_PATHS})
endforeach(filepath)
#遍历所有资源文件
file(GLOB_RECURSE RES_PATHS *.png *.jpg *.svg *.ico *.ttf *.webp qmldir)
file(GLOB_RECURSE RES_PATHS *.png *.jpg *.svg *.ico *.ttf *.webp *.js qmldir)
foreach(filepath ${RES_PATHS})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
list(APPEND resource_files ${filename})
@ -58,37 +55,42 @@ else()
set(LIB_TYPE "SHARED")
endif()
qt_add_library(${PROJECT_NAME} ${LIB_TYPE})
if (FLUENTUI_BUILD_STATIC_LIB)
qt_add_qml_module(${PROJECT_NAME}
#在静态库编译中使用PLUGIN_TARGET会导致链接失败
OUTPUT_DIRECTORY ${FLUENTUI_QML_PLUGIN_DIRECTORY}
VERSION 1.0
URI "FluentUI"
TYPEINFO "plugins.qmltypes"
SOURCES ${sources_files} fluentui.rc
QML_FILES ${qml_files}
RESOURCES ${resource_files}
)
set(PLUGIN_TARGET_NAME "")
else()
qt_add_qml_module(${PROJECT_NAME}
#没有下面这行代码就会生成fluentuiplugin.dll与fluentuipluginplugin.dll两个动态库
PLUGIN_TARGET fluentuiplugin
OUTPUT_DIRECTORY ${FLUENTUI_QML_PLUGIN_DIRECTORY}
VERSION 1.0
URI "FluentUI"
#修改qmltypes文件名称。默认fluentuiplugin.qmltypes使用默认名称有时候import FluentUI会爆红所以修改成plugins.qmltypes
TYPEINFO "plugins.qmltypes"
SOURCES ${sources_files} fluentui.rc
QML_FILES ${qml_files}
RESOURCES ${resource_files}
#如果是动态库,则使用插件目标作为其自己的支持目标来定义 QML 模块,在这种情况下,模块必须在运行时动态加载,并且不能由其他目标直接链接到
set(PLUGIN_TARGET_NAME ${PROJECT_NAME})
endif()
#如果是Windows平台则生成rc文件
set(FLUENTUI_VERSION_RC_PATH "")
if(WIN32)
set(FLUENTUI_VERSION_RC_PATH ${CMAKE_BINARY_DIR}/version_${PROJECT_NAME}.rc)
configure_file(
${CMAKE_SOURCE_DIR}/.cmake/version_dll.rc.in
${FLUENTUI_VERSION_RC_PATH}
)
endif()
qt_add_qml_module(${PROJECT_NAME}
PLUGIN_TARGET ${PLUGIN_TARGET_NAME}
OUTPUT_DIRECTORY ${FLUENTUI_QML_PLUGIN_DIRECTORY}
VERSION 1.0
URI "FluentUI"
#修改qmltypes文件名称。默认fluentuiplugin.qmltypes使用默认名称有时候import FluentUI会爆红所以修改成plugins.qmltypes
TYPEINFO "plugins.qmltypes"
SOURCES ${sources_files} ${FLUENTUI_VERSION_RC_PATH}
QML_FILES ${qml_files}
RESOURCES ${resource_files}
)
#链接库
target_link_libraries(${PROJECT_NAME} PUBLIC
Qt::CorePrivate
Qt::QuickPrivate
Qt::QmlPrivate
ZXing
)
#安装

137
src/Def.h
View File

@ -4,15 +4,138 @@
#include <QObject>
#include <QtQml/qqml.h>
namespace Fluent_DarkMode {
namespace FluThemeType {
Q_NAMESPACE
enum Fluent_DarkModeType {
System = 0x0,
Light = 0x1,
Dark = 0x2,
enum DarkMode {
System = 0x0000,
Light = 0x0001,
Dark = 0x0002,
};
Q_ENUM_NS(Fluent_DarkModeType)
QML_NAMED_ELEMENT(FluDarkMode)
Q_ENUM_NS(DarkMode)
QML_NAMED_ELEMENT(FluThemeType)
}
namespace FluTimelineType {
Q_NAMESPACE
enum Mode {
Left = 0x0000,
Right = 0x0001,
Alternate = 0x0002,
};
Q_ENUM_NS(Mode)
QML_NAMED_ELEMENT(FluTimelineType)
}
namespace FluPageType {
Q_NAMESPACE
enum LaunchMode {
Standard = 0x0000,
SingleTask = 0x0001,
SingleTop = 0x0002,
SingleInstance = 0x0004
};
Q_ENUM_NS(LaunchMode)
QML_NAMED_ELEMENT(FluPageType)
}
namespace FluWindowType {
Q_NAMESPACE
enum LaunchMode {
Standard = 0x0000,
SingleTask = 0x0001,
SingleInstance = 0x0002
};
Q_ENUM_NS(LaunchMode)
QML_NAMED_ELEMENT(FluWindowType)
}
namespace FluTreeViewType {
Q_NAMESPACE
enum SelectionMode {
None = 0x0000,
Single = 0x0001,
Multiple = 0x0002
};
Q_ENUM_NS(SelectionMode)
QML_NAMED_ELEMENT(FluTreeViewType)
}
namespace FluStatusViewType {
Q_NAMESPACE
enum StatusMode {
Loading = 0x0000,
Empty = 0x0001,
Error = 0x0002,
Success = 0x0004
};
Q_ENUM_NS(StatusMode)
QML_NAMED_ELEMENT(FluStatusViewType)
}
namespace FluContentDialogType {
Q_NAMESPACE
enum ButtonFlag {
NeutralButton = 0x0001,
NegativeButton = 0x0002,
PositiveButton = 0x0004
};
Q_ENUM_NS(ButtonFlag)
QML_NAMED_ELEMENT(FluContentDialogType)
}
namespace FluTimePickerType {
Q_NAMESPACE
enum HourFormat {
H = 0x0000,
HH = 0x0001
};
Q_ENUM_NS(HourFormat)
QML_NAMED_ELEMENT(FluTimePickerType)
}
namespace FluCalendarViewType {
Q_NAMESPACE
enum DisplayMode {
Month = 0x0000,
Year = 0x0001,
Decade = 0x0002
};
Q_ENUM_NS(DisplayMode)
QML_NAMED_ELEMENT(FluCalendarViewType)
}
namespace FluTabViewType {
Q_NAMESPACE
enum TabWidthBehavior {
Equal = 0x0000,
SizeToContent = 0x0001,
Compact = 0x0002
};
Q_ENUM_NS(TabWidthBehavior)
enum CloseButtonVisibility {
Nerver = 0x0000,
Always = 0x0001,
OnHover = 0x0002
};
Q_ENUM_NS(CloseButtonVisibility)
QML_NAMED_ELEMENT(FluTabViewType)
}
namespace FluNavigationViewType {
Q_NAMESPACE
enum DisplayMode {
Open = 0x0000,
Compact = 0x0001,
Minimal = 0x0002,
Auto = 0x0004
};
Q_ENUM_NS(DisplayMode)
enum PageMode {
Stack = 0x0000,
NoStack = 0x0001
};
Q_ENUM_NS(PageMode)
QML_NAMED_ELEMENT(FluNavigationViewType)
}
namespace Fluent_Awesome {

View File

@ -23,6 +23,7 @@ FluApp *FluApp::getInstance()
FluApp::FluApp(QObject *parent)
: QObject{parent}
{
httpInterceptor(nullptr);
}
FluApp::~FluApp(){
@ -48,14 +49,14 @@ void FluApp::navigate(const QString& route,const QJsonObject& argument,FluRegist
return;
}
QVariantMap properties;
properties.insert("route",route);
properties.insert("_route",route);
if(fluRegister){
properties.insert("pageRegister",QVariant::fromValue(fluRegister));
properties.insert("_pageRegister",QVariant::fromValue(fluRegister));
}
properties.insert("argument",argument);
QQuickWindow *view=nullptr;
for (auto& pair : wnds) {
QString r = pair->property("route").toString();
QString r = pair->property("_route").toString();
if(r == route){
view = pair;
break;

View File

@ -9,6 +9,7 @@
#include <QJsonObject>
#include <QQmlEngine>
#include "FluRegister.h"
#include "FluHttpInterceptor.h"
#include "stdafx.h"
/**
@ -27,6 +28,11 @@ class FluApp : public QObject
*/
Q_PROPERTY_AUTO(QJsonObject,routes);
/**
* @brief http拦截器
*/
Q_PROPERTY_AUTO(FluHttpInterceptor*,httpInterceptor);
QML_NAMED_ELEMENT(FluApp)
QML_SINGLETON
private:

305
src/FluHttp.cpp Normal file
View File

@ -0,0 +1,305 @@
#include "FluHttp.h"
#include <QThreadPool>
#include <QEventLoop>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QHttpMultiPart>
#include <QJsonDocument>
#include "MainThread.h"
#include "FluApp.h"
FluHttp::FluHttp(QObject *parent)
: QObject{parent}
{
retry(3);
timeout(15000);
}
FluHttp::~FluHttp(){
cancel();
}
void FluHttp::cancel(){
foreach (QPointer<QNetworkReply> item, _cache) {
if(item){
item->abort();
}
}
}
void FluHttp::handleReply(QNetworkReply* reply){
_cache.append(reply);
}
void FluHttp::post(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"post").toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QHttpMultiPart multiPart(QHttpMultiPart::FormDataType);
QString contentType = QString("multipart/form-data;boundary=%1").arg(multiPart.boundary());
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
for (const auto& each : data["params"].toMap().toStdMap())
{
const QString& key = each.first;
const QString& value = each.second.toString();
QString dispositionHeader = QString("form-data; name=\"%1\"").arg(key);
QHttpPart part;
part.setHeader(QNetworkRequest::ContentDispositionHeader, dispositionHeader);
part.setBody(value.toUtf8());
multiPart.append(part);
}
QEventLoop loop;
QNetworkReply* reply = manager.post(request,&multiPart);
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString);
}
}
}
onFinish(callable);
});
}
void FluHttp::postString(QString url,QJSValue callable,QString params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"postString").toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QString contentType = QString("text/plain;charset=utf-8");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop;
QNetworkReply* reply = manager.post(request,params.toUtf8());
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString);
}
}
}
onFinish(callable);
});
}
void FluHttp::postJson(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"postJson").toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QString contentType = QString("application/json;charset=utf-8");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop;
QNetworkReply* reply = manager.post(request,QJsonDocument::fromVariant(data["params"]).toJson());
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString);
}
}
}
onFinish(callable);
});
}
void FluHttp::get(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"get").toMap();
QThreadPool::globalInstance()->start([=](){
for (int i = 0; i < retry(); ++i) {
onStart(callable);
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
addQueryParam(&_url,data["params"].toMap());
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QEventLoop loop;
QNetworkReply* reply = manager.get(request);
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString);
}
}
}
onFinish(callable);
});
}
void FluHttp::download(QString url,QJSValue callable,QString filePath,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"download").toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
QNetworkAccessManager manager;
QUrl _url(url);
addQueryParam(&_url,data["params"].toMap());
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QSharedPointer<QFile> file(new QFile(filePath));
QIODevice::OpenMode mode = QIODevice::WriteOnly|QIODevice::Truncate;
if (!file->open(mode))
{
onError(callable,-1,QString("Url: %1 %2 Non-Writable").arg(request.url().toString(),file->fileName()));
onFinish(callable);
return;
}
QEventLoop loop;
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
QPointer<QNetworkReply> reply = manager.get(request);
_cache.append(reply);
connect(reply,&QNetworkReply::downloadProgress,this,[=](qint64 bytesReceived, qint64 bytesTotal){
onDownloadProgress(callable,bytesReceived,bytesTotal);
});
loop.exec();
if (reply->error() == QNetworkReply::NoError) {
file->write(reply->readAll());
onSuccess(callable,filePath);
}else{
onError(callable,reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(),reply->errorString());
}
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
onFinish(callable);
});
}
QVariant FluHttp::invokeIntercept(const QVariant& params,const QVariant& headers,const QString& method){
QMap<QString, QVariant> requet = {
{"params",params},
{"headers",headers},
{"method",method}
};
if(!FluApp::getInstance()->httpInterceptor()){
return requet;
}
QVariant target;
QMetaObject::invokeMethod(FluApp::getInstance()->httpInterceptor(), "onIntercept",Q_RETURN_ARG(QVariant,target),Q_ARG(QVariant, requet));
return target;
}
void FluHttp::addQueryParam(QUrl* url,const QMap<QString, QVariant>& params){
QMapIterator<QString, QVariant> iter(params);
QUrlQuery urlQuery(*url);
while (iter.hasNext())
{
iter.next();
urlQuery.addQueryItem(iter.key(), iter.value().toString());
}
url->setQuery(urlQuery);
}
void FluHttp::addHeaders(QNetworkRequest* request,const QMap<QString, QVariant>& headers){
QMapIterator<QString, QVariant> iter(headers);
while (iter.hasNext())
{
iter.next();
request->setRawHeader(iter.key().toUtf8(), iter.value().toString().toUtf8());
}
}
void FluHttp::onStart(const QJSValue& callable){
QJSValue onStart = callable.property("onStart");
MainThread::post([=](){onStart.call();});
}
void FluHttp::onFinish(const QJSValue& callable){
QJSValue onFinish = callable.property("onFinish");
MainThread::post([=](){onFinish.call();});
}
void FluHttp::onError(const QJSValue& callable,int status,QString errorString){
QJSValue onError = callable.property("onError");
QJSValueList args;
args<<status<<errorString;
MainThread::post([=](){onError.call(args);});
}
void FluHttp::onSuccess(const QJSValue& callable,QString result){
QJSValueList args;
args<<result;
QJSValue onSuccess = callable.property("onSuccess");
MainThread::post([=](){onSuccess.call(args);});
}
void FluHttp::onDownloadProgress(const QJSValue& callable,qint64 recv, qint64 total){
QJSValueList args;
args<<static_cast<double>(recv);
args<<static_cast<double>(total);
QJSValue onDownloadProgress = callable.property("onDownloadProgress");
MainThread::post([=](){onDownloadProgress.call(args);});
}

40
src/FluHttp.h Normal file
View File

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

View File

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

18
src/FluHttpInterceptor.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef FLUHTTPINTERCEPTOR_H
#define FLUHTTPINTERCEPTOR_H
#include <QObject>
#include <QtQml/qqml.h>
class FluHttpInterceptor : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(FluHttpInterceptor)
public:
explicit FluHttpInterceptor(QObject *parent = nullptr);
signals:
};
#endif // FLUHTTPINTERCEPTOR_H

53
src/FluQRCode.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "FluQRCode.h"
#include "BarcodeFormat.h"
#include "BitMatrix.h"
#include "MultiFormatWriter.h"
using namespace ZXing;
FluQRCode::FluQRCode(QQuickItem* parent) : QQuickPaintedItem(parent)
{
color(QColor(0,0,0,255));
size(100);
setWidth(_size);
setHeight(_size);
connect(this,&FluQRCode::textChanged,this,[=]{update();});
connect(this,&FluQRCode::colorChanged,this,[=]{update();});
connect(this,&FluQRCode::sizeChanged,this,[=]{
setWidth(_size);
setHeight(_size);
update();
});
}
void FluQRCode::paint(QPainter* painter)
{
if(_text.isEmpty()){
return;
}
if(_text.length()>1108){
return;
}
painter->save();
painter->eraseRect(boundingRect());
auto format = ZXing::BarcodeFormatFromString("QRCode");
auto writer = MultiFormatWriter(format);
writer.setMargin(0);
writer.setEncoding(ZXing::CharacterSet::UTF8);
auto matrix = writer.encode(_text.toUtf8().constData(), 0, 0);
auto bitmap = ToMatrix<uint8_t>(matrix);
auto image = QImage(bitmap.data(), bitmap.width(), bitmap.height(), bitmap.width(), QImage::Format::Format_Grayscale8).copy();
QImage rgbImage = image.convertToFormat(QImage::Format_ARGB32);
for (int y = 0; y < rgbImage.height(); ++y) {
for (int x = 0; x < rgbImage.width(); ++x) {
QRgb pixel = rgbImage.pixel(x, y);
if (qRed(pixel) == 0 && qGreen(pixel) == 0 && qBlue(pixel) == 0) {
rgbImage.setPixelColor(x, y, _color);
}
}
}
painter->drawImage(QRect(0, 0, static_cast<int>(width()), static_cast<int>(height())), rgbImage);
painter->restore();
}

22
src/FluQRCode.h Normal file
View File

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

View File

@ -32,7 +32,7 @@ FluTheme::FluTheme(QObject *parent)
primaryColor(FluColors::getInstance()->Blue());
nativeText(false);
enableAnimation(false);
darkMode(Fluent_DarkMode::Fluent_DarkModeType::Light);
darkMode(FluThemeType::DarkMode::Light);
_systemDark = systemDark();
qApp->installEventFilter(this);
}
@ -67,11 +67,11 @@ bool FluTheme::systemDark()
}
bool FluTheme::dark(){
if(_darkMode == Fluent_DarkMode::Fluent_DarkModeType::Dark){
if(_darkMode == FluThemeType::DarkMode::Dark){
return true;
}else if(_darkMode == Fluent_DarkMode::Fluent_DarkModeType::Light){
}else if(_darkMode == FluThemeType::DarkMode::Light){
return false;
}else if(_darkMode == Fluent_DarkMode::Fluent_DarkModeType::System){
}else if(_darkMode == FluThemeType::DarkMode::System){
return _systemDark;
}else{
return false;

View File

@ -3,6 +3,7 @@
#include <QClipboard>
#include <QUuid>
#include <QCursor>
#include <QTextDocument>
FluTools* FluTools::m_instance = nullptr;
@ -94,3 +95,13 @@ void FluTools::deleteItem(QObject *p){
p = nullptr;
}
}
QString FluTools::toLocalPath(const QUrl& url){
return url.toLocalFile();
}
QString FluTools::html2PlantText(const QString& html){
QTextDocument textDocument;
textDocument.setHtml(html);
return textDocument.toPlainText();
}

View File

@ -23,6 +23,37 @@ public:
return getInstance();
}
static FluTools *getInstance();
/**
* @brief qtMajor Qt Major版本
* @return
*/
Q_INVOKABLE int qtMajor();
/**
* @brief qtMajor Qt Minor版本
* @return
*/
Q_INVOKABLE int qtMinor();
/**
* @brief isMacos 是否是Macos系统
* @return
*/
Q_INVOKABLE bool isMacos();
/**
* @brief isLinux 是否是Linux系统
* @return
*/
Q_INVOKABLE bool isLinux();
/**
* @brief isWin 是否是Windows系统
* @return
*/
Q_INVOKABLE bool isWin();
/**
* @brief clipText 将字符串添加到剪切板
* @param text
@ -40,25 +71,45 @@ public:
* @param fileName
* @return
*/
Q_INVOKABLE QString readFile(const QString &fileName);
Q_INVOKABLE bool isMacos();
Q_INVOKABLE bool isLinux();
Q_INVOKABLE bool isWin();
Q_INVOKABLE QString readFile(const QString& fileName);
/**
* @brief setQuitOnLastWindowClosed 设置关闭最后一个窗口是否退出程序
* @param val
*/
Q_INVOKABLE void setQuitOnLastWindowClosed(bool val);
Q_INVOKABLE int qtMajor();
Q_INVOKABLE int qtMinor();
/**
* @brief setOverrideCursor 设置全局鼠标样式
* @param shape
*/
Q_INVOKABLE void setOverrideCursor(Qt::CursorShape shape);
/**
* @brief restoreOverrideCursor 还原全局鼠标样式
*/
Q_INVOKABLE void restoreOverrideCursor();
/**
* @brief html2PlantText 将html转换成纯文本
* @param html
*/
Q_INVOKABLE QString html2PlantText(const QString& html);
/**
* @brief toLocalPath 获取文件路径可以去掉windows系统下的file:///macos下的file://
* @param url
* @return 返回文件路径
*/
Q_INVOKABLE QString toLocalPath(const QUrl& url);
/**
* @brief deleteItem 销毁Item对象
* @param p
*/
Q_INVOKABLE void deleteItem(QObject *p);
};
#endif // FLUTOOLS_H

45
src/FluWatermark.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "FluWatermark.h"
FluWatermark::FluWatermark(QQuickItem* parent) : QQuickPaintedItem(parent)
{
gap(QPoint(100,100));
offset(QPoint(_gap.x()/2,_gap.y()/2));
rotate(22);
setZ(9999);
textColor(QColor(222,222,222,222));
textSize(16);
connect(this,&FluWatermark::textColorChanged,this,[=]{update();});
connect(this,&FluWatermark::gapChanged,this,[=]{update();});
connect(this,&FluWatermark::offsetChanged,this,[=]{update();});
connect(this,&FluWatermark::textChanged,this,[=]{update();});
connect(this,&FluWatermark::rotateChanged,this,[=]{update();});
connect(this,&FluWatermark::textSizeChanged,this,[=]{update();});
}
void FluWatermark::paint(QPainter* painter)
{
QFont font;
font.setPixelSize(_textSize);
painter->setFont(font);
painter->setPen(_textColor);
QFontMetricsF fontMetrics(font);
qreal fontWidth = fontMetrics.horizontalAdvance(_text);
qreal fontHeight = fontMetrics.height();
int stepX = fontWidth + _gap.x();
int stepY = fontHeight + _gap.y();
int rowCount = width() / stepX+1;
int colCount = height() / stepY+1;
for (int r = 0; r < rowCount; r++)
{
for (int c = 0; c < colCount; c++)
{
qreal centerX = stepX * r + _offset.x() + fontWidth / 2.0;
qreal centerY = stepY * c + _offset.y() + fontHeight / 2.0;
painter->save();
painter->translate(centerX, centerY);
painter->rotate(_rotate);
painter->drawText(QRectF(-fontWidth / 2.0, -fontHeight / 2.0, fontWidth, fontHeight), _text);
painter->restore();
}
}
}

25
src/FluWatermark.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef FLUWATERMARK_H
#define FLUWATERMARK_H
#include <QQuickItem>
#include <QQuickPaintedItem>
#include <QPainter>
#include "stdafx.h"
class FluWatermark : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY_AUTO(QString,text)
Q_PROPERTY_AUTO(QPoint,gap)
Q_PROPERTY_AUTO(QPoint,offset);
Q_PROPERTY_AUTO(QColor,textColor);
Q_PROPERTY_AUTO(int,rotate);
Q_PROPERTY_AUTO(int,textSize);
QML_NAMED_ELEMENT(FluWatermark)
public:
explicit FluWatermark(QQuickItem *parent = nullptr);
void paint(QPainter* painter) override;
};
#endif // FLUWATERMARK_H

39
src/MainThread.cpp Normal file
View File

@ -0,0 +1,39 @@
#include "MainThread.h"
#include <QGuiApplication>
#include <QMetaMethod>
std::shared_ptr<MainThread> MainThread::createShared(QObject* bindObject)
{
return std::shared_ptr<MainThread>(new MainThread(bindObject), [=](QObject* mainThread) {
mainThread->deleteLater();
});
}
MainThread::MainThread(QObject* bindObject)
: mBindObject(bindObject)
, mIgnoreNullObject(bindObject == nullptr)
{
qRegisterMetaType<std::function<void()>>("std::function<void()>");
auto mainUIThread = qApp->thread();
if (this->thread() != mainUIThread)
{
this->moveToThread(mainUIThread);
}
}
MainThread::~MainThread()
{
}
void MainThread::post(std::function<void()> func)
{
QMetaObject::invokeMethod(createShared().get(), "mainThreadSlot", Q_ARG(std::function<void()>, func));
}
void MainThread::mainThreadSlot(std::function<void()> func)
{
if ((mIgnoreNullObject || mBindObject) && func)
{
func();
}
}

24
src/MainThread.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef MAINTHREAD_H
#define MAINTHREAD_H
#include <QObject>
#include <QPointer>
#include <QDebug>
class MainThread : public QObject
{
Q_OBJECT
public:
static void post(std::function<void()> func);
~MainThread();
private:
static std::shared_ptr<MainThread> createShared(QObject* bindObject = nullptr);
private slots:
void mainThreadSlot(std::function<void()> func);
private:
MainThread(QObject* bindObject = nullptr);
QPointer<QObject> mBindObject;
bool mIgnoreNullObject{ false };
};
#endif // MAINTHREAD_H

View File

@ -1,40 +0,0 @@
#include <windows.h>
#define STR(x) #x
#define VER_JOIN(a,b,c,d) STR(a.b.c.d)
#define VER_JOIN_(x) VER_JOIN x
#define VER_STR VER_JOIN_((VERSION))
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION
PRODUCTVERSION VERSION
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "FluentUI for QML."
VALUE "CompanyName", "zhuzichu"
VALUE "FileDescription", "fluentui"
VALUE "FileVersion", VER_STR
VALUE "InternalName", ""
VALUE "LegalCopyright", "Copyright (C) 2023"
VALUE "OriginalFilename", ""
VALUE "ProductName", "fluentui"
VALUE "ProductVersion", VER_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@ -2,36 +2,39 @@ import QtQuick
import Qt5Compat.GraphicalEffects
import FluentUI
Item {
FluItem {
id: control
property alias color: rect.color
property alias acrylicOpacity: rect.opacity
property alias radius:bg.radius
property alias blurRadius: blur.radius
property int rectX: control.x
property int rectY: control.y
property var sourceItem: control.parent
FluRectangle{
id:bg
property color tintColor: Qt.rgba(1,1,1,1)
property real tintOpacity: 0.65
property real luminosity: 0.01
property real noiseOpacity : 0.066
property alias target : effect_source.sourceItem
property int blurRadius: 32
property rect targetRect : Qt.rect(control.x, control.y, control.width, control.height)
ShaderEffectSource {
id: effect_source
anchors.fill: parent
radius: [8,8,8,8]
ShaderEffectSource {
id: effect_source
anchors.fill: parent
sourceItem: control.sourceItem
sourceRect: Qt.rect(rectX, rectY, control.width, control.height)
Rectangle {
id: rect
anchors.fill: parent
color: "white"
opacity: 0.5
}
}
FastBlur {
id:blur
radius: 50
anchors.fill: effect_source
source: effect_source
}
visible: false
sourceRect: control.targetRect
}
FastBlur {
id:fast_blur
anchors.fill: parent
source: effect_source
radius: control.blurRadius
}
Rectangle{
anchors.fill: parent
color: Qt.rgba(255, 255, 255, luminosity)
}
Rectangle{
anchors.fill: parent
color: Qt.rgba(tintColor.r, tintColor.g, tintColor.b, tintOpacity)
}
Image{
anchors.fill: parent
source: "../Image/noise.png"
fillMode: Image.Tile
opacity: control.noiseOpacity
}
}

View File

@ -39,9 +39,9 @@ Rectangle{
}
property var darkClickListener: function(){
if(FluTheme.dark){
FluTheme.darkMode = FluDarkMode.Light
FluTheme.darkMode = FluThemeType.Light
}else{
FluTheme.darkMode = FluDarkMode.Dark
FluTheme.darkMode = FluThemeType.Dark
}
}
id:root

View File

@ -10,7 +10,6 @@ FluTextBox{
signal itemClicked(var data)
signal handleClicked
id:control
width: 300
Component.onCompleted: {
loadData()
}

View File

@ -3,12 +3,7 @@ import QtQuick.Controls
import FluentUI
Item {
enum DisplayMode {
Month,
Year,
Decade
}
property int displayMode: FluCalendarView.Month
property int displayMode: FluCalendarViewType.Month
property var date: new Date()
property var currentDate : new Date()
property var toDay: new Date()
@ -39,7 +34,7 @@ Item {
width: 70
onClicked:{
control.date = date
displayMode = FluCalendarView.Year
displayMode = FluCalendarViewType.Year
updateYear(date)
}
background: Item{
@ -98,7 +93,7 @@ Item {
width: 70
onClicked:{
control.date = date
displayMode = FluCalendarView.Month
displayMode = FluCalendarViewType.Month
updateMouth(date)
}
background: Item{
@ -248,13 +243,13 @@ Item {
left: parent.left
leftMargin: 14
}
disabled: displayMode === FluCalendarView.Decade
disabled: displayMode === FluCalendarViewType.Decade
onClicked:{
if(displayMode === FluCalendarView.Month){
displayMode = FluCalendarView.Year
if(displayMode === FluCalendarViewType.Month){
displayMode = FluCalendarViewType.Year
updateYear(date)
}else if(displayMode === FluCalendarView.Year){
displayMode = FluCalendarView.Decade
}else if(displayMode === FluCalendarViewType.Year){
displayMode = FluCalendarViewType.Decade
updateDecade(date)
}
}
@ -271,7 +266,7 @@ Item {
onClicked: {
var year = date.getFullYear()
var month = date.getMonth()
if(displayMode === FluCalendarView.Month){
if(displayMode === FluCalendarViewType.Month){
var lastMonthYear = year;
var lastMonthMonth = month - 1
if (month === 0) {
@ -280,10 +275,10 @@ Item {
}
date = new Date(lastMonthYear,lastMonthMonth,1)
updateMouth(date)
}else if(displayMode === FluCalendarView.Year){
}else if(displayMode === FluCalendarViewType.Year){
date = new Date(year-1,month,1)
updateYear(date)
}else if(displayMode === FluCalendarView.Decade){
}else if(displayMode === FluCalendarViewType.Decade){
date = new Date(Math.floor(year / 10) * 10-10,month,1)
updateDecade(date)
}
@ -301,7 +296,7 @@ Item {
onClicked: {
var year = date.getFullYear()
var month = date.getMonth()
if(displayMode === FluCalendarView.Month){
if(displayMode === FluCalendarViewType.Month){
var nextMonthYear = year
var nextMonth = month + 1
if (month === 11) {
@ -310,10 +305,10 @@ Item {
}
date = new Date(nextMonthYear,nextMonth,1)
updateMouth(date)
}else if(displayMode === FluCalendarView.Year){
}else if(displayMode === FluCalendarViewType.Year){
date = new Date(year+1,month,1)
updateYear(date)
}else if(displayMode === FluCalendarView.Decade){
}else if(displayMode === FluCalendarViewType.Decade){
date = new Date(Math.floor(year / 10) * 10+10,month,1)
updateDecade(date)
}
@ -334,8 +329,8 @@ Item {
GridView{
model: list_model
anchors.fill: parent
cellHeight: displayMode === FluCalendarView.Month ? 40 : 70
cellWidth: displayMode === FluCalendarView.Month ? 40 : 70
cellHeight: displayMode === FluCalendarViewType.Month ? 40 : 70
cellWidth: displayMode === FluCalendarViewType.Month ? 40 : 70
clip: true
boundsBehavior:Flickable.StopAtBounds
delegate: Loader{

View File

@ -2,102 +2,170 @@ import QtQuick
import QtQuick.Controls
import FluentUI
Item {
property bool flagXChanged: true
property int radius : 5
FluItem {
property int loopTime: 2000
property var model
property Component delegate
property bool showIndicator: true
property int indicatorGravity : Qt.AlignBottom | Qt.AlignHCenter
property int indicatorMarginLeft: 0
property int indicatorMarginRight: 0
property int indicatorMarginTop: 0
property int indicatorMarginBottom: 20
property int indicatorSpacing: 10
property alias indicatorAnchors: layout_indicator.anchors
property Component indicatorDelegate : com_indicator
id:control
width: 400
height: 300
ListModel{
id:content_model
}
FluRectangle{
anchors.fill: parent
radius: [control.radius,control.radius,control.radius,control.radius]
FluShadow{
radius:control.radius
QtObject{
id:d
property bool flagXChanged: true
function setData(data){
if(!data){
return
}
content_model.clear()
content_model.append(data[data.length-1])
content_model.append(data)
content_model.append(data[0])
list_view.highlightMoveDuration = 0
list_view.currentIndex = 1
list_view.highlightMoveDuration = 250
timer_run.restart()
}
ListView{
id:list_view
anchors.fill: parent
snapMode: ListView.SnapOneItem
clip: true
boundsBehavior: ListView.StopAtBounds
model:content_model
maximumFlickVelocity: 4 * (list_view.orientation === Qt.Horizontal ? width : height)
preferredHighlightBegin: 0
preferredHighlightEnd: 0
highlightMoveDuration: 0
orientation : ListView.Horizontal
delegate: Item{
width: ListView.view.width
height: ListView.view.height
property int displayIndex: {
if(index === 0)
return content_model.count-3
if(index === content_model.count-1)
return 0
return index-1
}
Image {
anchors.fill: parent
source: model.url
asynchronous: true
fillMode:Image.PreserveAspectCrop
}
}
ListView{
id:list_view
anchors.fill: parent
snapMode: ListView.SnapOneItem
clip: true
boundsBehavior: ListView.StopAtBounds
model:content_model
maximumFlickVelocity: 4 * (list_view.orientation === Qt.Horizontal ? width : height)
preferredHighlightBegin: 0
preferredHighlightEnd: 0
highlightMoveDuration: 0
Component.onCompleted: {
d.setData(control.model)
}
Connections{
target: control
function onModelChanged(){
d.setData(control.model)
}
onMovementEnded:{
currentIndex = list_view.contentX/list_view.width
if(currentIndex === 0){
currentIndex = list_view.count-2
}else if(currentIndex === list_view.count-1){
currentIndex = 1
}
flagXChanged = false
timer_run.start()
}
orientation : ListView.Horizontal
delegate: Item{
id:item_control
width: ListView.view.width
height: ListView.view.height
property int displayIndex: {
if(index === 0)
return content_model.count-3
if(index === content_model.count-1)
return 0
return index-1
}
onMovementStarted: {
flagXChanged = true
timer_run.stop()
}
onContentXChanged: {
if(flagXChanged){
var maxX = Math.min(list_view.width*(currentIndex+1),list_view.count*list_view.width)
var minY = Math.max(0,(list_view.width*(currentIndex-1)))
if(contentX>=maxX){
contentX = maxX
}
if(contentX<=minY){
contentX = minY
Loader{
property int displayIndex : item_control.displayIndex
property var model: list_view.model.get(index)
anchors.fill: parent
sourceComponent: {
if(model){
return control.delegate
}
return undefined
}
}
}
onMovementEnded:{
currentIndex = list_view.contentX/list_view.width
if(currentIndex === 0){
currentIndex = list_view.count-2
}else if(currentIndex === list_view.count-1){
currentIndex = 1
}
d.flagXChanged = false
timer_run.restart()
}
onMovementStarted: {
d.flagXChanged = true
timer_run.stop()
}
onContentXChanged: {
if(d.flagXChanged){
var maxX = Math.min(list_view.width*(currentIndex+1),list_view.count*list_view.width)
var minY = Math.max(0,(list_view.width*(currentIndex-1)))
if(contentX>=maxX){
contentX = maxX
}
if(contentX<=minY){
contentX = minY
}
}
}
}
Component{
id:com_indicator
Rectangle{
width: 8
height: 8
radius: 4
FluShadow{
radius: 4
}
scale: checked ? 1.2 : 1
color: checked ? FluTheme.primaryColor.dark : Qt.rgba(1,1,1,0.7)
border.width: mouse_item.containsMouse ? 1 : 0
border.color: FluTheme.primaryColor.dark
MouseArea{
id:mouse_item
hoverEnabled: true
anchors.fill: parent
onClicked: {
changedIndex(realIndex)
}
}
}
}
Row{
spacing: 10
id:layout_indicator
spacing: control.indicatorSpacing
anchors{
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 20
horizontalCenter:(indicatorGravity & Qt.AlignHCenter) ? parent.horizontalCenter : undefined
verticalCenter: (indicatorGravity & Qt.AlignVCenter) ? parent.verticalCenter : undefined
bottom: (indicatorGravity & Qt.AlignBottom) ? parent.bottom : undefined
top: (indicatorGravity & Qt.AlignTop) ? parent.top : undefined
left: (indicatorGravity & Qt.AlignLeft) ? parent.left : undefined
right: (indicatorGravity & Qt.AlignRight) ? parent.right : undefined
bottomMargin: control.indicatorMarginBottom
leftMargin: control.indicatorMarginBottom
rightMargin: control.indicatorMarginBottom
topMargin: control.indicatorMarginBottom
}
visible: showIndicator
Repeater{
id:repeater_indicator
model: list_view.count
Rectangle{
width: 8
height: 8
radius: 4
visible: {
if(index===0 || index===list_view.count-1)
return false
return true
Loader{
property int displayIndex: {
if(index === 0)
return list_view.count-3
if(index === list_view.count-1)
return 0
return index-1
}
property int realIndex: index
property bool checked: list_view.currentIndex === index
sourceComponent: {
if(index===0 || index===list_view.count-1)
return undefined
return control.indicatorDelegate
}
border.width: 1
border.color: FluColors.Grey100
color: list_view.currentIndex === index ? FluTheme.primaryColor.dark : Qt.rgba(1,1,1,0.5)
}
}
}
@ -121,12 +189,13 @@ Item {
timer_anim.start()
}
}
function setData(data){
content_model.clear()
content_model.append(data[data.length-1])
content_model.append(data)
content_model.append(data[0])
list_view.currentIndex = 1
function changedIndex(index){
d.flagXChanged = true
timer_run.stop()
list_view.currentIndex = index
d.flagXChanged = false
timer_run.restart()
}
}

View File

@ -0,0 +1,122 @@
import QtQuick 2.13
import "./../JS/Chart.js" as Chart
Canvas {
id: control
property var jsChart: undefined
property string chartType
property var chartData
property var chartOptions
property double chartAnimationProgress: 0.1
property int animationEasingType: Easing.InOutExpo
property double animationDuration: 500
property var memorizedContext
property var memorizedData
property var memorizedOptions
property alias animationRunning: chartAnimator.running
signal animationFinished()
function animateToNewData()
{
chartAnimationProgress = 0.1;
jsChart.update();
chartAnimator.restart();
}
MouseArea {
id: event
anchors.fill: control
hoverEnabled: true
enabled: true
property var handler: undefined
property QtObject mouseEvent: QtObject {
property int left: 0
property int top: 0
property int x: 0
property int y: 0
property int clientX: 0
property int clientY: 0
property string type: ""
property var target
}
function submitEvent(mouse, type) {
mouseEvent.type = type
mouseEvent.clientX = mouse ? mouse.x : 0;
mouseEvent.clientY = mouse ? mouse.y : 0;
mouseEvent.x = mouse ? mouse.x : 0;
mouseEvent.y = mouse ? mouse.y : 0;
mouseEvent.left = 0;
mouseEvent.top = 0;
mouseEvent.target = control;
if(handler) {
handler(mouseEvent);
}
control.requestPaint();
}
onClicked:(mouse)=> {
submitEvent(mouse, "click");
}
onPositionChanged:(mouse)=> {
submitEvent(mouse, "mousemove");
}
onExited: {
submitEvent(undefined, "mouseout");
}
onEntered: {
submitEvent(undefined, "mouseenter");
}
onPressed:(mouse)=> {
submitEvent(mouse, "mousedown");
}
onReleased:(mouse)=> {
submitEvent(mouse, "mouseup");
}
}
PropertyAnimation {
id: chartAnimator
target: control
property: "chartAnimationProgress"
alwaysRunToEnd: true
to: 1
duration: control.animationDuration
easing.type: control.animationEasingType
onFinished: {
control.animationFinished();
}
}
onChartAnimationProgressChanged: {
control.requestPaint();
}
onPaint: {
if(control.getContext('2d') !== null && memorizedContext !== control.getContext('2d') || memorizedData !== control.chartData || memorizedOptions !== control.chartOptions) {
var ctx = control.getContext('2d');
jsChart = Chart.build(ctx, {
type: control.chartType,
data: control.chartData,
options: control.chartOptions
});
memorizedData = control.chartData ;
memorizedContext = control.getContext('2d');
memorizedOptions = control.chartOptions;
control.jsChart.bindEvents(function(newHandler) {event.handler = newHandler;});
chartAnimator.start();
}
jsChart.draw(chartAnimationProgress);
}
onWidthChanged: {
if(jsChart) {
jsChart.resize();
}
}
onHeightChanged: {
if(jsChart) {
jsChart.resize();
}
}
}

View File

@ -11,6 +11,7 @@ Button{
height: 36
implicitWidth: width
implicitHeight: height
property alias colorValue: container.colorValue
background:
Rectangle{
id:layout_color
@ -75,4 +76,7 @@ Button{
popup.open()
}
}
function setColor(color){
container.setColor(color)
}
}

View File

@ -15,23 +15,13 @@ FluPopup {
signal neutralClicked
signal negativeClicked
signal positiveClicked
enum ButtonFlag{
NegativeButton=1
,NeutralButton=2
,PositiveButton=4
}
property int buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton
property var minWidth: {
if(Window.window==null)
return 400
return Math.min(Window.window.width,400)
}
property int buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
focus: true
implicitWidth: 400
implicitHeight: text_title.height + text_message.height + layout_actions.height
Rectangle {
id:layout_content
anchors.fill: parent
implicitWidth:minWidth
implicitHeight: text_title.height + text_message.height + layout_actions.height
color: 'transparent'
radius:5
FluText{
@ -67,7 +57,7 @@ FluPopup {
id:layout_actions
height: 68
radius: 5
color: FluTheme.dark ? Qt.rgba(32/255,32/255,32/255, blurBackground ? blurOpacity - 0.4 : 1) : Qt.rgba(243/255,243/255,243/255,blurBackground ? blurOpacity - 0.4 : 1)
color: FluTheme.dark ? Qt.rgba(32/255,32/255,32/255,1) : Qt.rgba(243/255,243/255,243/255,1)
anchors{
top:text_message.bottom
left: parent.left
@ -85,11 +75,11 @@ FluPopup {
id:neutral_btn
Layout.fillWidth: true
Layout.fillHeight: true
visible: popup.buttonFlags&FluContentDialog.NeutralButton
visible: popup.buttonFlags&FluContentDialogType.NeutralButton
text: neutralText
onClicked: {
popup.close()
timer_delay.targetFlags = FluContentDialog.NeutralButton
timer_delay.targetFlags = FluContentDialogType.NeutralButton
timer_delay.restart()
}
}
@ -97,11 +87,11 @@ FluPopup {
id:negative_btn
Layout.fillWidth: true
Layout.fillHeight: true
visible: popup.buttonFlags&FluContentDialog.NegativeButton
visible: popup.buttonFlags&FluContentDialogType.NegativeButton
text: negativeText
onClicked: {
popup.close()
timer_delay.targetFlags = FluContentDialog.NegativeButton
timer_delay.targetFlags = FluContentDialogType.NegativeButton
timer_delay.restart()
}
}
@ -109,11 +99,11 @@ FluPopup {
id:positive_btn
Layout.fillWidth: true
Layout.fillHeight: true
visible: popup.buttonFlags&FluContentDialog.PositiveButton
visible: popup.buttonFlags&FluContentDialogType.PositiveButton
text: positiveText
onClicked: {
popup.close()
timer_delay.targetFlags = FluContentDialog.PositiveButton
timer_delay.targetFlags = FluContentDialogType.PositiveButton
timer_delay.restart()
}
}
@ -125,13 +115,13 @@ FluPopup {
id:timer_delay
interval: popup.delayTime
onTriggered: {
if(targetFlags === FluContentDialog.NegativeButton){
if(targetFlags === FluContentDialogType.NegativeButton){
negativeClicked()
}
if(targetFlags === FluContentDialog.NeutralButton){
if(targetFlags === FluContentDialogType.NeutralButton){
neutralClicked()
}
if(targetFlags === FluContentDialog.PositiveButton){
if(targetFlags === FluContentDialogType.PositiveButton){
positiveClicked()
}
}

View File

@ -11,6 +11,17 @@ FluPage {
property int topPadding: 0
property int rightPadding: 10
property int bottomPadding: 10
property alias color: status_view.color
property alias statusMode: status_view.statusMode
property alias loadingText: status_view.loadingText
property alias emptyText:status_view.emptyText
property alias errorText:status_view.errorText
property alias errorButtonText:status_view.errorButtonText
property alias loadingItem :status_view.loadingItem
property alias emptyItem : status_view.emptyItem
property alias errorItem :status_view.errorItem
signal errorClicked
id:control
FluText{
id:text_title
@ -26,9 +37,11 @@ FluPage {
rightMargin: control.rightPadding
}
}
Item{
clip: true
id:container
FluStatusView{
id:status_view
color: "#00000000"
statusMode: FluStatusViewType.Success
onErrorClicked: control.errorClicked()
anchors{
left: parent.left
right: parent.right
@ -38,5 +51,10 @@ FluPage {
rightMargin: control.rightPadding
bottomMargin: control.bottomPadding
}
Item{
clip: true
id:container
anchors.fill: parent
}
}
}

View File

@ -3,5 +3,16 @@ import QtQuick.Window
import FluentUI
Rectangle {
color: FluTheme.dark ? Qt.rgba(80/255,80/255,80/255,1) : Qt.rgba(210/255,210/255,210/255,1)
property real spacing
property alias separatorHeight:separator.height
id:root
color:Qt.rgba(0,0,0,0)
height: spacing*2+separator.height
Rectangle{
id:separator
color: FluTheme.dark ? Qt.rgba(80/255,80/255,80/255,1) : Qt.rgba(210/255,210/255,210/255,1)
width:parent.width
anchors.centerIn: parent
}
}

View File

@ -70,7 +70,6 @@ Button {
contentItem: FluText {
id:title
text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: control.textColor
}

View File

@ -1,27 +0,0 @@
import QtQuick
import QtQuick.Templates as T
import FluentUI
T.HorizontalHeaderView {
id: control
implicitWidth: syncView ? syncView.width : 0
implicitHeight: Math.max(1, contentHeight)
delegate: Rectangle {
readonly property real cellPadding: 8
implicitWidth: text.implicitWidth + (cellPadding * 2)
implicitHeight: Math.max(control.height, text.implicitHeight + (cellPadding * 2))
color:FluTheme.dark ? Qt.rgba(50/255,50/255,50/255,1) : Qt.rgba(247/255,247/255,247/255,1)
border.color: FluTheme.dark ? "#252525" : "#e4e4e4"
FluText {
id: text
text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole]
: model[control.textRole])
: modelData
width: parent.width
height: parent.height
font.bold: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}

View File

@ -12,7 +12,6 @@ TextArea{
property color placeholderFocusColor: FluTheme.dark ? Qt.rgba(152/255,152/255,152/255,1) : Qt.rgba(141/255,141/255,141/255,1)
property color placeholderDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
id:control
width: 300
enabled: !disabled
color: {
if(!enabled){
@ -37,7 +36,10 @@ TextArea{
return placeholderNormalColor
}
selectByMouse: true
background: FluTextBoxBackground{ inputItem: control }
background: FluTextBoxBackground{
inputItem: control
implicitWidth: 240
}
Keys.onEnterPressed: (event)=> d.handleCommit(event)
Keys.onReturnPressed:(event)=> d.handleCommit(event)
QtObject{

View File

@ -6,39 +6,29 @@ import QtQuick.Layouts
import FluentUI
Item {
enum DisplayMode {
Open = 0,
Compact = 1,
Minimal = 2,
Auto = 3
}
enum PageMode {
Stack = 0,
NoStack = 1
}
property url logo
property string title: ""
property FluObject items
property FluObject footerItems
property int displayMode: FluNavigationView.Auto
property int displayMode: FluNavigationViewType.Auto
property Component autoSuggestBox
property Component actionItem
property int topPadding: 0
property int navWidth: 300
property int pageMode: FluNavigationView.Stack
property int pageMode: FluNavigationViewType.Stack
signal logoClicked
id:control
QtObject{
Item{
id:d
property bool animDisabled:false
property var stackItems: []
property int displayMode: control.displayMode
property bool enableNavigationPanel: false
property bool isCompact: d.displayMode === FluNavigationView.Compact
property bool isMinimal: d.displayMode === FluNavigationView.Minimal
property bool isCompactAndPanel: d.displayMode === FluNavigationView.Compact && d.enableNavigationPanel
property bool isCompactAndNotPanel:d.displayMode === FluNavigationView.Compact && !d.enableNavigationPanel
property bool isMinimalAndPanel: d.displayMode === FluNavigationView.Minimal && d.enableNavigationPanel
property bool isCompact: d.displayMode === FluNavigationViewType.Compact
property bool isMinimal: d.displayMode === FluNavigationViewType.Minimal
property bool isCompactAndPanel: d.displayMode === FluNavigationViewType.Compact && d.enableNavigationPanel
property bool isCompactAndNotPanel:d.displayMode === FluNavigationViewType.Compact && !d.enableNavigationPanel
property bool isMinimalAndPanel: d.displayMode === FluNavigationViewType.Minimal && d.enableNavigationPanel
onIsCompactAndNotPanelChanged: {
collapseAll()
}
@ -76,18 +66,22 @@ Item {
}
return data
}
function refreshWindow(){
Window.window.height = Window.window.height-1
Window.window.height = Window.window.height+1
}
}
Component.onCompleted: {
d.displayMode = Qt.binding(function(){
if(control.displayMode !==FluNavigationView.Auto){
if(control.displayMode !==FluNavigationViewType.Auto){
return control.displayMode
}
if(control.width<=700){
return FluNavigationView.Minimal
return FluNavigationViewType.Minimal
}else if(control.width<=900){
return FluNavigationView.Compact
return FluNavigationViewType.Compact
}else{
return FluNavigationView.Open
return FluNavigationViewType.Open
}
})
timer_anim_delay.restart()
@ -102,10 +96,10 @@ Item {
Connections{
target: d
function onDisplayModeChanged(){
if(d.displayMode === FluNavigationView.Compact){
if(d.displayMode === FluNavigationViewType.Compact){
collapseAll()
}
if(d.displayMode === FluNavigationView.Minimal){
if(d.displayMode === FluNavigationViewType.Minimal){
d.enableNavigationPanel = false
}
}
@ -120,11 +114,12 @@ Item {
id:com_panel_item_separatorr
FluDivider{
width: layout_list.width
height: {
spacing: model.spacing
separatorHeight: {
if(model.parent){
return model.parent.isExpand ? 1 : 0
return model.parent.isExpand ? model.size : 0
}
return 1
return model.size
}
}
}
@ -533,11 +528,11 @@ Item {
layout_footer.currentIndex = item._idx-(nav_list.count-layout_footer.count)
}
nav_list.currentIndex = item._idx
if(pageMode === FluNavigationView.Stack){
if(pageMode === FluNavigationViewType.Stack){
var nav_stack = loader_content.item.navStack()
var nav_stack2 = loader_content.item.navStack2()
nav_stack.pop()
if(nav_stack.currentItem.launchMode === FluPage.SingleInstance){
if(nav_stack.currentItem.launchMode === FluPageType.SingleInstance){
var url = nav_stack.currentItem.url
var pageIndex = -1
for(var i=0;i<nav_stack2.children.length;i++){
@ -551,7 +546,7 @@ Item {
nav_stack2.currentIndex = pageIndex
}
}
}else if(pageMode === FluNavigationView.NoStack){
}else if(pageMode === FluNavigationViewType.NoStack){
loader_content.setSource(item._ext.url,item._ext.argument)
}
}
@ -646,7 +641,7 @@ Item {
id:nav_stack2
anchors.fill: nav_stack
clip: true
visible: nav_stack.currentItem?.launchMode === FluPage.SingleInstance
visible: nav_stack.currentItem?.launchMode === FluPageType.SingleInstance
}
function navStack(){
return nav_stack
@ -707,7 +702,7 @@ Item {
border.color: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,230/255,234/255,1)
border.width: d.isMinimal || d.isCompactAndPanel ? 1 : 0
color: {
if(d.isMinimal){
if(d.isMinimal || d.enableNavigationPanel){
return FluTheme.dark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(243/255,243/255,243/255,1)
}
return "transparent"
@ -718,6 +713,11 @@ Item {
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
onRunningChanged: {
if(!running){
d.refreshWindow()
}
}
}
}
Behavior on x {
@ -725,27 +725,18 @@ Item {
NumberAnimation{
duration: 167
easing.type: Easing.OutCubic
onRunningChanged: {
if(!running){
d.refreshWindow()
}
}
}
}
visible: {
if(d.displayMode !== FluNavigationView.Minimal)
if(d.displayMode !== FluNavigationViewType.Minimal)
return true
return d.isMinimalAndPanel ? true : false
}
FluAcrylic {
sourceItem:loader_content
anchors.fill: layout_list
color: {
if(d.isMinimalAndPanel || d.isCompactAndPanel){
return FluTheme.dark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(243/255,243/255,243/255,1)
}
return "transparent"
}
visible: d.isMinimalAndPanel || d.isCompactAndPanel
rectX: d.isCompactAndPanel ? (layout_list.x - 50) : layout_list.x
rectY: layout_list.y - 60
acrylicOpacity:0.9
}
Item{
id:layout_header
width: layout_list.width
@ -1005,7 +996,7 @@ Item {
Component{
id:com_placeholder
Item{
property int launchMode: FluPage.SingleInstance
property int launchMode: FluPageType.SingleInstance
property string url
}
}
@ -1031,12 +1022,12 @@ Item {
return nav_list.currentIndex
}
function getCurrentUrl(){
if(pageMode === FluNavigationView.Stack){
if(pageMode === FluNavigationViewType.Stack){
var nav_stack = loader_content.item.navStack()
if(nav_stack.currentItem){
return nav_stack.currentItem.url
}
}else if(pageMode === FluNavigationView.NoStack){
}else if(pageMode === FluNavigationViewType.NoStack){
return loader_content.source.toString()
}
return undefined
@ -1051,19 +1042,19 @@ Item {
if(page){
switch(page.launchMode)
{
case FluPage.SingleTask:
case FluPageType.SingleTask:
while(nav_stack.currentItem !== page)
{
nav_stack.pop()
d.stackItems = d.stackItems.slice(0, -1)
}
return
case FluPage.SingleTop:
case FluPageType.SingleTop:
if (nav_stack.currentItem.url === url){
return
}
break
case FluPage.Standard:
case FluPageType.Standard:
default:
}
}
@ -1083,7 +1074,7 @@ Item {
var comp = Qt.createComponent(url)
if (comp.status === Component.Ready) {
var obj = comp.createObject(nav_stack,options)
if(obj.launchMode === FluPage.SingleInstance){
if(obj.launchMode === FluPageType.SingleInstance){
nav_stack.push(com_placeholder,options)
nav_stack2.children.push(obj)
nav_stack2.currentIndex = nav_stack2.count - 1
@ -1105,9 +1096,9 @@ Item {
obj._ext = {url:url,argument:argument}
d.stackItems = d.stackItems.concat(obj)
}
if(pageMode === FluNavigationView.Stack){
if(pageMode === FluNavigationViewType.Stack){
stackPush()
}else if(pageMode === FluNavigationView.NoStack){
}else if(pageMode === FluNavigationViewType.NoStack){
noStackPush()
}
}
@ -1133,4 +1124,7 @@ Item {
function navButton(){
return btn_nav
}
function logoButton(){
return image_logo
}
}

View File

@ -5,13 +5,7 @@ import QtQuick.Window
import FluentUI
Item {
enum LaunchMode{
Standard = 0,
SingleTask = 1,
SingleTop = 2,
SingleInstance = 3
}
property int launchMode: FluPage.SingleTop
property int launchMode: FluPageType.SingleTop
property bool animDisabled: false
property string url : ""
id: control

View File

@ -6,4 +6,6 @@ QtObject {
readonly property string key : FluTools.uuid()
property int _idx
property var parent
property real spacing
property int size:1
}

View File

@ -13,7 +13,6 @@ TextField{
property color placeholderFocusColor: FluTheme.dark ? Qt.rgba(152/255,152/255,152/255,1) : Qt.rgba(141/255,141/255,141/255,1)
property color placeholderDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
id:control
width: 300
enabled: !disabled
color: {
if(!enabled){
@ -41,6 +40,7 @@ TextField{
rightPadding: icon_end.visible ? 50 : 30
background: FluTextBoxBackground{
inputItem: control
implicitWidth: 240
FluIcon{
id:icon_end
iconSource: control.iconSource

View File

@ -10,11 +10,6 @@ Popup {
modal:true
anchors.centerIn: Overlay.overlay
closePolicy: Popup.CloseOnEscape
property alias blurSource: blur.sourceItem
property bool blurBackground: true
property alias blurOpacity: blur.acrylicOpacity
property alias blurRectX: blur.rectX
property alias blurRectY: blur.rectY
enter: Transition {
NumberAnimation {
properties: "scale"
@ -45,12 +40,8 @@ Popup {
to:0
}
}
background: FluAcrylic{
id:blur
color: FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(249/255,249/255,249/255,1)
rectX: popup.x
rectY: popup.y
acrylicOpacity:blurBackground ? 0.8 : 1
background: FluRectangle{
radius: [5,5,5,5]
color: FluTheme.dark ? Qt.rgba(43/255,43/255,43/255,1) : Qt.rgba(1,1,1,1)
}
}

View File

@ -2,56 +2,48 @@ import QtQuick
import QtQuick.Controls
import FluentUI
Item{
property real progress: 0.5
property bool indeterminate: true
ProgressBar{
property real strokeWidth: 6
property bool progressVisible: false
id: control
width: 150
height: 5
FluRectangle {
shadow: false
radius: [3,3,3,3]
anchors.fill: parent
color: FluTheme.dark ? Qt.rgba(99/255,99/255,99/255,1) : Qt.rgba(214/255,214/255,214/255,1)
Component.onCompleted: {
if(indeterminate){
bar.x = -control.width*0.5
behavior.enabled = true
bar.x = control.width
}else{
bar.x = 0
}
}
Rectangle{
id:bar
radius: 3
width: control.width*progress
height: control.height
color:FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
Behavior on x{
id:behavior
enabled: false
NumberAnimation{
duration: 1000
onRunningChanged: {
if(!running){
behavior.enabled = false
bar.x = -control.width*0.5
behavior.enabled = true
bar.x = control.width
}
}
property color color: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color backgroundColor : FluTheme.dark ? Qt.rgba(99/255,99/255,99/255,1) : Qt.rgba(214/255,214/255,214/255,1)
id:control
indeterminate : true
QtObject{
id:d
property real _radius: strokeWidth/2
}
background: Rectangle {
implicitWidth: 150
implicitHeight: control.strokeWidth
color: control.backgroundColor
radius: d._radius
}
contentItem: FluItem {
clip: true
radius: [d._radius,d._radius,d._radius,d._radius]
Rectangle {
id:rect_progress
width: {
if(control.indeterminate){
return 0.5 * parent.width
}
return control.visualPosition * parent.width
}
height: parent.height
radius: d._radius
color: control.color
PropertyAnimation on x {
running: control.indeterminate && control.visible
from: -rect_progress.width
to:control.width+rect_progress.width
loops: Animation.Infinite
duration: 888
}
}
}
FluText{
text:(control.progress * 100).toFixed(0) + "%"
font.pixelSize: 10
text:(control.visualPosition * 100).toFixed(0) + "%"
visible: {
if(control.indeterminate){
return false

View File

@ -1,32 +1,31 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Shapes
import FluentUI
Rectangle {
property real linWidth : 5
property real progress: 0.25
property bool indeterminate: true
property color primaryColor : FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
ProgressBar{
property real strokeWidth: 6
property bool progressVisible: false
id: control
width: 44
height: 44
radius: width/2
border.width: linWidth
color: "#00000000"
border.color: FluTheme.dark ? Qt.rgba(99/255,99/255,99/255,1) : Qt.rgba(214/255,214/255,214/255,1)
onProgressChanged: {
canvas.requestPaint()
}
Component.onCompleted: {
if(indeterminate){
behavior.enabled = true
control.rotation = 360
}
property color color: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
property color backgroundColor : FluTheme.dark ? Qt.rgba(99/255,99/255,99/255,1) : Qt.rgba(214/255,214/255,214/255,1)
id:control
indeterminate : true
clip: true
background: Rectangle {
implicitWidth: 56
implicitHeight: 56
radius: control.width/2
color:"transparent"
border.color: control.backgroundColor
border.width: control.strokeWidth
}
QtObject{
id:d
property real _radius: control.radius-control.linWidth/2
property real _radius: control.width/2-control.strokeWidth/2
property real _progress: control.indeterminate ? 0.3 : control.visualPosition
on_ProgressChanged: {
canvas.requestPaint()
}
}
Connections{
target: FluTheme
@ -34,43 +33,35 @@ Rectangle {
canvas.requestPaint()
}
}
Behavior on rotation{
id:behavior
enabled: false
NumberAnimation{
duration: 999
onRunningChanged: {
if(!running){
behavior.enabled = false
control.rotation = 0
behavior.enabled = true
control.rotation = 360
}
contentItem: Item {
RotationAnimation on rotation {
running: control.indeterminate && control.visible
from: 0
to:360
loops: Animation.Infinite
duration: 888
}
Canvas {
id:canvas
anchors.fill: parent
antialiasing: true
renderTarget: Canvas.Image
onPaint: {
var ctx = canvas.getContext("2d")
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.save()
ctx.lineWidth = control.strokeWidth
ctx.strokeStyle = control.color
ctx.beginPath()
ctx.arc(width/2, height/2, d._radius ,-0.5 * Math.PI,-0.5 * Math.PI + d._progress * 2 * Math.PI)
ctx.stroke()
ctx.closePath()
ctx.restore()
}
}
}
Canvas {
id:canvas
anchors.fill: parent
antialiasing: true
renderTarget: Canvas.Image
onPaint: {
var ctx = canvas.getContext("2d")
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.save()
ctx.lineWidth = linWidth
ctx.strokeStyle = primaryColor
ctx.fillStyle = primaryColor
ctx.beginPath()
ctx.arc(width/2, height/2, d._radius ,-0.5 * Math.PI,-0.5 * Math.PI + progress * 2 * Math.PI)
ctx.stroke()
ctx.closePath()
ctx.restore()
}
}
FluText{
text:(control.progress * 100).toFixed(0) + "%"
font.pixelSize: 10
text:(control.visualPosition * 100).toFixed(0) + "%"
visible: {
if(control.indeterminate){
return false
@ -80,3 +71,4 @@ Rectangle {
anchors.centerIn: parent
}
}

View File

@ -5,7 +5,7 @@ import FluentUI
Item{
property var radius:[0,0,0,0]
property color color : "#FFFFFF"
property color color : FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
property bool shadow: true
default property alias contentItem: container.data
id:control
@ -40,27 +40,27 @@ Item{
anchors.fill: parent
visible: false
onPaint: {
var ctx = getContext("2d");
var x = 0;
var y = 0;
var w = control.width;
var h = control.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
var ctx = getContext("2d")
var x = 0
var y = 0
var w = control.width
var h = control.height
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.save()
ctx.beginPath();
ctx.moveTo(x + radius[0], y);
ctx.lineTo(x + w - radius[1], y);
ctx.arcTo(x + w, y, x + w, y + radius[1], radius[1]);
ctx.lineTo(x + w, y + h - radius[2]);
ctx.arcTo(x + w, y + h, x + w - radius[2], y + h, radius[2]);
ctx.lineTo(x + radius[3], y + h);
ctx.arcTo(x, y + h, x, y + h - radius[3], radius[3]);
ctx.lineTo(x, y + radius[0]);
ctx.arcTo(x, y, x + radius[0], y, radius[0]);
ctx.closePath();
ctx.fillStyle = control.color;
ctx.fill();
ctx.restore();
ctx.moveTo(x + radius[0], y)
ctx.lineTo(x + w - radius[1], y)
ctx.arcTo(x + w, y, x + w, y + radius[1], radius[1])
ctx.lineTo(x + w, y + h - radius[2])
ctx.arcTo(x + w, y + h, x + w - radius[2], y + h, radius[2])
ctx.lineTo(x + radius[3], y + h)
ctx.arcTo(x, y + h, x, y + h - radius[3], radius[3])
ctx.lineTo(x, y + radius[0])
ctx.arcTo(x, y, x + radius[0], y, radius[0])
ctx.closePath()
ctx.fillStyle = "#000000"
ctx.fill()
ctx.restore()
}
}
OpacityMask {

View File

@ -21,11 +21,11 @@ FluStatusView {
asynchronous: true
onStatusChanged: {
if(status === Loader.Error){
control.statusMode = FluStatusView.Error
control.statusMode = FluStatusViewType.Error
}else if(status === Loader.Loading){
control.statusMode = FluStatusView.Loading
control.statusMode = FluStatusViewType.Loading
}else{
control.statusMode = FluStatusView.Success
control.statusMode = FluStatusViewType.Success
}
}
}

View File

@ -12,6 +12,16 @@ FluPage {
property int topPadding: 0
property int rightPadding: 10
property int bottomPadding: 10
property alias color: status_view.color
property alias statusMode: status_view.statusMode
property alias loadingText: status_view.loadingText
property alias emptyText:status_view.emptyText
property alias errorText:status_view.errorText
property alias errorButtonText:status_view.errorButtonText
property alias loadingItem :status_view.loadingItem
property alias emptyItem : status_view.emptyItem
property alias errorItem :status_view.errorItem
signal errorClicked
id:control
FluText{
id:text_title
@ -28,9 +38,11 @@ FluPage {
rightMargin: control.rightPadding
}
}
Flickable{
id:flickview
clip: true
FluStatusView{
id:status_view
color: "#00000000"
statusMode: FluStatusViewType.Success
onErrorClicked: control.errorClicked()
anchors{
left: parent.left
right: parent.right
@ -38,29 +50,30 @@ FluPage {
bottom: parent.bottom
bottomMargin: control.bottomPadding
}
contentWidth: parent.width
contentHeight: container.height
ScrollBar.vertical: FluScrollBar {
anchors.right: flickview.right
anchors.rightMargin: 2
}
boundsBehavior: Flickable.StopAtBounds
anchors{
top: text_title.bottom
bottom: parent.bottom
}
ColumnLayout{
id:container
spacing: control.spacing
Flickable{
id:flickview
clip: true
anchors{
left: parent.left
right: parent.right
top: parent.top
leftMargin: control.leftPadding
rightMargin: control.rightPadding
anchors.fill: parent
contentWidth: parent.width
contentHeight: container.height
ScrollBar.vertical: FluScrollBar {
anchors.right: flickview.right
anchors.rightMargin: 2
}
boundsBehavior: Flickable.StopAtBounds
ColumnLayout{
id:container
spacing: control.spacing
clip: true
anchors{
left: parent.left
right: parent.right
top: parent.top
leftMargin: control.leftPadding
rightMargin: control.rightPadding
}
width: parent.width
}
width: parent.width
}
}
}

View File

@ -1,160 +1,157 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Controls.impl
import QtQuick.Templates as T
import FluentUI
Rectangle{
readonly property string displayText : d._displayText
T.SpinBox {
id: control
property bool disabled: false
property int from: 0
property int to: 99
property var validator: IntValidator {
property color normalColor: FluTheme.dark ? Qt.rgba(56/255,56/255,56/255,1) : Qt.rgba(232/255,232/255,232/255,1)
property color hoverColor: FluTheme.dark ? Qt.rgba(64/255,64/255,64/255,1) : Qt.rgba(224/255,224/255,224/255,1)
property color pressedColor: FluTheme.dark ? Qt.rgba(72/255,72/255,72/255,1) : Qt.rgba(216/255,216/255,216/255,1)
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentItem.implicitWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding,
up.implicitIndicatorHeight, down.implicitIndicatorHeight)
leftPadding: padding + (control.mirrored ? (up.indicator ? up.indicator.width : 0) : (down.indicator ? down.indicator.width : 0))
rightPadding: padding + (control.mirrored ? (down.indicator ? down.indicator.width : 0) : (up.indicator ? up.indicator.width : 0))
enabled: !disabled
validator: IntValidator {
locale: control.locale.name
bottom: Math.min(control.from, control.to)
top: Math.max(control.from, control.to)
}
id:control
implicitWidth: 200
implicitHeight: 34
radius: 4
color: FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
border.width: 1
border.color: FluTheme.dark ? Qt.rgba(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
QtObject{
id:d
property string _displayText: "0"
}
Component{
id:com_edit
FluTextBox{
rightPadding: 80
closeRightMargin: 55
disabled: control.disabled
validator: control.validator
text: d._displayText
Component.onCompleted: {
forceActiveFocus()
}
onCommit: {
var number = Number(text)
if(number>=control.from && number<=control.to){
d._displayText = String(number)
}
edit_loader.sourceComponent = null
}
onActiveFocusChanged: {
if(!activeFocus){
edit_loader.sourceComponent = null
}
}
}
}
FluTextBox{
id:text_number
anchors.fill: parent
readOnly: true
rightPadding: 80
disabled: control.disabled
contentItem: TextInput {
property color normalColor: FluTheme.dark ? Qt.rgba(255/255,255/255,255/255,1) : Qt.rgba(27/255,27/255,27/255,1)
property color disableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
property color placeholderNormalColor: FluTheme.dark ? Qt.rgba(210/255,210/255,210/255,1) : Qt.rgba(96/255,96/255,96/255,1)
property color placeholderFocusColor: FluTheme.dark ? Qt.rgba(152/255,152/255,152/255,1) : Qt.rgba(141/255,141/255,141/255,1)
property color placeholderDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
z: 2
text: control.displayText
MouseArea{
clip: width < implicitWidth
padding: 6
font: control.font
color: {
if(!enabled){
return disableColor
}
return normalColor
}
selectionColor: Qt.alpha(FluTheme.primaryColor.lightest,0.6)
selectedTextColor: color
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
readOnly: !control.editable
validator: control.validator
inputMethodHints: control.inputMethodHints
Rectangle{
width: parent.width
height: contentItem.activeFocus ? 3 : 1
anchors.bottom: parent.bottom
visible: contentItem.enabled
color: {
if(FluTheme.dark){
contentItem.activeFocus ? FluTheme.primaryColor.lighter : Qt.rgba(166/255,166/255,166/255,1)
}else{
return contentItem.activeFocus ? FluTheme.primaryColor.dark : Qt.rgba(183/255,183/255,183/255,1)
}
}
Behavior on height{
enabled: FluTheme.enableAnimation
NumberAnimation{
duration: 83
easing.type: Easing.OutCubic
}
}
}
}
up.indicator: FluItem {
x: control.mirrored ? 0 : control.width - width
height: control.height
implicitWidth: 32
implicitHeight: 32
radius: [0,4,4,0]
Rectangle{
anchors.fill: parent
onClicked: {
edit_loader.sourceComponent = com_edit
color: {
if(control.up.pressed){
return control.pressedColor
}
if(control.up.hovered){
return control.hoverColor
}
return control.normalColor
}
}
Rectangle {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: parent.width / 3
height: 2
color: enabled ? FluTheme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1) : FluColors.Grey90
}
Rectangle {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: 2
height: parent.width / 3
color: enabled ? FluTheme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1) : FluColors.Grey90
}
}
Loader{
id:edit_loader
anchors.fill: parent
}
FluIconButton{
id:btn_up
width: 20
height: 20
iconSize: 16
disabled: {
if(control.disabled===true){
return true
}
return Number(control.displayText) === control.to
}
iconSource: FluentIcons.ChevronUp
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 30
}
onClicked: {
d._displayText = String(Math.min(Number(d._displayText)+1,control.to))
}
MouseArea{
down.indicator: FluItem {
x: control.mirrored ? parent.width - width : 0
height: control.height
implicitWidth: 32
implicitHeight: 32
radius: [4,0,0,4]
Rectangle{
anchors.fill: parent
onReleased: {
timer.stop()
}
TapHandler{
onTapped: {
btn_up.clicked()
}
onCanceled: {
timer.stop()
}
onLongPressed: {
timer.isUp = true
timer.start()
}
color: {
if(control.down.pressed){
return control.pressedColor
}
if(control.down.hovered){
return control.hoverColor
}
return normalColor
}
}
Rectangle {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: parent.width / 3
height: 2
color: enabled ? FluTheme.dark ? Qt.rgba(1,1,1,1) : Qt.rgba(0,0,0,1) : FluColors.Grey90
}
}
FluIconButton{
id:btn_down
iconSource: FluentIcons.ChevronDown
width: 20
height: 20
disabled: {
if(control.disabled === true){
return true
background: Rectangle {
implicitWidth: 136
radius: 4
border.width: 1
border.color: {
if(contentItem.disabled){
return FluTheme.dark ? Qt.rgba(73/255,73/255,73/255,1) : Qt.rgba(237/255,237/255,237/255,1)
}
return Number(control.displayText) === control.from
return FluTheme.dark ? Qt.rgba(76/255,76/255,76/255,1) : Qt.rgba(240/255,240/255,240/255,1)
}
iconSize: 16
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: 5
}
onClicked: {
d._displayText = String(Math.max(Number(d._displayText)-1,control.from))
}
MouseArea{
anchors.fill: parent
onReleased: {
timer.stop()
color: {
if(contentItem.disabled){
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
}
TapHandler{
onTapped: {
btn_down.clicked()
}
onCanceled: {
timer.stop()
}
onLongPressed: {
timer.isUp = false
timer.start()
}
if(contentItem.activeFocus){
return FluTheme.dark ? Qt.rgba(36/255,36/255,36/255,1) : Qt.rgba(1,1,1,1)
}
}
}
Timer{
id:timer
property bool isUp : true
interval: 50
repeat: true
onTriggered: {
if(isUp){
btn_up.clicked()
}else{
btn_down.clicked()
if(contentItem.hovered){
return FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
}
return FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(1,1,1,1)
}
}
}

View File

@ -5,14 +5,8 @@ import FluentUI
Item{
id:control
enum StatusMode {
Loading,
Empty,
Error,
Success
}
default property alias content: container.data
property int statusMode: FluStatusView.Loading
property int statusMode: FluStatusViewType.Loading
property string loadingText:"正在加载..."
property string emptyText: "空空如也"
property string errorText: "页面出错了.."
@ -26,20 +20,20 @@ Item{
Item{
id:container
anchors.fill: parent
visible: statusMode===FluStatusView.Success
visible: statusMode===FluStatusViewType.Success
}
Loader{
id:loader
anchors.fill: parent
visible: statusMode!==FluStatusView.Success
visible: statusMode!==FluStatusViewType.Success
sourceComponent: {
if(statusMode === FluStatusView.Loading){
if(statusMode === FluStatusViewType.Loading){
return loadingItem
}
if(statusMode === FluStatusView.Empty){
if(statusMode === FluStatusViewType.Empty){
return emptyItem
}
if(statusMode === FluStatusView.Error){
if(statusMode === FluStatusViewType.Error){
return errorItem
}
return undefined
@ -108,15 +102,15 @@ Item{
}
}
function showSuccessView(){
statusMode = FluStatusView.Success
statusMode = FluStatusViewType.Success
}
function showLoadingView(){
statusMode = FluStatusView.Loading
statusMode = FluStatusViewType.Loading
}
function showEmptyView(){
statusMode = FluStatusView.Empty
statusMode = FluStatusViewType.Empty
}
function showErrorView(){
statusMode = FluStatusView.Error
statusMode = FluStatusViewType.Error
}
}

View File

@ -4,18 +4,8 @@ import QtQuick.Layouts
import FluentUI
Item {
enum TabWidthBehavior {
Equal,
SizeToContent,
Compact
}
enum CloseButtonVisibility{
Nerver,
Always,
OnHover
}
property int tabWidthBehavior : FluTabView.Equal
property int closeButtonVisibility : FluTabView.Always
property int tabWidthBehavior : FluTabViewType.Equal
property int closeButtonVisibility : FluTabViewType.Always
property int itemWidth: 146
property bool addButtonVisibility: true
signal newPressed
@ -91,13 +81,13 @@ Item {
property real timestamp: new Date().getTime()
height: tab_nav.height
width: {
if(tabWidthBehavior === FluTabView.Equal){
if(tabWidthBehavior === FluTabViewType.Equal){
return Math.max(Math.min(d.maxEqualWidth,tab_nav.width/tab_nav.count),41 + item_btn_close.width)
}
if(tabWidthBehavior === FluTabView.SizeToContent){
if(tabWidthBehavior === FluTabViewType.SizeToContent){
return itemWidth
}
if(tabWidthBehavior === FluTabView.Compact){
if(tabWidthBehavior === FluTabViewType.Compact){
return item_mouse_hove.containsMouse || item_btn_close.hovered || tab_nav.currentIndex === index ? itemWidth : 41 + item_btn_close.width
}
return Math.max(Math.min(d.maxEqualWidth,tab_nav.width/tab_nav.count),41 + item_btn_close.width)
@ -233,13 +223,13 @@ Item {
text: model.text
Layout.leftMargin: 10
visible: {
if(tabWidthBehavior === FluTabView.Equal){
if(tabWidthBehavior === FluTabViewType.Equal){
return true
}
if(tabWidthBehavior === FluTabView.SizeToContent){
if(tabWidthBehavior === FluTabViewType.SizeToContent){
return true
}
if(tabWidthBehavior === FluTabView.Compact){
if(tabWidthBehavior === FluTabViewType.Compact){
return item_mouse_hove.containsMouse || item_btn_close.hovered || tab_nav.currentIndex === index
}
return false
@ -256,9 +246,9 @@ Item {
width: visible ? 24 : 0
height: 24
visible: {
if(closeButtonVisibility === FluTabView.Nerver)
if(closeButtonVisibility === FluTabViewType.Nerver)
return false
if(closeButtonVisibility === FluTabView.OnHover)
if(closeButtonVisibility === FluTabViewType.OnHover)
return item_mouse_hove.containsMouse || item_btn_close.hovered
return true
}

View File

@ -322,7 +322,7 @@ Rectangle {
property bool canceled: false
readonly property var obj : columnSource[column]
implicitWidth: column_text.implicitWidth + (cellPadding * 2)
implicitHeight: Math.max(header_horizontal.height, column_text.implicitHeight + (cellPadding * 2))
implicitHeight: Math.max(36, column_text.implicitHeight + (cellPadding * 2))
color:{
d.selectionFlag
if(column_item_control_mouse.pressed){
@ -436,7 +436,7 @@ Rectangle {
id:item_control
readonly property real cellPadding: 8
property bool canceled: false
implicitWidth: Math.max(header_vertical.width, row_text.implicitWidth + (cellPadding * 2))
implicitWidth: Math.max(30, row_text.implicitWidth + (cellPadding * 2))
implicitHeight: row_text.implicitHeight + (cellPadding * 2)
color: {
d.selectionFlag

View File

@ -12,9 +12,9 @@ TextField{
property color placeholderNormalColor: FluTheme.dark ? Qt.rgba(210/255,210/255,210/255,1) : Qt.rgba(96/255,96/255,96/255,1)
property color placeholderFocusColor: FluTheme.dark ? Qt.rgba(152/255,152/255,152/255,1) : Qt.rgba(141/255,141/255,141/255,1)
property color placeholderDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
property int closeRightMargin: icon_end.visible ? 25 : 5
property int iconRightMargin: icon_end.visible ? 25 : 5
property bool cleanEnabled: true
id:control
width: 300
padding: 8
leftPadding: padding+2
enabled: !disabled
@ -41,6 +41,7 @@ TextField{
rightPadding: icon_end.visible ? 50 : 30
background: FluTextBoxBackground{
inputItem: control
implicitWidth: 240
FluIcon{
id:icon_end
iconSource: control.iconSource
@ -74,6 +75,9 @@ TextField{
width: 20
height: 20
visible: {
if(control.cleanEnabled === false){
return false
}
if(control.readOnly)
return false
return control.text !== ""
@ -81,7 +85,7 @@ TextField{
anchors{
verticalCenter: parent.verticalCenter
right: parent.right
rightMargin: closeRightMargin
rightMargin: control.iconRightMargin
}
contentDescription:"清空"
onClicked:{

View File

@ -12,7 +12,7 @@ Rectangle{
if(inputItem.disabled){
return FluTheme.dark ? Qt.rgba(59/255,59/255,59/255,1) : Qt.rgba(252/255,252/255,252/255,1)
}
if(inputItem.focus){
if(inputItem.activeFocus){
return FluTheme.dark ? Qt.rgba(36/255,36/255,36/255,1) : Qt.rgba(1,1,1,1)
}
if(inputItem.hovered){
@ -36,14 +36,14 @@ Rectangle{
}
Rectangle{
width: parent.width
height: inputItem.focus ? 3 : 1
height: inputItem.activeFocus ? 3 : 1
anchors.bottom: parent.bottom
visible: !inputItem.disabled
color: {
if(FluTheme.dark){
inputItem.focus ? FluTheme.primaryColor.lighter : Qt.rgba(166/255,166/255,166/255,1)
inputItem.activeFocus ? FluTheme.primaryColor.lighter : Qt.rgba(166/255,166/255,166/255,1)
}else{
return inputItem.focus ? FluTheme.primaryColor.dark : Qt.rgba(183/255,183/255,183/255,1)
return inputItem.activeFocus ? FluTheme.primaryColor.dark : Qt.rgba(183/255,183/255,183/255,1)
}
}
Behavior on height{

View File

@ -5,15 +5,11 @@ import QtQuick.Window
import FluentUI
Rectangle {
enum HourFormat {
H,
HH
}
property color dividerColor: FluTheme.dark ? Qt.rgba(77/255,77/255,77/255,1) : Qt.rgba(239/255,239/255,239/255,1)
property color hoverColor: FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
property color normalColor: FluTheme.dark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(254/255,254/255,254/255,1)
property int hourFormat: FluTimePicker.H
property int isH: hourFormat === FluTimePicker.H
property int hourFormat: FluTimePickerType.H
property int isH: hourFormat === FluTimePickerType.H
property var current
id:control
color: {
@ -326,7 +322,7 @@ Rectangle {
const period = text_ampm.text
const date = new Date()
var hours24 = parseInt(hours);
if(control.hourFormat === FluTimePicker.H){
if(control.hourFormat === FluTimePickerType.H){
if (hours === "12") {
hours24 = (period === "上午") ? 0 : 12;
} else {

View File

@ -0,0 +1,298 @@
import QtQuick
import QtQuick.Controls
import FluentUI
Item{
property int mode: FluTimelineType.Left
property alias model: repeater.model
property color lineColor: Qt.rgba(240/255,240/255,240/255,1)
id:control
implicitWidth: 380
implicitHeight: layout_column.height
QtObject{
id:d
property bool isLeft: control.mode === FluTimelineType.Left
property bool isRight: control.mode === FluTimelineType.Right
property bool isAlternate: control.mode === FluTimelineType.Alternate
property bool hasLable: {
for(var i=0;i<model.count;i++){
var lable = model.get(i).lable
if(lable !== undefined && undefined !== ""){
return true
}
}
return false
}
property string stateName : {
if(hasLable){
return "Center"
}
if(isRight){
return "Right"
}
if(isAlternate){
return "Center"
}
return "Left"
}
}
Rectangle{
id:rect_line
color: control.lineColor
height: parent.height
width: 2
state: d.stateName
states: [
State {
name: "Left"
AnchorChanges {
target: rect_line
anchors.left: control.left
}
PropertyChanges {
target: rect_line
anchors.leftMargin: 7
}
},
State {
name: "Right"
AnchorChanges {
target: rect_line
anchors.right: control.right
}
PropertyChanges {
target: rect_line
anchors.rightMargin: 7
}
},
State {
name: "Center"
AnchorChanges {
target: rect_line
anchors.horizontalCenter: control.horizontalCenter
}
}
]
}
Component{
id:com_dot
Rectangle{
width: 16
height: 16
radius: 8
border.width: 4
color:FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
border.color: FluTheme.dark ? FluTheme.primaryColor.lighter : FluTheme.primaryColor.dark
}
}
Component{
id:com_lable
FluText{
wrapMode: Text.WrapAnywhere
horizontalAlignment: textAlignment
text: modelData.lable
color: FluTheme.primaryColor.dark
}
}
Component{
id:com_text
FluText{
wrapMode: Text.WrapAnywhere
horizontalAlignment: textAlignment
text: modelData.text
}
}
Column{
id:layout_column
spacing: 30
width: control.width
Repeater{
id:repeater
Item{
id:item_layout
width: layout_column.width
height: loader_text.height
Loader{
id:item_loader
state: d.stateName
states: [
State {
name: "Left"
AnchorChanges {
target: item_loader
anchors.left: item_layout.left
}
},
State {
name: "Right"
AnchorChanges {
target: item_loader
anchors.right: item_layout.right
}
},
State {
name: "Center"
AnchorChanges {
target: item_loader
anchors.horizontalCenter: item_layout.horizontalCenter
}
}
]
sourceComponent: {
if(model.dot)
return model.dot()
return com_dot
}
}
Loader{
property var modelData: control.model.get(index)
property int textAlignment: state === "Right" ? Qt.AlignRight : Qt.AlignLeft
id:loader_lable
sourceComponent: {
if(!modelData){
return undefined
}
var lableDelegate = model.lableDelegate
if(lableDelegate instanceof Function && lableDelegate() instanceof Component){
return lableDelegate()
}
return com_lable
}
state: {
if(d.isRight){
return "Left"
}
if(d.isAlternate){
if(index%2===0){
return "Right"
}else{
return "Left"
}
}
return "Right"
}
states: [
State {
name: "Left"
AnchorChanges {
target: loader_lable
anchors.left: item_loader.right
anchors.right: item_layout.right
}
PropertyChanges {
target: loader_lable
anchors.leftMargin: 14
anchors.rightMargin: 14
}
},
State {
name: "Right"
AnchorChanges {
target: loader_lable
anchors.right: item_loader.left
anchors.left: item_layout.left
}
PropertyChanges {
target: loader_lable
anchors.leftMargin: 14
anchors.rightMargin: 14
}
},
State {
name: "Center"
AnchorChanges {
target: loader_lable
anchors.right: item_loader.left
anchors.left: item_layout.left
}
PropertyChanges {
target: loader_lable
anchors.leftMargin: 14
anchors.rightMargin: 14
}
}
]
}
Loader{
id:loader_text
property var modelData: control.model.get(index)
property int textAlignment: state === "Right" ? Qt.AlignRight : Qt.AlignLeft
state: {
if(d.isRight){
return "Right"
}
if(d.isAlternate){
if(index%2===0){
return "Left"
}else{
return "Right"
}
}
return "Left"
}
sourceComponent: {
if(!modelData){
return undefined
}
var textDelegate = model.textDelegate
if(textDelegate instanceof Function && textDelegate() instanceof Component){
return textDelegate()
}
return com_text
}
states: [
State {
name: "Left"
AnchorChanges {
target: loader_text
anchors.left: item_loader.right
anchors.right: item_layout.right
}
PropertyChanges {
target: loader_text
anchors.leftMargin: 14
anchors.rightMargin: 14
}
},
State {
name: "Right"
AnchorChanges {
target: loader_text
anchors.right: item_loader.left
anchors.left: item_layout.left
}
PropertyChanges {
target: loader_text
anchors.leftMargin: 14
anchors.rightMargin: 14
}
},
State {
name: "Center"
AnchorChanges {
target: loader_text
anchors.right: item_loader.left
anchors.left: item_layout.left
}
PropertyChanges {
target: loader_text
anchors.leftMargin: 14
anchors.rightMargin: 14
}
}
]
}
}
}
}
}

View File

@ -0,0 +1,178 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Shapes
import QtQuick.Window
import FluentUI
Popup{
property var steps : []
property int targetMargins: 5
property Component nextButton: com_next_button
property Component prevButton: com_prev_button
property int index : 0
id:control
padding: 0
anchors.centerIn: Overlay.overlay
width: d.window?.width
height: d.window?.height
background: Item{}
contentItem: Item{}
onVisibleChanged: {
if(visible){
control.index = 0
}
}
onIndexChanged: {
canvas.requestPaint()
}
Component{
id:com_next_button
FluFilledButton{
text: isEnd ? "结束导览" :"下一步"
onClicked: {
if(isEnd){
control.close()
}else{
control.index = control.index + 1
}
}
}
}
Component{
id:com_prev_button
FluButton{
text: "上一步"
onClicked: {
control.index = control.index - 1
}
}
}
Item{
id:d
property var window: Window.window
property var pos:Qt.point(0,0)
property var step : steps[index]
property var target : step.target()
}
Connections{
target: d.window
function onWidthChanged(){
canvas.requestPaint()
}
function onHeightChanged(){
canvas.requestPaint()
}
}
Canvas{
id:canvas
anchors.fill: parent
onPaint: {
d.pos = d.target.mapToGlobal(0,0)
d.pos = Qt.point(d.pos.x-d.window.x,d.pos.y-d.window.y)
var ctx = canvas.getContext("2d")
ctx.clearRect(0, 0, canvasSize.width, canvasSize.height)
ctx.save()
ctx.fillStyle = "#88000000"
ctx.fillRect(0, 0, canvasSize.width, canvasSize.height)
ctx.globalCompositeOperation = 'destination-out'
ctx.fillStyle = 'black'
drawRoundedRect(Qt.rect(d.pos.x-control.targetMargins,d.pos.y-control.targetMargins, d.target.width+control.targetMargins*2, d.target.height+control.targetMargins*2),2,ctx)
ctx.restore()
}
function drawRoundedRect(rect, r, ctx) {
var ptA = Qt.point(rect.x + r, rect.y)
var ptB = Qt.point(rect.x + rect.width, rect.y)
var ptC = Qt.point(rect.x + rect.width, rect.y + rect.height)
var ptD = Qt.point(rect.x, rect.y + rect.height)
var ptE = Qt.point(rect.x, rect.y)
ctx.beginPath()
ctx.moveTo(ptA.x, ptA.y)
ctx.arcTo(ptB.x, ptB.y, ptC.x, ptC.y, r)
ctx.arcTo(ptC.x, ptC.y, ptD.x, ptD.y, r)
ctx.arcTo(ptD.x, ptD.y, ptE.x, ptE.y, r)
ctx.arcTo(ptE.x, ptE.y, ptA.x, ptA.y, r)
ctx.fill()
ctx.closePath()
}
}
FluArea{
id:layout_panne
radius: 5
width: 500
height: 88 + text_desc.height
color: FluTheme.dark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(251/255,251/255,253/255,1)
x: Math.min(Math.max(0,d.pos.x+d.target.width/2-width/2),d.window?.width-width)
y: d.pos.y+d.target.height+control.targetMargins + 15
border.width: 0
FluText{
text: d.step.title
font: FluTextStyle.BodyStrong
elide: Text.ElideRight
anchors{
top: parent.top
left: parent.left
topMargin: 15
leftMargin: 15
right: parent.right
rightMargin: 32
}
}
FluText{
id:text_desc
font: FluTextStyle.Body
wrapMode: Text.WrapAnywhere
maximumLineCount: 4
elide: Text.ElideRight
text: d.step.description
anchors{
top: parent.top
left: parent.left
right: parent.right
rightMargin: 15
topMargin: 42
leftMargin: 15
}
}
Loader{
id:loader_next
property bool isEnd: control.index === steps.length-1
sourceComponent: com_next_button
anchors{
top:text_desc.bottom
topMargin: 10
right: parent.right
rightMargin: 15
}
}
Loader{
id:loader_prev
visible: control.index !== 0
sourceComponent: com_prev_button
anchors{
right:loader_next.left
top: loader_next.top
rightMargin: 14
}
}
FluIconButton{
anchors{
right: parent.right
top: parent.top
margins: 10
}
width: 26
height: 26
iconSize: 12
iconSource : FluentIcons.ChromeClose
onClicked: {
control.close()
}
}
}
FluIcon{
iconSource: FluentIcons.FlickDown
color: layout_panne.color
x: d.pos.x+d.target.width/2-10
y: d.pos.y+d.target.height
}
}

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