Compare commits

..

81 Commits

Author SHA1 Message Date
997101f349 update 2023-08-19 01:40:54 +08:00
2b96f06fe0 update 2023-08-18 23:05:15 +08:00
6ea873f75c update 2023-08-18 22:58:26 +08:00
efd21a5f55 Merge pull request #248 from FeJQ/FluNavigationView-pr
补交漏掉的FluEditableText.qml
2023-08-18 20:13:00 +08:00
a7ff28466d 补提交漏掉的文件 2023-08-18 19:48:48 +08:00
64728632c7 update 2023-08-18 19:17:45 +08:00
a54a99bbe1 1.增加FluEditableText
2.Nav组件增加切换编辑功能
3.example的导航栏增加右键重命名演示
2023-08-18 18:18:46 +08:00
cb8b128598 fix #246 2023-08-18 16:55:49 +08:00
153090637b update 2023-08-18 12:45:16 +08:00
c0efd71d31 update 2023-08-18 11:46:02 +08:00
2ead64e9b5 update 2023-08-18 09:59:38 +08:00
fc99c19479 update 2023-08-18 09:30:56 +08:00
1e9bd0fd05 update 2023-08-17 23:03:00 +08:00
b47f2d12ad update 2023-08-17 22:06:26 +08:00
9bf6ed9d1d update 2023-08-17 19:51:36 +08:00
c0726d89f0 update 2023-08-17 19:09:56 +08:00
06c9b4e382 update 2023-08-17 17:14:31 +08:00
26669f76e0 update 2023-08-17 12:38:44 +08:00
1729b46e13 update 2023-08-17 12:26:57 +08:00
5661d46862 update 2023-08-16 23:36:18 +08:00
2d492ceb95 update 2023-08-16 23:25:52 +08:00
736c19c41e update 2023-08-16 23:02:14 +08:00
ce552012f3 update 2023-08-16 22:56:22 +08:00
0df4c6858b update 2023-08-16 22:23:58 +08:00
ec9a9a5074 update 2023-08-16 18:05:49 +08:00
1b72b840d6 update 2023-08-16 13:14:00 +08:00
79d29769e1 update 2023-08-15 16:59:55 +08:00
a9d0dd9017 update 2023-08-15 11:05:01 +08:00
0b6491e730 update 2023-08-14 18:10:37 +08:00
7fe71c892b update 2023-08-13 20:49:04 +08:00
0b17d03f23 update 2023-08-12 11:01:40 +08:00
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
124 changed files with 26302 additions and 1475 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"

3
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "framelesshelper"] [submodule "framelesshelper"]
path = 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,17 +2,29 @@ cmake_minimum_required(VERSION 3.20)
project(FluentUI VERSION 0.1 LANGUAGES CXX) project(FluentUI VERSION 0.1 LANGUAGES CXX)
set(FLUENTUI_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
list(APPEND CMAKE_MODULE_PATH ${FLUENTUI_DIRECTORY}/.cmake/)
include(GetGitRevisionDescription)
option(FLUENTUI_BUILD_EXAMPLES "Build FluentUI demo applications." ON) option(FLUENTUI_BUILD_EXAMPLES "Build FluentUI demo applications." ON)
option(FLUENTUI_BUILD_FRAMELESSHEPLER "Build FramelessHelper." ON) option(FLUENTUI_BUILD_FRAMELESSHEPLER "Build FramelessHelper." ON)
option(FLUENTUI_BUILD_STATIC_LIB "Build static library." OFF) option(FLUENTUI_BUILD_STATIC_LIB "Build static library." OFF)
find_package(Qt6 REQUIRED COMPONENTS Core Quick Qml)
set(QT_SDK_DIR "${Qt6_DIR}")
cmake_path(GET QT_SDK_DIR PARENT_PATH QT_SDK_DIR)
cmake_path(GET QT_SDK_DIR PARENT_PATH QT_SDK_DIR)
cmake_path(GET QT_SDK_DIR PARENT_PATH QT_SDK_DIR)
#设置QML插件输出目录可以通过外部设置如果外部没有设置就默认到<QT_SDK_DIR_PATH>\qml\FluentUI目录下 #设置QML插件输出目录可以通过外部设置如果外部没有设置就默认到<QT_SDK_DIR_PATH>\qml\FluentUI目录下
set(FLUENTUI_QML_PLUGIN_DIRECTORY "" CACHE PATH "Path to FluentUI plugin") set(FLUENTUI_QML_PLUGIN_DIRECTORY "" CACHE PATH "Path to FluentUI plugin")
if(NOT FLUENTUI_QML_PLUGIN_DIRECTORY) if(NOT FLUENTUI_QML_PLUGIN_DIRECTORY)
set(FLUENTUI_QML_PLUGIN_DIRECTORY ${CMAKE_PREFIX_PATH}/qml/FluentUI) set(FLUENTUI_QML_PLUGIN_DIRECTORY ${QT_SDK_DIR}/qml/FluentUI)
endif() endif()
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(zxing-cpp)
if (FLUENTUI_BUILD_EXAMPLES) if (FLUENTUI_BUILD_EXAMPLES)
add_subdirectory(example) add_subdirectory(example)
@ -20,9 +32,7 @@ endif ()
if (FLUENTUI_BUILD_FRAMELESSHEPLER) if (FLUENTUI_BUILD_FRAMELESSHEPLER)
set(FRAMELESSHELPER_BUILD_STATIC ON) set(FRAMELESSHELPER_BUILD_STATIC ON)
set(FRAMELESSHELPER_NO_SUMMARY OFF) set(FRAMELESSHELPER_NO_DEBUG_OUTPUT ON)
set(FRAMELESSHELPER_NO_DEBUG_OUTPUT OFF)
set(FRAMELESSHELPER_BUILD_WIDGETS OFF)
add_subdirectory(framelesshelper) add_subdirectory(framelesshelper)
endif () endif ()

265
THIRD_PARTY_COPYRIGHT.txt Normal file
View File

@ -0,0 +1,265 @@
This product use Third Party code for certain functions may include licensed under
terms that require meeting to display the following notices:
A text copy of this license is included with the download of
this FluentUI software. You may obtain a copy of the
source code from https://github.com/zhuzichu520/FluentUI
************************************************************************************
About Qt 6.5.0 Libraries Used in This Project:
This project, "FluentUI", utilizes the Qt 6.5.0 library.
Below is a list of the Qt modules used and their corresponding open-source licenses:
Qt Core - LGPL v3 / GPL v2 / Commercial License
Qt Quick - LGPL v3 / GPL v2 / Commercial License
Qt Quick Controls 2 - LGPL v3 / GPL v2 / Commercial License
Qt Gui - LGPL v3 / GPL v2 / Commercial License
Qt Multimedia - LGPL v3 / GPL v2 / Commercial License
Qt WebEngineQuick - LGPL v3 / GPL v3 / Commercial License
Qt Sql - LGPL v3 / GPL v2 / Commercial License
Qt Svg - LGPL v3 / GPL v2 / Commercial License
Qt Core5Compat - LGPL v3 / GPL v2 / Commercial License
Please note that the license information provided above is for reference only.
For the exact licensing, refer to the official Qt documentation and source code:
https://doc.qt.io/qt-6/licenses-used-in-qt.html
You can choose an appropriate license based on your requirements.
If you choose to use the LGPL or GPL licenses, please follow the relevant
open-source license regulations, such as releasing source code,
retaining copyright notices, etc.
We would like to thank the Qt project and its contributors for
providing powerful tools and support for this project.
For more information about the Qt project,
please visit the official Qt website (https://www.qt.io/).
************************************************************************************
framelesshelper
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
************************************************************************************
zxing-cpp
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -8,6 +8,9 @@ if(APPLE)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE) set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif() endif()
#导入exmaple的QML位置不然import example有时候会爆红
set(QML_IMPORT_PATH ${CMAKE_BINARY_DIR}/example CACHE STRING "Qt Creator extra QML import paths" FORCE)
#判断FluentUI库类型 #判断FluentUI库类型
if(FLUENTUI_BUILD_STATIC_LIB) if(FLUENTUI_BUILD_STATIC_LIB)
add_definitions(-DFLUENTUI_BUILD_STATIC_LIB) add_definitions(-DFLUENTUI_BUILD_STATIC_LIB)
@ -23,10 +26,7 @@ endif()
#获取文件路径分隔符(解决执行命令的时候有些平台会报错) #获取文件路径分隔符(解决执行命令的时候有些平台会报错)
file(TO_CMAKE_PATH "/" PATH_SEPARATOR) file(TO_CMAKE_PATH "/" PATH_SEPARATOR)
#设置版本号 find_package(Qt6 REQUIRED COMPONENTS Quick Svg Network)
add_definitions(-DVERSION=1,3,7,4)
find_package(Qt6 REQUIRED COMPONENTS Quick Svg)
if(QT_VERSION VERSION_GREATER_EQUAL "6.3") if(QT_VERSION VERSION_GREATER_EQUAL "6.3")
qt_standard_project_setup() qt_standard_project_setup()
@ -36,6 +36,13 @@ else()
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
endif() endif()
##生成版本信息头文件
set(HEADER_FILE_VERSION_PATH ${FLUENTUI_DIRECTORY}/example/Version.h)
configure_file(
${FLUENTUI_DIRECTORY}/.cmake/Version.h.in
${HEADER_FILE_VERSION_PATH}
)
#遍历所有Cpp文件 #遍历所有Cpp文件
file(GLOB_RECURSE CPP_FILES *.cpp *.h) file(GLOB_RECURSE CPP_FILES *.cpp *.h)
foreach(filepath ${CPP_FILES}) foreach(filepath ${CPP_FILES})
@ -57,11 +64,21 @@ foreach(filepath ${RES_PATHS})
list(APPEND resource_files ${filename}) list(APPEND resource_files ${filename})
endforeach(filepath) 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(
${FLUENTUI_DIRECTORY}/.cmake/version_exe.rc.in
${EXAMPLE_VERSION_RC_PATH}
)
endif()
#添加可执行文件 #添加可执行文件
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (WIN32)
qt_add_executable(example qt_add_executable(example
${sources_files} ${sources_files}
example.rc ${EXAMPLE_VERSION_RC_PATH}
) )
else () else ()
qt_add_executable(example qt_add_executable(example
@ -87,12 +104,17 @@ endif()
#添加qml模块 #添加qml模块
qt_add_qml_module(example qt_add_qml_module(example
URI example URI "example"
VERSION 1.0 VERSION 1.0
QML_FILES ${qml_files} QML_FILES ${qml_files}
RESOURCES ${resource_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 set_target_properties(example PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
@ -106,7 +128,8 @@ set_target_properties(example PROPERTIES
if (FLUENTUI_BUILD_STATIC_LIB) if (FLUENTUI_BUILD_STATIC_LIB)
target_link_libraries(example PRIVATE target_link_libraries(example PRIVATE
Qt6::Quick Qt6::Quick
Qt::Svg Qt6::Svg
Qt6::Network
fluentui fluentui
fluentuiplugin fluentuiplugin
FramelessHelper::Core FramelessHelper::Core
@ -115,7 +138,8 @@ if (FLUENTUI_BUILD_STATIC_LIB)
else() else()
target_link_libraries(example PRIVATE target_link_libraries(example PRIVATE
Qt6::Quick Qt6::Quick
Qt::Svg Qt6::Svg
Qt6::Network
fluentuiplugin fluentuiplugin
FramelessHelper::Core FramelessHelper::Core
FramelessHelper::Quick 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 { Window {
id: app id: app
flags: Qt.SplashScreen 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: { Component.onCompleted: {
FluApp.init(app) FluApp.init(app)
FluTheme.darkMode = FluDarkMode.System FluTheme.darkMode = FluThemeType.System
FluTheme.enableAnimation = true FluTheme.enableAnimation = true
FluApp.routes = { FluApp.routes = {
"/":"qrc:/example/qml/window/MainWindow.qml", "/":"qrc:/example/qml/window/MainWindow.qml",
@ -21,6 +38,7 @@ Window {
"/singleInstanceWindow":"qrc:/example/qml/window/SingleInstanceWindow.qml" "/singleInstanceWindow":"qrc:/example/qml/window/SingleInstanceWindow.qml"
} }
FluApp.initialRoute = "/" FluApp.initialRoute = "/"
FluApp.httpInterceptor = interceptor
FluApp.run() FluApp.run()
} }
} }

View File

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

View File

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

View File

@ -7,6 +7,12 @@ FluObject{
property var navigationView property var navigationView
function rename(item, newName){
if(newName && newName.trim().length>0){
item.title = newName;
}
}
FluPaneItem{ FluPaneItem{
id:item_home id:item_home
count: 9 count: 9
@ -21,11 +27,38 @@ FluObject{
} }
navigationView.push("qrc:/example/qml/page/T_Home.qml") navigationView.push("qrc:/example/qml/page/T_Home.qml")
} }
editDelegate: FluTextBox{
text:item_home.title
}
menuDelegate: FluMenu{
id:nav_item_right_menu
width: 120
FluMenuItem{
text: "重命名"
visible: true
onClicked: {
item_home.showEdit = true
}
}
}
} }
FluPaneItemExpander{ FluPaneItemExpander{
id:item_expander_basic_input
title:lang.basic_input title:lang.basic_input
icon:FluentIcons.CheckboxComposite icon:FluentIcons.CheckboxComposite
editDelegate: FluTextBox{
text:item_expander_basic_input.title
}
menuDelegate: FluMenu{
FluMenuItem{
text: "重命名"
visible: true
onClicked: {
item_expander_basic_input.showEdit = true
}
}
}
FluPaneItem{ FluPaneItem{
id:item_buttons id:item_buttons
count: 99 count: 99
@ -181,6 +214,12 @@ FluObject{
navigationView.push("qrc:/example/qml/page/T_Expander.qml") navigationView.push("qrc:/example/qml/page/T_Expander.qml")
} }
} }
FluPaneItem{
title:"Watermark"
onTap:{
navigationView.push("qrc:/example/qml/page/T_Watermark.qml")
}
}
} }
FluPaneItemExpander{ FluPaneItemExpander{
@ -317,9 +356,56 @@ FluObject{
} }
} }
FluPaneItemSeparator{
spacing:20
size:1
}
FluPaneItemExpander{ FluPaneItemExpander{
title:lang.other title:lang.other
icon:FluentIcons.Shop 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:"Screenshot"
onTap:{
navigationView.push("qrc:/example/qml/page/T_Screenshot.qml")
}
}
FluPaneItem{
title:"Captcha"
onTap:{
navigationView.push("qrc:/example/qml/page/T_Captcha.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{ FluPaneItem{
id:item_other id:item_other
title:"RemoteLoader" title:"RemoteLoader"
@ -336,7 +422,7 @@ FluObject{
FluPaneItem{ FluPaneItem{
title:"HotLoader" title:"HotLoader"
tapFunc:function(){ tapFunc:function(){
FluApp.navigate("/hotload") FluApp.navigate("/hotload")
} }
} }
} }
@ -379,7 +465,12 @@ FluObject{
for(var i=0;i<items.length;i++){ for(var i=0;i<items.length;i++){
var item = items[i] var item = items[i]
if(item instanceof FluPaneItem){ 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 return arr

View File

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

View File

@ -9,35 +9,84 @@ FluScrollablePage{
title:"Acrylic" 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{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 20 height: 1200/4+20
height: 1200/5+20
paddings: 10 paddings: 10
Layout.topMargin: 10
FluRectangle{ FluRectangle{
width: 1920/5 width: 1920/4
height: 1200/5 height: 1200/4
radius:[15,15,15,15] radius:[15,15,15,15]
Image { Image {
id:image id:image
asynchronous: true asynchronous: true
source: "qrc:/example/res/image/banner_3.jpg" source: "qrc:/example/res/image/bg_scenic.png"
anchors.fill: parent anchors.fill: parent
sourceSize: Qt.size(2*width,2*height) sourceSize: Qt.size(2*width,2*height)
} }
FluAcrylic { FluAcrylic {
sourceItem:image id:acrylic
anchors.bottom: parent.bottom target: image
anchors.right: parent.right width: 200
width: 100 height: 200
height: 100 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 { FluText {
anchors.centerIn: parent anchors.centerIn: parent
text: "Acrylic" text: "Acrylic"
color: "#FFFFFF" color: "#FFFFFF"
font.bold: true 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 Layout.topMargin: 20
} }
@ -54,7 +103,7 @@ FluScrollablePage{
radius: 8 radius: 8
} }
FluAcrylic{ FluAcrylic{
sourceItem:image target:image
width: 100 width: 100
height: 100 height: 100
anchors.centerIn: parent anchors.centerIn: parent

View File

@ -167,16 +167,26 @@ FluScrollablePage{
height: 68 height: 68
paddings: 10 paddings: 10
Layout.topMargin: 20 Layout.topMargin: 20
FluIconButton{ Row{
iconSource:FluentIcons.ChromeCloseContrast spacing: 20
disabled:icon_button_switch.checked
iconSize: 15
anchors{ anchors{
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter
left: parent.left left: parent.left
} }
onClicked:{ FluIconButton{
showSuccess("点击IconButton") iconSource:FluentIcons.ChromeCloseContrast
disabled:icon_button_switch.checked
iconSize: 15
onClicked:{
showSuccess("点击IconButton")
}
}
FluIconButton{
disabled:icon_button_switch.checked
iconDelegate: Image{ width: 20; height: 20; source: "qrc:/example/res/image/ic_home_github.png" }
onClicked:{
showSuccess("点击IconButton")
}
} }
} }
FluToggleSwitch{ FluToggleSwitch{

View File

@ -0,0 +1,54 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Window
import QtQuick.Controls
import FluentUI
import "qrc:///example/qml/component"
FluScrollablePage{
title:"Captcha"
FluCaptcha{
id:captcha
Layout.topMargin: 20
MouseArea{
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
captcha.refresh()
}
}
}
FluButton{
text:"Refresh"
Layout.topMargin: 20
onClicked: {
captcha.refresh()
}
}
RowLayout{
spacing: 10
Layout.topMargin: 10
FluTextBox{
id:text_box
placeholderText: "请输入验证码"
}
FluButton{
text:"verify"
onClicked: {
var success = captcha.verify(text_box.text)
if(success){
showSuccess("验证码正确")
}else{
showError("错误验证,请重新输入")
}
}
}
}
}

View File

@ -9,6 +9,19 @@ FluScrollablePage{
title:"Carousel" 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{ FluArea{
Layout.fillWidth: true Layout.fillWidth: true
height: 370 height: 370
@ -24,23 +37,95 @@ FluScrollablePage{
text:"轮播图支持无限轮播无限滑动用ListView实现的组件" text:"轮播图支持无限轮播无限滑动用ListView实现的组件"
} }
FluCarousel{ 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.topMargin: 20
Layout.leftMargin: 5 Layout.leftMargin: 5
Component.onCompleted: { 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{ CodeExpander{
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: -1 Layout.topMargin: -1
code:'FluCarousel{ code:'FluCarousel{
id:carousel
width: 400 width: 400
height: 300 height: 300
delegate: Component{
Image {
anchors.fill: parent
source: model.url
asynchronous: true
fillMode:Image.PreserveAspectCrop
}
}
Component.onCompleted: { 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:"友情提示" title:"友情提示"
message:"确定要退出程序么?" message:"确定要退出程序么?"
negativeText:"取消" negativeText:"取消"
buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
onNegativeClicked:{ onNegativeClicked:{
showSuccess("点击取消按钮") showSuccess("点击取消按钮")
} }
@ -47,7 +47,7 @@ FluScrollablePage{
id:double_btn_dialog id:double_btn_dialog
title:"友情提示" title:"友情提示"
message:"确定要退出程序么?" message:"确定要退出程序么?"
buttonFlags: FluContentDialog.NegativeButton | FluContentDialog.PositiveButton buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText:"取消" negativeText:"取消"
onNegativeClicked:{ onNegativeClicked:{
showSuccess("点击取消按钮") showSuccess("点击取消按钮")
@ -80,7 +80,7 @@ FluScrollablePage{
title:"友情提示" title:"友情提示"
message:"确定要退出程序么?" message:"确定要退出程序么?"
negativeText:"取消" negativeText:"取消"
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText:"取消" negativeText:"取消"
onNegativeClicked:{ onNegativeClicked:{
showSuccess("点击取消按钮") showSuccess("点击取消按钮")
@ -101,7 +101,7 @@ FluScrollablePage{
id:triple_btn_dialog id:triple_btn_dialog
title:"友情提示" title:"友情提示"
message:"确定要退出程序么?" message:"确定要退出程序么?"
buttonFlags: FluContentDialog.NeutralButton | FluContentDialog.NegativeButton | FluContentDialog.PositiveButton buttonFlags: FluContentDialogType.NeutralButton | FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
negativeText:"取消" negativeText:"取消"
onNegativeClicked:{ onNegativeClicked:{
showSuccess("点击取消按钮") showSuccess("点击取消按钮")

View File

@ -7,7 +7,7 @@ import FluentUI
FluScrollablePage{ FluScrollablePage{
launchMode: FluPage.SingleTask launchMode: FluPageType.SingleTask
animDisabled: true animDisabled: true
ListModel{ ListModel{
@ -22,7 +22,7 @@ FluScrollablePage{
Item{ Item{
Layout.fillWidth: true Layout.fillWidth: true
height: 320 Layout.preferredHeight: 320
Image { Image {
id: bg id: bg
fillMode:Image.PreserveAspectCrop fillMode:Image.PreserveAspectCrop
@ -68,19 +68,23 @@ FluScrollablePage{
id: control id: control
width: 220 width: 220
height: 240 height: 240
FluArea{ FluShadow{
radius: 8 radius:8
anchors.fill: item_content
}
FluItem{
id:item_content
radius: [8,8,8,8]
width: 200 width: 200
height: 220 height: 220
anchors.centerIn: parent anchors.centerIn: parent
color: 'transparent' FluAcrylic{
FluAcrylic {
sourceItem:bg
anchors.fill: parent 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) tintColor: FluTheme.dark ? Qt.rgba(0,0,0,1) : Qt.rgba(1,1,1,1)
rectX: list.x-list.contentX+10+(control.width)*index target: bg
rectY: list.y+10 tintOpacity: FluTheme.dark ? 0.8 : 0.9
acrylicOpacity:0.8 blurRadius : 40
targetRect: Qt.rect(list.x-list.contentX+10+(control.width)*index,list.y+10,width,height)
} }
Rectangle{ Rectangle{
anchors.fill: parent anchors.fill: parent
@ -90,16 +94,15 @@ FluScrollablePage{
if(item_mouse.containsMouse){ if(item_mouse.containsMouse){
return Qt.rgba(1,1,1,0.03) return Qt.rgba(1,1,1,0.03)
} }
return Qt.rgba(0,0,0,0) return Qt.rgba(0,0,0,0.0)
}else{ }else{
if(item_mouse.containsMouse){ if(item_mouse.containsMouse){
return Qt.rgba(0,0,0,0.03) return Qt.rgba(0,0,0,0.03)
} }
return Qt.rgba(0,0,0,0) return Qt.rgba(0,0,0,0.0)
} }
} }
} }
ColumnLayout{ ColumnLayout{
Image { Image {
Layout.topMargin: 20 Layout.topMargin: 20
@ -138,10 +141,11 @@ FluScrollablePage{
id:item_mouse id:item_mouse
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onWheel: (wheel)=>{ onWheel:
if (wheel.angleDelta.y > 0) scrollbar_header.decrease() (wheel)=>{
else scrollbar_header.increase() if (wheel.angleDelta.y > 0) scrollbar_header.decrease()
} else scrollbar_header.increase()
}
onClicked: { onClicked: {
Qt.openUrlExternally(model.url) Qt.openUrlExternally(model.url)
} }
@ -252,7 +256,7 @@ FluScrollablePage{
GridView{ GridView{
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: contentHeight Layout.preferredHeight: contentHeight
cellHeight: 120 cellHeight: 120
cellWidth: 320 cellWidth: 320
model:ItemsOriginal.getRecentlyAddedData() model:ItemsOriginal.getRecentlyAddedData()
@ -269,7 +273,7 @@ FluScrollablePage{
GridView{ GridView{
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: contentHeight Layout.preferredHeight: contentHeight
cellHeight: 120 cellHeight: 120
cellWidth: 320 cellWidth: 320
interactive: false interactive: false

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

@ -0,0 +1,279 @@
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: {
folder_dialog.open()
}
}
FluButton{
id:btn_upload
implicitWidth: parent.width
implicitHeight: 36
text: "文件上传"
onClicked: {
file_dialog.open()
}
}
}
}
FileDialog {
id: file_dialog
onAccepted: {
var param = {}
for(var i=0;i<selectedFiles.length;i++){
var fileUrl = selectedFiles[i]
var fileName = FluTools.getFileNameByUrl(fileUrl)
var filePath = FluTools.toLocalPath(fileUrl)
param[fileName] = filePath
}
console.debug(JSON.stringify(param))
var callable = {}
callable.onStart = function(){
btn_upload.disabled = true
}
callable.onFinish = function(){
btn_upload.disabled = false
btn_upload.text = "上传文件"
layout_upload_file_size.visible = false
text_upload_file_size.text = ""
}
callable.onSuccess = function(result){
text_info.text = result
console.debug(result)
}
callable.onError = function(status,errorString,result){
text_info.text = result
console.debug(result)
}
callable.onUploadProgress = function(sent,total){
var locale = Qt.locale()
var precent = (sent/total * 100).toFixed(0) + "%"
btn_upload.text = "上传中..."+precent
text_upload_file_size.text = "%1/%2".arg(locale.formattedDataSize(sent)).arg(locale.formattedDataSize(total))
layout_upload_file_size.visible = true
}
http.upload("https://httpbingo.org/post",callable,param)
}
}
FolderDialog {
id: folder_dialog
currentFolder: StandardPaths.standardLocations(StandardPaths.DownloadLocation)[0]
onAccepted: {
var callable = {}
callable.onStart = function(){
btn_download.disabled = true
}
callable.onFinish = function(){
btn_download.disabled = false
btn_download.text = "下载文件"
layout_download_file_size.visible = false
text_download_file_size.text = ""
}
callable.onSuccess = function(result){
showSuccess(result)
}
callable.onError = function(status,errorString){
showError(errorString)
}
callable.onDownloadProgress = function(recv,total){
var locale = Qt.locale()
var precent = (recv/total * 100).toFixed(0) + "%"
btn_download.text = "下载中..."+precent
text_download_file_size.text = "%1/%2".arg(locale.formattedDataSize(recv)).arg(locale.formattedDataSize(total))
layout_download_file_size.visible = true
}
var path = FluTools.toLocalPath(currentFolder)+ "/big_buck_bunny.mp4"
http.download("http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",callable,path)
}
}
FluArea{
anchors{
top: layout_flick.top
bottom: layout_flick.bottom
left: layout_flick.right
right: parent.right
leftMargin: 8
}
Flickable{
clip: true
id:scrollview
boundsBehavior:Flickable.StopAtBounds
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_download_file_size
radius: [4,4,4,4]
height: 36
width: 160
visible: false
x:layout_flick.width
y: 173 - layout_flick.contentY
FluText{
id:text_download_file_size
anchors.centerIn: parent
}
}
FluRectangle{
id:layout_upload_file_size
radius: [4,4,4,4]
height: 36
width: 160
visible: false
x:layout_flick.width
y: 210 - layout_flick.contentY
FluText{
id:text_upload_file_size
anchors.centerIn: parent
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,106 @@
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
bgColor: bgcolor_picker.colorValue
margins:slider_margins.value
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
Layout.topMargin: 10
FluText{
text:"bgColor:"
Layout.alignment: Qt.AlignVCenter
}
FluColorPicker{
id:bgcolor_picker
Component.onCompleted: {
setColor(Qt.rgba(1,1,1,1))
}
}
}
RowLayout{
spacing: 10
FluText{
text:"margins:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_margins
from:0
to:80
value: 0
}
}
RowLayout{
spacing: 10
FluText{
text:"size:"
Layout.alignment: Qt.AlignVCenter
}
FluSlider{
id:slider_size
from:120
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" import "qrc:///example/qml/component"
FluPage{ FluPage{
launchMode: FluPage.SingleTop launchMode: FluPageType.SingleTop
FluRemoteLoader{ FluRemoteLoader{
anchors.fill: parent anchors.fill: parent
source: "https://zhu-zichu.gitee.io/T_RemoteLoader.qml" source: "https://zhu-zichu.gitee.io/T_RemoteLoader.qml"

View File

@ -0,0 +1,54 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Window
import QtQuick.Controls
import FluentUI
import "qrc:///example/qml/component"
FluScrollablePage{
title:"Screenshot"
FluArea{
Layout.fillWidth: true
height: 100
paddings: 10
Layout.topMargin: 20
FluFilledButton{
anchors.verticalCenter: parent.verticalCenter
text:"Open Screenshot"
onClicked: {
screenshot.open()
}
}
}
Rectangle{
Layout.preferredHeight: 400
Layout.preferredWidth: 400
Layout.topMargin: 10
Layout.leftMargin: 4
color: FluTheme.dark ? FluColors.Black : FluColors.White
FluShadow{
radius: 0
color: FluTheme.primaryColor.dark
}
Image{
id:image
anchors.fill: parent
fillMode: Image.PreserveAspectFit
asynchronous: true
}
}
FluScreenshot{
id:screenshot
captrueMode: FluScreenshotType.File
saveFolder: FluTools.getApplicationDirPath()+"/screenshot"
onCaptrueCompleted:
(captrue)=>{
image.source = captrue
}
}
}

View File

@ -28,7 +28,7 @@ FluScrollablePage{
Layout.bottomMargin: 4 Layout.bottomMargin: 4
} }
Repeater{ 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{ delegate: FluRadioButton{
checked : FluTheme.darkMode === modelData.mode checked : FluTheme.darkMode === modelData.mode
text:modelData.title text:modelData.title
@ -58,7 +58,7 @@ FluScrollablePage{
Layout.bottomMargin: 4 Layout.bottomMargin: 4
} }
Repeater{ 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{ delegate: FluRadioButton{
checked : MainEvent.displayMode===modelData.mode checked : MainEvent.displayMode===modelData.mode
text:modelData.title text:modelData.title

View File

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

View File

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

View File

@ -64,9 +64,6 @@ FluContentPage{
address: getRandomAddresses(), address: getRandomAddresses(),
nickname: getRandomNickname(), nickname: getRandomNickname(),
longstring:"你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好", longstring:"你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好",
height:42,
minimumHeight:42,
maximumHeight:300,
action:com_action action:com_action
}) })
} }
@ -110,10 +107,7 @@ FluContentPage{
{ {
title: '姓名', title: '姓名',
dataIndex: 'name', dataIndex: 'name',
width:100, readOnly:true,
minimumWidth:80,
maximumWidth:200,
readOnly:true
}, },
{ {
title: '年龄', title: '年龄',

View File

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

View File

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

View File

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

View File

@ -0,0 +1,170 @@
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: FluTheme.dark ? FluColors.Teal.lighter : FluColors.Teal.dark
}
}
Component{
id:com_lable
FluText{
wrapMode: Text.WrapAnywhere
font.bold: true
horizontalAlignment: isRight ? Qt.AlignRight : Qt.AlignLeft
text: modelData.lable
color: FluTheme.dark ? FluColors.Teal.lighter : FluColors.Teal.dark
MouseArea{
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
showSuccess(modelData.lable)
}
}
}
}
Component{
id:com_text
FluText{
wrapMode: Text.WrapAnywhere
horizontalAlignment: isRight ? Qt.AlignRight : Qt.AlignLeft
text: modelData.text
font.bold: true
linkColor: FluTheme.dark ? FluColors.Teal.lighter : FluColors.Teal.dark
onLinkActivated:
(link)=> {
Qt.openUrlExternally(link)
}
onLinkHovered:
(link)=> {
if(link === ""){
FluTools.restoreOverrideCursor()
}else{
FluTools.setOverrideCursor(Qt.PointingHandCursor)
}
}
}
}
ListModel{
id:list_model
ListElement{
lable:"2013-09-01"
text:"考上中国皮城大学,杰斯武器工坊专业"
}
ListElement{
lable:"2017-07-01"
text:"大学毕业在寝室打了4年LOL没想到毕业还要找工作瞬间蒙蔽害"
}
ListElement{
lable:"2017-09-01"
text:"开始找工作,毕业即失业!回农村老家躺平,继承三亩良田"
}
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组件库"
}
ListElement{
lable:"2023-03-28富文本展示"
text:'将FluentUI源码开源到<a href="https://github.com/zhuzichu520/FluentUI">github</a>,并发布视频到<a href="https://www.bilibili.com/video/BV1mg4y1M71w">B站</a>'
lableDelegate:()=>com_lable
textDelegate:()=>com_text
dot:()=>com_dot
}
}
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
Layout.topMargin: 20
Layout.bottomMargin: 20
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" text:"None"
onClicked: { onClicked: {
btn_selection_model.text = text btn_selection_model.text = text
tree_view.selectionMode = FluTabView.Equal tree_view.selectionMode = FluTabViewType.Equal
} }
} }
FluMenuItem{ FluMenuItem{
text:"Single" text:"Single"
onClicked: { onClicked: {
btn_selection_model.text = text btn_selection_model.text = text
tree_view.selectionMode = FluTabView.SizeToContent tree_view.selectionMode = FluTabViewType.SizeToContent
} }
} }
FluMenuItem{ FluMenuItem{
text:"Muiltple" text:"Muiltple"
onClicked: { onClicked: {
btn_selection_model.text = text btn_selection_model.text = text
tree_view.selectionMode = FluTabView.Compact tree_view.selectionMode = FluTabViewType.Compact
} }
} }
} }
FluFilledButton{ FluFilledButton{
text:"获取选中的数据" text:"获取选中的数据"
onClicked: { onClicked: {
if(tree_view.selectionMode === FluTreeView.None){ if(tree_view.selectionMode === FluTreeViewType.None){
showError("当前非选择模式,没有选中的数据") showError("当前非选择模式,没有选中的数据")
} }
if(tree_view.selectionMode === FluTreeView.Single){ if(tree_view.selectionMode === FluTreeViewType.Single){
if(!tree_view.signleData()){ if(!tree_view.signleData()){
showError("没有选中数据") showError("没有选中数据")
return return
} }
showSuccess(tree_view.signleData().text) showSuccess(tree_view.signleData().text)
} }
if(tree_view.selectionMode === FluTreeView.Multiple){ if(tree_view.selectionMode === FluTreeViewType.Multiple){
if(tree_view.multipData().length===0){ if(tree_view.multipData().length===0){
showError("没有选中数据") showError("没有选中数据")
return 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 width: 600
height: 600 height: 600
fixSize: true fixSize: true
launchMode: FluWindow.SingleTask launchMode: FluWindowType.SingleTask
ColumnLayout{ ColumnLayout{
anchors{ anchors{

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

View File

@ -4,16 +4,12 @@
#include <QDebug> #include <QDebug>
#include "lang/En.h" #include "lang/En.h"
#include "lang/Zh.h" #include "lang/Zh.h"
#include "Version.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))
AppInfo::AppInfo(QObject *parent) AppInfo::AppInfo(QObject *parent)
: QObject{parent} : QObject{parent}
{ {
version(VER_STR); version(APPLICATION_VERSION);
lang(new En()); lang(new En());
} }
@ -39,18 +35,3 @@ void AppInfo::changeLang(const QString& locale){
lang(new En()); 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 <QObject>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include "tool/IPC.h"
#include "lang/Lang.h" #include "lang/Lang.h"
#include "stdafx.h" #include "stdafx.h"
@ -15,9 +14,7 @@ class AppInfo : public QObject
public: public:
explicit AppInfo(QObject *parent = nullptr); explicit AppInfo(QObject *parent = nullptr);
void init(QQmlApplicationEngine *engine); void init(QQmlApplicationEngine *engine);
bool isOwnerProcess(IPC *ipc);
Q_INVOKABLE void changeLang(const QString& locale); Q_INVOKABLE void changeLang(const QString& locale);
Q_SIGNAL void activeWindow();
}; };
#endif // APPINFO_H #endif // APPINFO_H

View File

@ -6,11 +6,12 @@
CircularReveal::CircularReveal(QQuickItem* parent) : QQuickPaintedItem(parent) CircularReveal::CircularReveal(QQuickItem* parent) : QQuickPaintedItem(parent)
{ {
setVisible(false); setVisible(false);
_anim = new QPropertyAnimation(this, "radius", this); _anim.setDuration(333);
_anim->setDuration(333); _anim.setEasingCurve(QEasingCurve::OutCubic);
_anim->setEasingCurve(QEasingCurve::OutCubic); connect(&_anim, &QPropertyAnimation::finished,this,[=](){
connect(_anim, &QPropertyAnimation::finished,this,[=](){ update();
setVisible(false); setVisible(false);
Q_EMIT animationFinished();
}); });
connect(this,&CircularReveal::radiusChanged,this,[=](){ connect(this,&CircularReveal::radiusChanged,this,[=](){
update(); update();
@ -20,7 +21,6 @@ CircularReveal::CircularReveal(QQuickItem* parent) : QQuickPaintedItem(parent)
void CircularReveal::paint(QPainter* painter) void CircularReveal::paint(QPainter* painter)
{ {
painter->save(); painter->save();
painter->eraseRect(boundingRect());
painter->drawImage(QRect(0, 0, static_cast<int>(width()), static_cast<int>(height())), _source); painter->drawImage(QRect(0, 0, static_cast<int>(width()), static_cast<int>(height())), _source);
QPainterPath path; QPainterPath path;
path.moveTo(_center.x(),_center.y()); path.moveTo(_center.x(),_center.y());
@ -31,8 +31,8 @@ void CircularReveal::paint(QPainter* painter)
} }
void CircularReveal::start(int w,int h,const QPoint& center,int radius){ void CircularReveal::start(int w,int h,const QPoint& center,int radius){
_anim->setStartValue(0); _anim.setStartValue(0);
_anim->setEndValue(radius); _anim.setEndValue(radius);
_center = center; _center = center;
_grabResult = _target->grabToImage(QSize(w,h)); _grabResult = _target->grabToImage(QSize(w,h));
connect(_grabResult.data(), &QQuickItemGrabResult::ready, this, &CircularReveal::handleGrabResult); connect(_grabResult.data(), &QQuickItemGrabResult::ready, this, &CircularReveal::handleGrabResult);
@ -43,5 +43,5 @@ void CircularReveal::handleGrabResult(){
update(); update();
setVisible(true); setVisible(true);
Q_EMIT imageChanged(); Q_EMIT imageChanged();
_anim->start(); _anim.start();
} }

View File

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

View File

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

View File

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

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

149
src/Def.h
View File

@ -4,15 +4,150 @@
#include <QObject> #include <QObject>
#include <QtQml/qqml.h> #include <QtQml/qqml.h>
namespace Fluent_DarkMode {
namespace FluScreenshotType {
Q_NAMESPACE Q_NAMESPACE
enum Fluent_DarkModeType { enum CaptrueMode {
System = 0x0, Pixmap = 0x0000,
Light = 0x1, File = 0x0001,
Dark = 0x2,
}; };
Q_ENUM_NS(Fluent_DarkModeType) Q_ENUM_NS(CaptrueMode)
QML_NAMED_ELEMENT(FluDarkMode) QML_NAMED_ELEMENT(FluScreenshotType)
}
namespace FluThemeType {
Q_NAMESPACE
enum DarkMode {
System = 0x0000,
Light = 0x0001,
Dark = 0x0002,
};
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 { namespace Fluent_Awesome {

View File

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

View File

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

73
src/FluCaptcha.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "FluCaptcha.h"
#include <QTime>
#include <QChar>
#include <QPainter>
#include <QRandomGenerator>
#include <qmath.h>
FluCaptcha::FluCaptcha(QQuickItem *parent)
: QQuickPaintedItem(parent)
{
font(QFont("楷体",25,QFont::Bold,true));
setWidth(180);
setHeight(80);
refresh();
}
void FluCaptcha::paint(QPainter* painter)
{
painter->save();
painter->fillRect(boundingRect().toRect(),QColor(255,255,255,255));
QPen pen;
painter->setFont(_font);
for(int i=0;i<100;i++)
{
pen = QPen(QColor(_generaNumber(256),_generaNumber(256),_generaNumber(256)));
painter->setPen(pen);
painter->drawPoint(_generaNumber(180),_generaNumber(80));
}
for(int i=0;i<5;i++)
{
pen = QPen(QColor(_generaNumber(256),_generaNumber(256),_generaNumber(256)));
painter->setPen(pen);
painter->drawLine(_generaNumber(180),_generaNumber(80),_generaNumber(180),_generaNumber(80));
}
for(int i=0;i<4;i++)
{
pen = QPen(QColor(_generaNumber(255),_generaNumber(255),_generaNumber(255)));
painter->setPen(pen);
painter->drawText(15+35*i,10+_generaNumber(15),30,40,Qt::AlignCenter, QString(_code[i]));
}
painter->restore();
}
int FluCaptcha::_generaNumber(int number){
return QRandomGenerator::global()->bounded(0,number);
}
void FluCaptcha::refresh(){
this->_code.clear();
for(int i = 0;i < 4;++i)
{
int num = _generaNumber(3);
if(num == 0)
{
this->_code += QString::number(_generaNumber(10));
}
else if(num == 1)
{
int temp = 'A';
this->_code += static_cast<QChar>(temp + _generaNumber(26));
}
else if(num == 2)
{
int temp = 'a';
this->_code += static_cast<QChar>(temp + _generaNumber(26));
}
}
update();
}
bool FluCaptcha::verify(const QString& code){
return this->_code == code;
}

24
src/FluCaptcha.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef FLUCAPTCHA_H
#define FLUCAPTCHA_H
#include <QQuickItem>
#include <QQuickPaintedItem>
#include <QPainter>
#include "stdafx.h"
class FluCaptcha : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY_AUTO(QFont,font);
QML_NAMED_ELEMENT(FluCaptcha)
private:
int _generaNumber(int number);
QString _code;
public:
explicit FluCaptcha(QQuickItem *parent = nullptr);
void paint(QPainter* painter) override;
Q_INVOKABLE void refresh();
Q_INVOKABLE bool verify(const QString& code);
};
#endif // FLUCAPTCHA_H

View File

@ -13,6 +13,7 @@ FluColors *FluColors::getInstance()
FluColors::FluColors(QObject *parent) FluColors::FluColors(QObject *parent)
: QObject{parent} : QObject{parent}
{ {
Transparent("#00000000");
Black("#000000"); Black("#000000");
White("#ffffff"); White("#ffffff");
Grey10("#faf9f8"); Grey10("#faf9f8");

View File

@ -12,6 +12,7 @@
class FluColors : public QObject class FluColors : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY_AUTO(QString,Transparent);
Q_PROPERTY_AUTO(QString,Black); Q_PROPERTY_AUTO(QString,Black);
Q_PROPERTY_AUTO(QString,White); Q_PROPERTY_AUTO(QString,White);
Q_PROPERTY_AUTO(QString,Grey10); Q_PROPERTY_AUTO(QString,Grey10);

361
src/FluHttp.cpp Normal file
View File

@ -0,0 +1,361 @@
#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);
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,result);
}
}
}
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,result);
}
}
}
onFinish(callable);
});
}
void FluHttp::postJson(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"postJson").toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
for (int i = 0; i < retry(); ++i) {
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QString contentType = QString("application/json;charset=utf-8");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
QEventLoop loop;
QNetworkReply* reply = manager.post(request,QJsonDocument::fromVariant(data["params"]).toJson());
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString,result);
}
}
}
onFinish(callable);
});
}
void FluHttp::get(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"get").toMap();
QThreadPool::globalInstance()->start([=](){
for (int i = 0; i < retry(); ++i) {
onStart(callable);
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
addQueryParam(&_url,data["params"].toMap());
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QEventLoop loop;
QNetworkReply* reply = manager.get(request);
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
break;
}else{
if(i == retry()-1){
onError(callable,status,errorString,result);
}
}
}
onFinish(callable);
});
}
void FluHttp::download(QString url,QJSValue callable,QString filePath,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"download").toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
QNetworkAccessManager manager;
QUrl _url(url);
addQueryParam(&_url,data["params"].toMap());
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QSharedPointer<QFile> file(new QFile(filePath));
QIODevice::OpenMode mode = QIODevice::WriteOnly|QIODevice::Truncate;
if (!file->open(mode))
{
onError(callable,-1,QString("Url: %1 %2 Non-Writable").arg(request.url().toString(),file->fileName()),"");
onFinish(callable);
return;
}
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);
});
}
void FluHttp::upload(QString url,QJSValue callable,QMap<QString, QVariant> params,QMap<QString, QVariant> headers){
QMap<QString, QVariant> data = invokeIntercept(params,headers,"upload").toMap();
QThreadPool::globalInstance()->start([=](){
onStart(callable);
QNetworkAccessManager manager;
manager.setTransferTimeout(timeout());
QUrl _url(url);
QNetworkRequest request(_url);
addHeaders(&request,data["headers"].toMap());
QHttpMultiPart multiPart(QHttpMultiPart::FormDataType);
for (const auto& each : data["params"].toMap().toStdMap())
{
const QString& key = each.first;
const QString& filePath = each.second.toString();
QFile *file = new QFile(filePath);
file->open(QIODevice::ReadOnly);
file->setParent(&multiPart);
QString dispositionHeader = QString("form-data; name=\"%1\"; filename=\"%2\"").arg(key,filePath);
QHttpPart part;
part.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
part.setHeader(QNetworkRequest::ContentDispositionHeader, dispositionHeader);
part.setBodyDevice(file);
multiPart.append(part);
}
QEventLoop loop;
QNetworkReply* reply = manager.post(request,&multiPart);
_cache.append(reply);
connect(&manager,&QNetworkAccessManager::finished,this,[&loop](QNetworkReply *reply){
loop.quit();
});
connect(reply,&QNetworkReply::uploadProgress,this,[=](qint64 bytesSent, qint64 bytesTotal){
onUploadProgress(callable,bytesSent,bytesTotal);
});
loop.exec();
QString result = QString::fromUtf8(reply->readAll());
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString errorString = reply->errorString();
bool isSuccess = reply->error() == QNetworkReply::NoError;
_cache.removeOne(reply);
reply->deleteLater();
reply = nullptr;
if (isSuccess) {
onSuccess(callable,result);
}else{
onError(callable,status,errorString,result);
}
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,QString result){
QJSValue onError = callable.property("onError");
QJSValueList args;
args<<status<<errorString<<result;
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);});
}
void FluHttp::onUploadProgress(const QJSValue& callable,qint64 sent, qint64 total){
QJSValueList args;
args<<static_cast<double>(sent);
args<<static_cast<double>(total);
QJSValue onUploadProgress = callable.property("onUploadProgress");
MainThread::post([=](){onUploadProgress.call(args);});
}

42
src/FluHttp.h Normal file
View File

@ -0,0 +1,42 @@
#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,QString result);
void onSuccess(const QJSValue& callable,QString result);
void onDownloadProgress(const QJSValue& callable,qint64 recv, qint64 total);
void onUploadProgress(const QJSValue& callable,qint64 recv, qint64 total);
public:
explicit FluHttp(QObject *parent = nullptr);
~FluHttp();
//神坑!!! 如果参数使用QVariantMap会有问题在6.4.3版本中QML一调用就会编译失败。所以改用QMap<QString, QVariant>
Q_INVOKABLE void get(QString url,QJSValue callable,QMap<QString, QVariant> params= {},QMap<QString, QVariant> headers = {});
Q_INVOKABLE void post(QString url,QJSValue callable,QMap<QString, QVariant> params= {},QMap<QString, QVariant> headers = {});
Q_INVOKABLE void postString(QString url,QJSValue callable,QString params = "",QMap<QString, QVariant> headers = {});
Q_INVOKABLE void postJson(QString url,QJSValue callable,QMap<QString, QVariant> params = {},QMap<QString, QVariant> headers = {});
Q_INVOKABLE void download(QString url,QJSValue callable,QString filePath,QMap<QString, QVariant> params = {},QMap<QString, QVariant> headers = {});
Q_INVOKABLE void upload(QString url,QJSValue callable,QMap<QString, QVariant> params = {},QMap<QString, QVariant> headers = {});
Q_INVOKABLE void 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

View File

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

View File

@ -3,6 +3,9 @@
#include <QClipboard> #include <QClipboard>
#include <QUuid> #include <QUuid>
#include <QCursor> #include <QCursor>
#include <QScreen>
#include <QFileInfo>
#include <QTextDocument>
FluTools* FluTools::m_instance = nullptr; FluTools* FluTools::m_instance = nullptr;
@ -94,3 +97,29 @@ void FluTools::deleteItem(QObject *p){
p = nullptr; p = nullptr;
} }
} }
QString FluTools::toLocalPath(const QUrl& url){
return url.toLocalFile();
}
QString FluTools::getFileNameByUrl(const QUrl& url){
return QFileInfo(url.toLocalFile()).fileName();
}
QString FluTools::html2PlantText(const QString& html){
QTextDocument textDocument;
textDocument.setHtml(html);
return textDocument.toPlainText();
}
QRect FluTools::getVirtualGeometry(){
return qApp->primaryScreen()->virtualGeometry();
}
QString FluTools::getApplicationDirPath(){
return qApp->applicationDirPath();
}
QUrl FluTools::getUrlByFilePath(const QString& path){
return QUrl::fromLocalFile(path);
}

View File

@ -23,6 +23,37 @@ public:
return getInstance(); return getInstance();
} }
static FluTools *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 将字符串添加到剪切板 * @brief clipText 将字符串添加到剪切板
* @param text * @param text
@ -40,25 +71,71 @@ public:
* @param fileName * @param fileName
* @return * @return
*/ */
Q_INVOKABLE QString readFile(const QString &fileName); Q_INVOKABLE QString readFile(const QString& fileName);
Q_INVOKABLE bool isMacos();
Q_INVOKABLE bool isLinux();
Q_INVOKABLE bool isWin();
/**
* @brief setQuitOnLastWindowClosed 设置关闭最后一个窗口是否退出程序
* @param val
*/
Q_INVOKABLE void setQuitOnLastWindowClosed(bool val); Q_INVOKABLE void setQuitOnLastWindowClosed(bool val);
Q_INVOKABLE int qtMajor(); /**
* @brief setOverrideCursor 设置全局鼠标样式
Q_INVOKABLE int qtMinor(); * @param shape
*/
Q_INVOKABLE void setOverrideCursor(Qt::CursorShape shape); Q_INVOKABLE void setOverrideCursor(Qt::CursorShape shape);
/**
* @brief restoreOverrideCursor 还原全局鼠标样式
*/
Q_INVOKABLE void restoreOverrideCursor(); Q_INVOKABLE void restoreOverrideCursor();
/**
* @brief html2PlantText 将html转换成纯文本
* @param html
*/
Q_INVOKABLE QString html2PlantText(const QString& html);
/**
* @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); Q_INVOKABLE void deleteItem(QObject *p);
/**
* @brief getFileNameByUrl
* @param url
* @return
*/
Q_INVOKABLE QString getFileNameByUrl(const QUrl& url);
/**
* @brief getVirtualGeometry
* @return
*/
Q_INVOKABLE QRect getVirtualGeometry();
/**
* @brief getApplicationDirPath
* @return
*/
Q_INVOKABLE QString getApplicationDirPath();
/**
* @brief getUrlByFilePath
* @param path
* @return
*/
Q_INVOKABLE QUrl getUrlByFilePath(const QString& path);
}; };
#endif // FLUTOOLS_H #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

57
src/QRCode.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "QRCode.h"
#include "BarcodeFormat.h"
#include "BitMatrix.h"
#include "MultiFormatWriter.h"
using namespace ZXing;
QRCode::QRCode(QQuickItem* parent) : QQuickPaintedItem(parent)
{
color(QColor(0,0,0,255));
bgColor(QColor(255,255,255,255));
size(100);
setWidth(_size);
setHeight(_size);
connect(this,&QRCode::textChanged,this,[=]{update();});
connect(this,&QRCode::colorChanged,this,[=]{update();});
connect(this,&QRCode::bgColorChanged,this,[=]{update();});
connect(this,&QRCode::sizeChanged,this,[=]{
setWidth(_size);
setHeight(_size);
update();
});
}
void QRCode::paint(QPainter* painter)
{
if(_text.isEmpty()){
return;
}
if(_text.length()>1108){
return;
}
painter->save();
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);
}
if (qRed(pixel) == 255 && qGreen(pixel) == 255 && qBlue(pixel) == 255) {
rgbImage.setPixelColor(x, y, _bgColor);
}
}
}
painter->drawImage(QRect(0, 0, static_cast<int>(width()), static_cast<int>(height())), rgbImage);
painter->restore();
}

23
src/QRCode.h Normal file
View File

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

73
src/Screenshot.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "Screenshot.h"
#include <QGuiApplication>
#include <QScreen>
#include <QQuickWindow>
#include <QDir>
#include <Def.h>
#include <QtMath>
#include <QThreadPool>
Screenshot::Screenshot(QQuickItem* parent) : QQuickPaintedItem(parent)
{
_desktopGeometry = qApp->primaryScreen()->virtualGeometry();
maskColor(QColor(0,0,0,80));
start(QPoint(0,0));
end(QPoint(0,0));
connect(this,&Screenshot::startChanged,this,[=]{update();});
connect(this,&Screenshot::endChanged,this,[=]{update();});
}
void Screenshot::paint(QPainter* painter)
{
painter->save();
painter->fillRect(_desktopGeometry,_maskColor);
painter->setCompositionMode(QPainter::CompositionMode_Clear);
painter->fillRect(QRect(_start.x(),_start.y(),_end.x()-_start.x(),_end.y()-_start.y()), Qt::black);
painter->restore();
}
ScreenshotBackground::ScreenshotBackground(QQuickItem* parent) : QQuickPaintedItem(parent)
{
_devicePixelRatio = qApp->primaryScreen()->devicePixelRatio();
_desktopGeometry = qApp->primaryScreen()->virtualGeometry();
_desktopPixmap = qApp->primaryScreen()->grabWindow(0, _desktopGeometry.x(), _desktopGeometry.y(), _desktopGeometry.width(), _desktopGeometry.height());
int w = qApp->primaryScreen()->geometry().width();
int h = qApp->primaryScreen()->geometry().height();
foreach (auto item, qApp->screens()) {
if(item != qApp->primaryScreen()){
w = w + item->geometry().width()/qApp->primaryScreen()->devicePixelRatio();
}
}
setWidth(w);
setHeight(h);
}
void ScreenshotBackground::paint(QPainter* painter)
{
painter->save();
_sourcePixmap = _desktopPixmap.copy();
painter->drawPixmap(_desktopGeometry,_sourcePixmap);
painter->restore();
}
void ScreenshotBackground::capture(const QPoint& start,const QPoint& end){
auto pixelRatio = qApp->primaryScreen()->devicePixelRatio();
auto x = qMin(start.x(),end.x()) * pixelRatio;
auto y = qMin(start.y(),end.y()) * pixelRatio;
auto w = qAbs(end.x()-start.x()) * pixelRatio;
auto h = qAbs(end.y()-start.y()) * pixelRatio;
_captureRect = QRect(x,y,w,h);
if(_captureMode == FluScreenshotType::CaptrueMode::Pixmap){
Q_EMIT captrueToPixmapCompleted(_sourcePixmap.copy(_captureRect));
}
if(_captureMode == FluScreenshotType::CaptrueMode::File){
QDir dir = _saveFolder;
if (!dir.exists(_saveFolder)){
dir.mkpath(_saveFolder);
}
auto filePath = _saveFolder.append("/").append(QString::number(QDateTime::currentDateTime().toMSecsSinceEpoch())).append(".png");
_sourcePixmap.copy(_captureRect).save(filePath);
Q_EMIT captrueToFileCompleted(QUrl::fromLocalFile(filePath));
}
}

47
src/Screenshot.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef SCREENSHOT_H
#define SCREENSHOT_H
#include <QQuickItem>
#include <QQuickPaintedItem>
#include <QPainter>
#include <QQuickItemGrabResult>
#include "stdafx.h"
class ScreenshotBackground : public QQuickPaintedItem
{
Q_OBJECT;
QML_NAMED_ELEMENT(ScreenshotBackground)
Q_PROPERTY_AUTO(QString,saveFolder);
Q_PROPERTY_AUTO(int,captureMode);
public:
ScreenshotBackground(QQuickItem* parent = nullptr);
void paint(QPainter* painter) override;
Q_INVOKABLE void capture(const QPoint& start,const QPoint& end);
Q_SIGNAL void captrueToPixmapCompleted(QPixmap captrue);
Q_SIGNAL void captrueToFileCompleted(QUrl captrue);
private:
QRect _desktopGeometry;
QPixmap _desktopPixmap;
QPixmap _sourcePixmap;
qreal _devicePixelRatio;
QSharedPointer<QQuickItemGrabResult> _grabResult;
QRect _captureRect;
};
class Screenshot : public QQuickPaintedItem
{
Q_OBJECT
QML_NAMED_ELEMENT(Screenshot)
Q_PROPERTY_AUTO(QPoint,start);
Q_PROPERTY_AUTO(QPoint,end);
Q_PROPERTY_AUTO(QColor,maskColor);
public:
Screenshot(QQuickItem* parent = nullptr);
void paint(QPainter* painter) override;
private:
QRect _desktopGeometry;
};
#endif // SCREENSHOT_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 Qt5Compat.GraphicalEffects
import FluentUI import FluentUI
Item { FluItem {
id: control id: control
property alias color: rect.color property color tintColor: Qt.rgba(1,1,1,1)
property alias acrylicOpacity: rect.opacity property real tintOpacity: 0.65
property alias radius:bg.radius property real luminosity: 0.01
property alias blurRadius: blur.radius property real noiseOpacity : 0.066
property int rectX: control.x property alias target : effect_source.sourceItem
property int rectY: control.y property int blurRadius: 32
property var sourceItem: control.parent property rect targetRect : Qt.rect(control.x, control.y, control.width, control.height)
FluRectangle{ ShaderEffectSource {
id:bg id: effect_source
anchors.fill: parent anchors.fill: parent
radius: [8,8,8,8] visible: false
ShaderEffectSource { sourceRect: control.targetRect
id: effect_source }
anchors.fill: parent FastBlur {
sourceItem: control.sourceItem id:fast_blur
sourceRect: Qt.rect(rectX, rectY, control.width, control.height) anchors.fill: parent
Rectangle { source: effect_source
id: rect radius: control.blurRadius
anchors.fill: parent }
color: "white" Rectangle{
opacity: 0.5 anchors.fill: parent
} color: Qt.rgba(1, 1, 1, luminosity)
} }
FastBlur { Rectangle{
id:blur anchors.fill: parent
radius: 50 color: Qt.rgba(tintColor.r, tintColor.g, tintColor.b, tintOpacity)
anchors.fill: effect_source }
source: effect_source 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(){ property var darkClickListener: function(){
if(FluTheme.dark){ if(FluTheme.dark){
FluTheme.darkMode = FluDarkMode.Light FluTheme.darkMode = FluThemeType.Light
}else{ }else{
FluTheme.darkMode = FluDarkMode.Dark FluTheme.darkMode = FluThemeType.Dark
} }
} }
id:root id:root

View File

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

View File

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

View File

@ -2,102 +2,170 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import FluentUI import FluentUI
Item { FluItem {
property bool flagXChanged: true
property int radius : 5
property int loopTime: 2000 property int loopTime: 2000
property var model
property Component delegate
property bool showIndicator: true 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 id:control
width: 400 width: 400
height: 300 height: 300
ListModel{ ListModel{
id:content_model id:content_model
} }
FluRectangle{ QtObject{
anchors.fill: parent id:d
radius: [control.radius,control.radius,control.radius,control.radius] property bool flagXChanged: true
FluShadow{ function setData(data){
radius:control.radius 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 ListView{
anchors.fill: parent id:list_view
snapMode: ListView.SnapOneItem anchors.fill: parent
clip: true snapMode: ListView.SnapOneItem
boundsBehavior: ListView.StopAtBounds clip: true
model:content_model boundsBehavior: ListView.StopAtBounds
maximumFlickVelocity: 4 * (list_view.orientation === Qt.Horizontal ? width : height) model:content_model
preferredHighlightBegin: 0 maximumFlickVelocity: 4 * (list_view.orientation === Qt.Horizontal ? width : height)
preferredHighlightEnd: 0 preferredHighlightBegin: 0
highlightMoveDuration: 0 preferredHighlightEnd: 0
orientation : ListView.Horizontal highlightMoveDuration: 0
delegate: Item{ Component.onCompleted: {
width: ListView.view.width d.setData(control.model)
height: ListView.view.height }
property int displayIndex: { Connections{
if(index === 0) target: control
return content_model.count-3 function onModelChanged(){
if(index === content_model.count-1) d.setData(control.model)
return 0
return index-1
}
Image {
anchors.fill: parent
source: model.url
asynchronous: true
fillMode:Image.PreserveAspectCrop
}
} }
onMovementEnded:{ }
currentIndex = list_view.contentX/list_view.width orientation : ListView.Horizontal
if(currentIndex === 0){ delegate: Item{
currentIndex = list_view.count-2 id:item_control
}else if(currentIndex === list_view.count-1){ width: ListView.view.width
currentIndex = 1 height: ListView.view.height
} property int displayIndex: {
flagXChanged = false if(index === 0)
timer_run.start() return content_model.count-3
if(index === content_model.count-1)
return 0
return index-1
} }
onMovementStarted: { Loader{
flagXChanged = true property int displayIndex : item_control.displayIndex
timer_run.stop() property var model: list_view.model.get(index)
} anchors.fill: parent
onContentXChanged: { sourceComponent: {
if(flagXChanged){ if(model){
var maxX = Math.min(list_view.width*(currentIndex+1),list_view.count*list_view.width) return control.delegate
var minY = Math.max(0,(list_view.width*(currentIndex-1)))
if(contentX>=maxX){
contentX = maxX
}
if(contentX<=minY){
contentX = minY
} }
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{ Row{
spacing: 10 id:layout_indicator
spacing: control.indicatorSpacing
anchors{ anchors{
horizontalCenter: parent.horizontalCenter horizontalCenter:(indicatorGravity & Qt.AlignHCenter) ? parent.horizontalCenter : undefined
bottom: parent.bottom verticalCenter: (indicatorGravity & Qt.AlignVCenter) ? parent.verticalCenter : undefined
bottomMargin: 20 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 visible: showIndicator
Repeater{ Repeater{
id:repeater_indicator
model: list_view.count model: list_view.count
Rectangle{ Loader{
width: 8 property int displayIndex: {
height: 8 if(index === 0)
radius: 4 return list_view.count-3
visible: { if(index === list_view.count-1)
if(index===0 || index===list_view.count-1) return 0
return false return index-1
return true }
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() timer_anim.start()
} }
} }
function setData(data){
content_model.clear() function changedIndex(index){
content_model.append(data[data.length-1]) d.flagXChanged = true
content_model.append(data) timer_run.stop()
content_model.append(data[0]) list_view.currentIndex = index
list_view.currentIndex = 1 d.flagXChanged = false
timer_run.restart() 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,17 +11,26 @@ Button{
height: 36 height: 36
implicitWidth: width implicitWidth: width
implicitHeight: height implicitHeight: height
property alias colorValue: container.colorValue
background: background:
Rectangle{ Rectangle{
id:layout_color id:layout_color
radius: 5 radius: 5
color: container.colorValue color:"#00000000"
border.color: { border.color: {
if(hovered) if(hovered)
return FluTheme.primaryColor.light return FluTheme.primaryColor.light
return FluTheme.dark ? Qt.rgba(45/255,45/255,45/255,1) : Qt.rgba(226/255,229/255,234/255,1) return FluTheme.dark ? Qt.rgba(100/255,100/255,100/255,1) : Qt.rgba(200/255,200/255,200/255,1)
} }
border.width: 1 border.width: 1
Rectangle{
anchors.fill: parent
anchors.margins: 4
radius: 5
color: container.colorValue
}
} }
contentItem: Item{} contentItem: Item{}
onClicked: { onClicked: {
@ -75,4 +84,7 @@ Button{
popup.open() popup.open()
} }
} }
function setColor(color){
container.setColor(color)
}
} }

View File

@ -6,7 +6,7 @@ import QtQuick.Templates as T
ComboBox { ComboBox {
id: control id: control
signal commit signal commit(string text)
property bool disabled: false property bool disabled: false
property color normalColor: FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(254/255,254/255,254/255,1) property color normalColor: FluTheme.dark ? Qt.rgba(62/255,62/255,62/255,1) : Qt.rgba(254/255,254/255,254/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 hoverColor: FluTheme.dark ? Qt.rgba(68/255,68/255,68/255,1) : Qt.rgba(251/255,251/255,251/255,1)
@ -71,7 +71,7 @@ ComboBox {
Keys.onEnterPressed: (event)=> handleCommit(event) Keys.onEnterPressed: (event)=> handleCommit(event)
Keys.onReturnPressed:(event)=> handleCommit(event) Keys.onReturnPressed:(event)=> handleCommit(event)
function handleCommit(event){ function handleCommit(event){
control.commit() control.commit(control.editText)
} }
} }

View File

@ -15,23 +15,13 @@ FluPopup {
signal neutralClicked signal neutralClicked
signal negativeClicked signal negativeClicked
signal positiveClicked signal positiveClicked
enum ButtonFlag{ property int buttonFlags: FluContentDialogType.NegativeButton | FluContentDialogType.PositiveButton
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)
}
focus: true focus: true
implicitWidth: 400
implicitHeight: text_title.height + text_message.height + layout_actions.height
Rectangle { Rectangle {
id:layout_content id:layout_content
anchors.fill: parent anchors.fill: parent
implicitWidth:minWidth
implicitHeight: text_title.height + text_message.height + layout_actions.height
color: 'transparent' color: 'transparent'
radius:5 radius:5
FluText{ FluText{
@ -67,7 +57,7 @@ FluPopup {
id:layout_actions id:layout_actions
height: 68 height: 68
radius: 5 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{ anchors{
top:text_message.bottom top:text_message.bottom
left: parent.left left: parent.left
@ -85,11 +75,11 @@ FluPopup {
id:neutral_btn id:neutral_btn
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
visible: popup.buttonFlags&FluContentDialog.NeutralButton visible: popup.buttonFlags&FluContentDialogType.NeutralButton
text: neutralText text: neutralText
onClicked: { onClicked: {
popup.close() popup.close()
timer_delay.targetFlags = FluContentDialog.NeutralButton timer_delay.targetFlags = FluContentDialogType.NeutralButton
timer_delay.restart() timer_delay.restart()
} }
} }
@ -97,11 +87,11 @@ FluPopup {
id:negative_btn id:negative_btn
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
visible: popup.buttonFlags&FluContentDialog.NegativeButton visible: popup.buttonFlags&FluContentDialogType.NegativeButton
text: negativeText text: negativeText
onClicked: { onClicked: {
popup.close() popup.close()
timer_delay.targetFlags = FluContentDialog.NegativeButton timer_delay.targetFlags = FluContentDialogType.NegativeButton
timer_delay.restart() timer_delay.restart()
} }
} }
@ -109,11 +99,11 @@ FluPopup {
id:positive_btn id:positive_btn
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
visible: popup.buttonFlags&FluContentDialog.PositiveButton visible: popup.buttonFlags&FluContentDialogType.PositiveButton
text: positiveText text: positiveText
onClicked: { onClicked: {
popup.close() popup.close()
timer_delay.targetFlags = FluContentDialog.PositiveButton timer_delay.targetFlags = FluContentDialogType.PositiveButton
timer_delay.restart() timer_delay.restart()
} }
} }
@ -125,13 +115,13 @@ FluPopup {
id:timer_delay id:timer_delay
interval: popup.delayTime interval: popup.delayTime
onTriggered: { onTriggered: {
if(targetFlags === FluContentDialog.NegativeButton){ if(targetFlags === FluContentDialogType.NegativeButton){
negativeClicked() negativeClicked()
} }
if(targetFlags === FluContentDialog.NeutralButton){ if(targetFlags === FluContentDialogType.NeutralButton){
neutralClicked() neutralClicked()
} }
if(targetFlags === FluContentDialog.PositiveButton){ if(targetFlags === FluContentDialogType.PositiveButton){
positiveClicked() positiveClicked()
} }
} }

View File

@ -11,6 +11,17 @@ FluPage {
property int topPadding: 0 property int topPadding: 0
property int rightPadding: 10 property int rightPadding: 10
property int bottomPadding: 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 id:control
FluText{ FluText{
id:text_title id:text_title
@ -26,9 +37,11 @@ FluPage {
rightMargin: control.rightPadding rightMargin: control.rightPadding
} }
} }
Item{ FluStatusView{
clip: true id:status_view
id:container color: "#00000000"
statusMode: FluStatusViewType.Success
onErrorClicked: control.errorClicked()
anchors{ anchors{
left: parent.left left: parent.left
right: parent.right right: parent.right
@ -38,5 +51,10 @@ FluPage {
rightMargin: control.rightPadding rightMargin: control.rightPadding
bottomMargin: control.bottomPadding bottomMargin: control.bottomPadding
} }
Item{
clip: true
id:container
anchors.fill: parent
}
} }
} }

View File

@ -3,5 +3,16 @@ import QtQuick.Window
import FluentUI import FluentUI
Rectangle { 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 { contentItem: FluText {
id:title id:title
text: control.text text: control.text
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
color: control.textColor 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

@ -13,6 +13,7 @@ Button {
property color pressedColor: FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06) property color pressedColor: FluTheme.dark ? Qt.rgba(1,1,1,0.06) : Qt.rgba(0,0,0,0.06)
property color normalColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0) property color normalColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
property color disableColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0) property color disableColor: FluTheme.dark ? Qt.rgba(0,0,0,0) : Qt.rgba(0,0,0,0)
property Component iconDelegate: com_icon
property color color: { property color color: {
if(!enabled){ if(!enabled){
return disableColor return disableColor
@ -54,16 +55,22 @@ Button {
visible: control.activeFocus visible: control.activeFocus
} }
} }
contentItem: Item{ Component{
id:com_icon
FluIcon { FluIcon {
id:text_icon id:text_icon
font.pixelSize: iconSize font.pixelSize: iconSize
iconSize: control.iconSize iconSize: control.iconSize
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
anchors.centerIn: parent
iconColor: control.iconColor iconColor: control.iconColor
iconSource: control.iconSource; iconSource: control.iconSource
}
}
contentItem: Item{
Loader{
anchors.centerIn: parent
sourceComponent: iconDelegate
} }
FluTooltip{ FluTooltip{
id:tool_tip id:tool_tip

View File

@ -4,7 +4,7 @@ import QtQuick.Controls.Basic
import FluentUI import FluentUI
TextArea{ TextArea{
signal commit signal commit(string text)
property bool disabled: false property bool disabled: false
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 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 disableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
@ -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 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 color placeholderDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
id:control id:control
width: 300
enabled: !disabled enabled: !disabled
color: { color: {
if(!enabled){ if(!enabled){
@ -37,7 +36,11 @@ TextArea{
return placeholderNormalColor return placeholderNormalColor
} }
selectByMouse: true selectByMouse: true
background: FluTextBoxBackground{ inputItem: control } width: background.implicitWidth
background: FluTextBoxBackground{
inputItem: control
implicitWidth: 240
}
Keys.onEnterPressed: (event)=> d.handleCommit(event) Keys.onEnterPressed: (event)=> d.handleCommit(event)
Keys.onReturnPressed:(event)=> d.handleCommit(event) Keys.onReturnPressed:(event)=> d.handleCommit(event)
QtObject{ QtObject{
@ -47,7 +50,7 @@ TextArea{
insert(control.cursorPosition, "\n") insert(control.cursorPosition, "\n")
return return
} }
control.commit() control.commit(control.text)
} }
} }
MouseArea{ MouseArea{

View File

@ -6,39 +6,31 @@ import QtQuick.Layouts
import FluentUI import FluentUI
Item { Item {
enum DisplayMode {
Open = 0,
Compact = 1,
Minimal = 2,
Auto = 3
}
enum PageMode {
Stack = 0,
NoStack = 1
}
property url logo property url logo
property string title: "" property string title: ""
property FluObject items property FluObject items
property FluObject footerItems property FluObject footerItems
property int displayMode: FluNavigationView.Auto property int displayMode: FluNavigationViewType.Auto
property Component autoSuggestBox property Component autoSuggestBox
property Component actionItem property Component actionItem
property int topPadding: 0 property int topPadding: 0
property int navWidth: 300 property int navWidth: 300
property int pageMode: FluNavigationView.Stack property int pageMode: FluNavigationViewType.Stack
property FluMenu navItemRightMenu
property FluMenu navItemExpanderRightMenu
signal logoClicked signal logoClicked
id:control id:control
QtObject{ Item{
id:d id:d
property bool animDisabled:false property bool animDisabled:false
property var stackItems: [] property var stackItems: []
property int displayMode: control.displayMode property int displayMode: control.displayMode
property bool enableNavigationPanel: false property bool enableNavigationPanel: false
property bool isCompact: d.displayMode === FluNavigationView.Compact property bool isCompact: d.displayMode === FluNavigationViewType.Compact
property bool isMinimal: d.displayMode === FluNavigationView.Minimal property bool isMinimal: d.displayMode === FluNavigationViewType.Minimal
property bool isCompactAndPanel: d.displayMode === FluNavigationView.Compact && d.enableNavigationPanel property bool isCompactAndPanel: d.displayMode === FluNavigationViewType.Compact && d.enableNavigationPanel
property bool isCompactAndNotPanel:d.displayMode === FluNavigationView.Compact && !d.enableNavigationPanel property bool isCompactAndNotPanel:d.displayMode === FluNavigationViewType.Compact && !d.enableNavigationPanel
property bool isMinimalAndPanel: d.displayMode === FluNavigationView.Minimal && d.enableNavigationPanel property bool isMinimalAndPanel: d.displayMode === FluNavigationViewType.Minimal && d.enableNavigationPanel
onIsCompactAndNotPanelChanged: { onIsCompactAndNotPanelChanged: {
collapseAll() collapseAll()
} }
@ -79,15 +71,15 @@ Item {
} }
Component.onCompleted: { Component.onCompleted: {
d.displayMode = Qt.binding(function(){ d.displayMode = Qt.binding(function(){
if(control.displayMode !==FluNavigationView.Auto){ if(control.displayMode !==FluNavigationViewType.Auto){
return control.displayMode return control.displayMode
} }
if(control.width<=700){ if(control.width<=700){
return FluNavigationView.Minimal return FluNavigationViewType.Minimal
}else if(control.width<=900){ }else if(control.width<=900){
return FluNavigationView.Compact return FluNavigationViewType.Compact
}else{ }else{
return FluNavigationView.Open return FluNavigationViewType.Open
} }
}) })
timer_anim_delay.restart() timer_anim_delay.restart()
@ -102,12 +94,10 @@ Item {
Connections{ Connections{
target: d target: d
function onDisplayModeChanged(){ function onDisplayModeChanged(){
if(d.displayMode === FluNavigationView.Compact){ if(d.displayMode === FluNavigationViewType.Compact){
collapseAll() collapseAll()
} }
if(d.displayMode === FluNavigationView.Minimal){ d.enableNavigationPanel = false
d.enableNavigationPanel = false
}
} }
} }
Component{ Component{
@ -120,11 +110,12 @@ Item {
id:com_panel_item_separatorr id:com_panel_item_separatorr
FluDivider{ FluDivider{
width: layout_list.width width: layout_list.width
height: { spacing: model.spacing
separatorHeight: {
if(model.parent){ if(model.parent){
return model.parent.isExpand ? 1 : 0 return model.parent.isExpand ? model.size : 0
} }
return 1 return model.size
} }
} }
} }
@ -172,6 +163,20 @@ Item {
leftMargin: 6 leftMargin: 6
rightMargin: 6 rightMargin: 6
} }
MouseArea{
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: function(mouse){
if (mouse.button === Qt.RightButton) {
if(model.menuDelegate){
loader_item_menu.sourceComponent = model.menuDelegate
loader_item_menu.item.popup()
}
}
}
z:-100
}
onClicked: { onClicked: {
if(d.isCompactAndNotPanel){ if(d.isCompactAndNotPanel){
control_popup.showPopup(Qt.point(50,mapToItem(control,0,0).y),model.children) control_popup.showPopup(Qt.point(50,mapToItem(control,0,0).y),model.children)
@ -194,7 +199,6 @@ Item {
} }
visible: { visible: {
if(!model.isExpand){ if(!model.isExpand){
for(var i=0;i<model.children.length;i++){ for(var i=0;i<model.children.length;i++){
var item = model.children[i] var item = model.children[i]
if(item.infoBadge && item.count !==0){ if(item.infoBadge && item.count !==0){
@ -322,6 +326,36 @@ Item {
return FluTheme.dark ? FluColors.White : FluColors.Grey220 return FluTheme.dark ? FluColors.White : FluColors.Grey220
} }
} }
Loader{
id:item_edit_loader
anchors{
top: parent.top
bottom: parent.bottom
left: item_title.left
right: item_title.right
rightMargin: 8
}
sourceComponent: model.showEdit ? model.editDelegate : undefined
onStatusChanged: {
if(status === Loader.Ready){
item.forceActiveFocus()
item_connection_edit_focus.target = item
}
}
Connections{
id:item_connection_edit_focus
ignoreUnknownSignals:true
function onActiveFocusChanged(focus){
if(focus === false){
model.showEdit = false
}
}
function onCommit(text){
model.title = text
model.showEdit = false
}
}
}
} }
} }
} }
@ -361,6 +395,19 @@ Item {
leftMargin: 6 leftMargin: 6
rightMargin: 6 rightMargin: 6
} }
MouseArea{
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: function(mouse){
if (mouse.button === Qt.RightButton) {
if(model.menuDelegate){
loader_item_menu.sourceComponent = model.menuDelegate
loader_item_menu.item.popup();
}
}
}
z:-100
}
onClicked: { onClicked: {
if(type === 0){ if(type === 0){
if(model.tapFunc){ if(model.tapFunc){
@ -473,6 +520,36 @@ Item {
right: item_dot_loader.left right: item_dot_loader.left
} }
} }
Loader{
id:item_edit_loader
anchors{
top: parent.top
bottom: parent.bottom
left: item_title.left
right: item_title.right
rightMargin: 8
}
sourceComponent: model.showEdit ? model.editDelegate : undefined
onStatusChanged: {
if(status === Loader.Ready){
item.forceActiveFocus()
item_connection_edit_focus.target = item
}
}
Connections{
id:item_connection_edit_focus
ignoreUnknownSignals:true
function onActiveFocusChanged(focus){
if(focus === false){
model.showEdit = false
}
}
function onCommit(text){
model.title = text
model.showEdit = false
}
}
}
Loader{ Loader{
id:item_dot_loader id:item_dot_loader
property bool isDot: (item_dot_loader.item&&item_dot_loader.item.isDot) property bool isDot: (item_dot_loader.item&&item_dot_loader.item.isDot)
@ -533,11 +610,11 @@ Item {
layout_footer.currentIndex = item._idx-(nav_list.count-layout_footer.count) layout_footer.currentIndex = item._idx-(nav_list.count-layout_footer.count)
} }
nav_list.currentIndex = item._idx nav_list.currentIndex = item._idx
if(pageMode === FluNavigationView.Stack){ if(pageMode === FluNavigationViewType.Stack){
var nav_stack = loader_content.item.navStack() var nav_stack = loader_content.item.navStack()
var nav_stack2 = loader_content.item.navStack2() var nav_stack2 = loader_content.item.navStack2()
nav_stack.pop() nav_stack.pop()
if(nav_stack.currentItem.launchMode === FluPage.SingleInstance){ if(nav_stack.currentItem.launchMode === FluPageType.SingleInstance){
var url = nav_stack.currentItem.url var url = nav_stack.currentItem.url
var pageIndex = -1 var pageIndex = -1
for(var i=0;i<nav_stack2.children.length;i++){ for(var i=0;i<nav_stack2.children.length;i++){
@ -551,7 +628,7 @@ Item {
nav_stack2.currentIndex = pageIndex nav_stack2.currentIndex = pageIndex
} }
} }
}else if(pageMode === FluNavigationView.NoStack){ }else if(pageMode === FluNavigationViewType.NoStack){
loader_content.setSource(item._ext.url,item._ext.argument) loader_content.setSource(item._ext.url,item._ext.argument)
} }
} }
@ -646,7 +723,7 @@ Item {
id:nav_stack2 id:nav_stack2
anchors.fill: nav_stack anchors.fill: nav_stack
clip: true clip: true
visible: nav_stack.currentItem?.launchMode === FluPage.SingleInstance visible: nav_stack.currentItem?.launchMode === FluPageType.SingleInstance
} }
function navStack(){ function navStack(){
return nav_stack return nav_stack
@ -686,6 +763,7 @@ Item {
MouseArea{ MouseArea{
anchors.fill: parent anchors.fill: parent
visible: d.isMinimalAndPanel||d.isCompactAndPanel visible: d.isMinimalAndPanel||d.isCompactAndPanel
hoverEnabled: true
onWheel: { onWheel: {
} }
onClicked: { onClicked: {
@ -707,7 +785,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.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 border.width: d.isMinimal || d.isCompactAndPanel ? 1 : 0
color: { 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 FluTheme.dark ? Qt.rgba(61/255,61/255,61/255,1) : Qt.rgba(243/255,243/255,243/255,1)
} }
return "transparent" return "transparent"
@ -728,24 +806,10 @@ Item {
} }
} }
visible: { visible: {
if(d.displayMode !== FluNavigationView.Minimal) if(d.displayMode !== FluNavigationViewType.Minimal)
return true return true
return d.isMinimalAndPanel ? true : false 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{ Item{
id:layout_header id:layout_header
width: layout_list.width width: layout_list.width
@ -1002,10 +1066,13 @@ Item {
control_popup.open() control_popup.open()
} }
} }
Loader{
id:loader_item_menu
}
Component{ Component{
id:com_placeholder id:com_placeholder
Item{ Item{
property int launchMode: FluPage.SingleInstance property int launchMode: FluPageType.SingleInstance
property string url property string url
} }
} }
@ -1031,12 +1098,12 @@ Item {
return nav_list.currentIndex return nav_list.currentIndex
} }
function getCurrentUrl(){ function getCurrentUrl(){
if(pageMode === FluNavigationView.Stack){ if(pageMode === FluNavigationViewType.Stack){
var nav_stack = loader_content.item.navStack() var nav_stack = loader_content.item.navStack()
if(nav_stack.currentItem){ if(nav_stack.currentItem){
return nav_stack.currentItem.url return nav_stack.currentItem.url
} }
}else if(pageMode === FluNavigationView.NoStack){ }else if(pageMode === FluNavigationViewType.NoStack){
return loader_content.source.toString() return loader_content.source.toString()
} }
return undefined return undefined
@ -1051,19 +1118,19 @@ Item {
if(page){ if(page){
switch(page.launchMode) switch(page.launchMode)
{ {
case FluPage.SingleTask: case FluPageType.SingleTask:
while(nav_stack.currentItem !== page) while(nav_stack.currentItem !== page)
{ {
nav_stack.pop() nav_stack.pop()
d.stackItems = d.stackItems.slice(0, -1) d.stackItems = d.stackItems.slice(0, -1)
} }
return return
case FluPage.SingleTop: case FluPageType.SingleTop:
if (nav_stack.currentItem.url === url){ if (nav_stack.currentItem.url === url){
return return
} }
break break
case FluPage.Standard: case FluPageType.Standard:
default: default:
} }
} }
@ -1083,7 +1150,7 @@ Item {
var comp = Qt.createComponent(url) var comp = Qt.createComponent(url)
if (comp.status === Component.Ready) { if (comp.status === Component.Ready) {
var obj = comp.createObject(nav_stack,options) var obj = comp.createObject(nav_stack,options)
if(obj.launchMode === FluPage.SingleInstance){ if(obj.launchMode === FluPageType.SingleInstance){
nav_stack.push(com_placeholder,options) nav_stack.push(com_placeholder,options)
nav_stack2.children.push(obj) nav_stack2.children.push(obj)
nav_stack2.currentIndex = nav_stack2.count - 1 nav_stack2.currentIndex = nav_stack2.count - 1
@ -1105,9 +1172,9 @@ Item {
obj._ext = {url:url,argument:argument} obj._ext = {url:url,argument:argument}
d.stackItems = d.stackItems.concat(obj) d.stackItems = d.stackItems.concat(obj)
} }
if(pageMode === FluNavigationView.Stack){ if(pageMode === FluNavigationViewType.Stack){
stackPush() stackPush()
}else if(pageMode === FluNavigationView.NoStack){ }else if(pageMode === FluNavigationViewType.NoStack){
noStackPush() noStackPush()
} }
} }
@ -1133,4 +1200,7 @@ Item {
function navButton(){ function navButton(){
return btn_nav return btn_nav
} }
function logoButton(){
return image_logo
}
} }

View File

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

View File

@ -19,4 +19,7 @@ QtObject {
property int count: 0 property int count: 0
signal tap signal tap
property var tapFunc property var tapFunc
property Component menuDelegate
property Component editDelegate
property bool showEdit
} }

View File

@ -10,4 +10,7 @@ FluObject {
property Component cusIcon property Component cusIcon
property bool isExpand: false property bool isExpand: false
property var parent property var parent
property Component menuDelegate
property Component editDelegate
property bool showEdit
} }

View File

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

View File

@ -4,7 +4,7 @@ import QtQuick.Controls.Basic
import FluentUI import FluentUI
TextField{ TextField{
signal commit signal commit(string text)
property bool disabled: false property bool disabled: false
property int iconSource: 0 property int iconSource: 0
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 normalColor: FluTheme.dark ? Qt.rgba(255/255,255/255,255/255,1) : Qt.rgba(27/255,27/255,27/255,1)
@ -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 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 color placeholderDisableColor: FluTheme.dark ? Qt.rgba(131/255,131/255,131/255,1) : Qt.rgba(160/255,160/255,160/255,1)
id:control id:control
width: 300
enabled: !disabled enabled: !disabled
color: { color: {
if(!enabled){ if(!enabled){
@ -41,6 +40,7 @@ TextField{
rightPadding: icon_end.visible ? 50 : 30 rightPadding: icon_end.visible ? 50 : 30
background: FluTextBoxBackground{ background: FluTextBoxBackground{
inputItem: control inputItem: control
implicitWidth: 240
FluIcon{ FluIcon{
id:icon_end id:icon_end
iconSource: control.iconSource iconSource: control.iconSource
@ -59,7 +59,7 @@ TextField{
QtObject{ QtObject{
id:d id:d
function handleCommit(event){ function handleCommit(event){
control.commit() control.commit(control.text)
} }
} }
FluIconButton{ FluIconButton{

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