diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..135919afd6f9820b063467b69bb7d57aa9c63c3e --- /dev/null +++ b/.clang-format @@ -0,0 +1,152 @@ +BasedOnStyle: Google +IndentWidth: 4 +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Allman +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3f415430d90b361f0b88fda764779862d44b19bf --- /dev/null +++ b/.gitignore @@ -0,0 +1,184 @@ +# Created by .ignore support plugin (hsz.mobi) +### C++ template +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +### CMake template +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +### Example user template template +### Example user template + +# IntelliJ project files +.idea +*.iml +out +gen +### C template +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### Linux template +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Qt template +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.so.* +*.dll +*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.cpp +moc_*.h +qrc_*.cpp +ui_*.h +*.qmlc +*.jsc +Makefile* +*build-* +*.qm +*.prl + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* + +# QtCreator 4.8< compilation database +compile_commands.json + +# QtCreator local machine specific files for imported projects +*creator.user* + +*_qmlcache.qrc + +/build/* + +/.vscode/* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..bf571f2e11b0e762663feb7f0115f275a8ef66d1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.5) + +set(CMAKE_CXX_STANDARD 11) +project(kiran-qt5-integration) + +set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH} ) +list(APPEND ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules/) + +include(GNUInstallDirs) +include(Qt5PluginInstallDirs) + +#共用代码,生成静态库 +add_subdirectory(common) +#QPlatformTheme插件 +add_subdirectory(platformtheme) +#KiranStyle提供的相关接口,也提供给KiranStyle获取颜色表内容的接口 +add_subdirectory(style-helper) +#QStyle插件 +add_subdirectory(style) +add_subdirectory(test) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..9e32cdef1625daed25cf365c865f01050877cff3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,127 @@ + 木兰宽松许可证, 第2版 + + 木兰宽松许可证, 第2版 + 2020年1月 http://license.coscl.org.cn/MulanPSL2 + + + 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: + + 0. 定义 + + “软件”是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 + + “贡献”是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 + + “贡献者”是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 + + “法人实体”是指提交贡献的机构及其“关联实体”。 + + “关联实体”是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 + + 1. 授予版权许可 + + 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。 + + 2. 授予专利许可 + + 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。 + + 3. 无商标许可 + + “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。 + + 4. 分发限制 + + 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 + + 5. 免责声明与责任限制 + + “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。 + + 6. 语言 + “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。 + + 条款结束 + + 如何将木兰宽松许可证,第2版,应用到您的软件 + + 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: + + 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; + + 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; + + 3, 请将如下声明文本放入每个源文件的头部注释中。 + + Copyright (c) [Year] [name of copyright holder] + [Software Name] is licensed under Mulan PSL v2. + You can use this software according to the terms and conditions of the Mulan PSL v2. + You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. + + + Mulan Permissive Software License,Version 2 + + Mulan Permissive Software License,Version 2 (Mulan PSL v2) + January 2020 http://license.coscl.org.cn/MulanPSL2 + + Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions: + + 0. Definition + + Software means the program and related documents which are licensed under this License and comprise all Contribution(s). + + Contribution means the copyrightable work licensed by a particular Contributor under this License. + + Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License. + + Legal Entity means the entity making a Contribution and all its Affiliates. + + Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity. + + 1. Grant of Copyright License + + Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not. + + 2. Grant of Patent License + + Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken. + + 3. No Trademark License + + No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4. + + 4. Distribution Restriction + + You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software. + + 5. Disclaimer of Warranty and Limitation of Liability + + THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + 6. Language + + THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL. + + END OF THE TERMS AND CONDITIONS + + How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software + + To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps: + + i Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner; + + ii Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package; + + iii Attach the statement to the appropriate annotated syntax at the beginning of each source file. + + + Copyright (c) [Year] [name of copyright holder] + [Software Name] is licensed under Mulan PSL v2. + You can use this software according to the terms and conditions of the Mulan PSL v2. + You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. diff --git a/cmake/modules/FindQt5ThemeSupport.cmake b/cmake/modules/FindQt5ThemeSupport.cmake new file mode 100644 index 0000000000000000000000000000000000000000..d5c1753f8631d25aa6c9e793117daa2ca2fad559 --- /dev/null +++ b/cmake/modules/FindQt5ThemeSupport.cmake @@ -0,0 +1,57 @@ +find_package(PkgConfig) +# 提取Qt5Gui.pc中的信息 +pkg_check_modules(PKG_Qt5ThemeSupport QUIET Qt5Gui) + +set(Qt5ThemeSupport_DEFINITIONS ${PKG_Qt5ThemeSupport_CFLAGS_OTHER}) +set(Qt5ThemeSupport_VERSION ${PKG_Qt5ThemeSupport_VERSION}) + +# 拼出Qt5ThemeSupport头文件路径 +find_path(Qt5ThemeSupport_INCLUDE_DIR + NAMES + QtThemeSupport/private/qgenericunixthemes_p.h + HINTS + ${PKG_Qt5ThemeSupport_INCLUDEDIR}/QtThemeSupport/${PKG_Qt5ThemeSupport_VERSION}/ + ) + +# 拼出Qt5ThemeSupport静态库位置 +find_library(Qt5ThemeSupport_LIBRARY + NAMES + Qt5ThemeSupport + HINTS + ${PKG_Qt5ThemeSupport_LIBRARY_DIRS} + ) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Qt5ThemeSupport + FOUND_VAR + Qt5ThemeSupport_FOUND + REQUIRED_VARS + Qt5ThemeSupport_LIBRARY + Qt5ThemeSupport_INCLUDE_DIR + VERSION_VAR + Qt5ThemeSupport_VERSION + ) + +if(Qt5ThemeSupport_FOUND AND NOT TARGET Qt5ThemeSupport::Qt5ThemeSupport) + add_library(Qt5ThemeSupport::Qt5ThemeSupport UNKNOWN IMPORTED) + set_target_properties(Qt5ThemeSupport::Qt5ThemeSupport PROPERTIES + IMPORTED_LOCATION "${Qt5ThemeSupport_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${Qt5ThemeSupport_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${Qt5ThemeSupport_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(Qt5ThemeSupport_LIBRARY Qt5ThemeSupport_INCLUDE_DIR) + +# compatibility variables +set(Qt5ThemeSupport_LIBRARIES ${Qt5ThemeSupport_LIBRARY}) +set(Qt5ThemeSupport_INCLUDE_DIRS ${Qt5ThemeSupport_INCLUDE_DIR}) +set(Qt5ThemeSupport_VERSION_STRING ${Qt5ThemeSupport_VERSION}) + + +include(FeatureSummary) +set_package_properties(Qt5ThemeSupport PROPERTIES + URL "https://www.qt.io" + DESCRIPTION "Qt ThemeSupport module." + ) + diff --git a/cmake/modules/Qt5PluginInstallDirs.cmake b/cmake/modules/Qt5PluginInstallDirs.cmake new file mode 100644 index 0000000000000000000000000000000000000000..5192444ea889ab3b723ee08ca3a7f49b9ea86594 --- /dev/null +++ b/cmake/modules/Qt5PluginInstallDirs.cmake @@ -0,0 +1,8 @@ +find_package(PkgConfig REQUIRED) + +#通过qt5的pkgconfig配置文件取出插件安装位置、插件共享库安装位置 +pkg_search_module(Qt5 REQUIRED Qt5) +pkg_get_variable(QT5_PLUGIN_DIR Qt5 plugindir) + +set(QT5_PLATFORM_THEME_INSTALL_DIR ${QT5_PLUGIN_DIR}/platformthemes/) +set(QT5_STYLE_INSTALL_DIR ${QT5_PLUGIN_DIR}/styles) \ No newline at end of file diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1ee647d50a50602cd2a8dbfa59bcf8366283469a --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,49 @@ +set(TARGET_NAME "common") + +find_package(Qt5 COMPONENTS Gui DBus) +find_package(PkgConfig REQUIRED) +find_package(KiranDBusGenerate REQUIRED) + +pkg_search_module(KIRAN_CC_DAEMON REQUIRED kiran-cc-daemon) +pkg_search_module(KLOG_QT5 REQUIRED klog-qt5) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +#生成使用的DBus接口源文件 +##kiran session appearance proxy +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/data/com.kylinsec.Kiran.SessionDaemon.Appearance.xml + PROPERTIES + CLASSNAME KiranAppearanceProxy + NO_NAMESPACE true) +kiran_qt5_add_dbus_interface(KIRAN_INTERFACE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/data/com.kylinsec.Kiran.SessionDaemon.Appearance.xml kiran_appearance_proxy) +##kiran session display proxy +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/data/com.kylinsec.Kiran.SessionDaemon.Display.xml + PROPERTIES + CLASSNAME KiranDisplayProxy + NO_NAMESPACE true) +kiran_qt5_add_dbus_interface(KIRAN_INTERFACE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/data/com.kylinsec.Kiran.SessionDaemon.Display.xml kiran_display_proxy) +#避免cmake错误 +foreach (interface_item ${KIRAN_INTERFACE_SRC}) + set_property(SOURCE ${interface_item} PROPERTY SKIP_AUTOGEN ON) +endforeach () + +file(GLOB_RECURSE COMMON_H_FILES src/*.h) +file(GLOB_RECURSE COMMON_CPP_FILES src/*.cpp) + +set (ALL_FILES ${COMMON_H_FILES} ${COMMON_CPP_FILES} ${KIRAN_INTERFACE_SRC}) +add_library(${TARGET_NAME} STATIC ${ALL_FILES}) + +target_link_libraries(${TARGET_NAME} + Qt5::Gui + Qt5::DBus + ${QGSETTINGS_LIBRARIES}) + +target_include_directories(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${QGSETTINGS_INCLUDE_DIRS} + ${KIRAN_CC_DAEMON_INCLUDE_DIRS}) + +target_include_directories(${TARGET_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/src) diff --git a/common/data/com.kylinsec.Kiran.SessionDaemon.Appearance.xml b/common/data/com.kylinsec.Kiran.SessionDaemon.Appearance.xml new file mode 100644 index 0000000000000000000000000000000000000000..e17e0516aa599039c808048a264c97c602eb1113 --- /dev/null +++ b/common/data/com.kylinsec.Kiran.SessionDaemon.Appearance.xml @@ -0,0 +1,99 @@ + + + + + + + + The theme type that contains gtk, icon and cursor, etc. + + + The theme info that contain name and path. + + Get the theme info. + + + + + The theme type that contains gtk, icon and cursor, etc. + + + The theme name. + + Set the theme name for the type. + + + + + The theme type that contains gtk, icon and cursor, etc. + + + The theme name. + + Get the theme name for the type. + + + + + The appFont type that contains application appFont, document appFont, desktop appFont and monospace appFont, etc. + + + The appFont that contain name and size, for example 'San 10'. + + Get the appFont for the type. + + + + + The appFont type that contains application appFont, document appFont, desktop appFont and monospace appFont, etc. + + + The appFont, for example 'San 10'. + + Set the appFont for the type. + + + + + File path of desktop background. + + Set the desktop background. + + + + + File path of lock screen background. + + Set the lock screen background. + + + + + The file path of desktop background. + + + + + The file path of lock screen background. + + + + + The theme type. + + + The new theme name. + + + + + + The appFont type. Refer to enum AppearanceFontType in appearance-i.h + + + The appFont which contains appFont name and size, for example 'San 10'. + + + + + diff --git a/common/data/com.kylinsec.Kiran.SessionDaemon.Display.xml b/common/data/com.kylinsec.Kiran.SessionDaemon.Display.xml new file mode 100644 index 0000000000000000000000000000000000000000..c520c6bf7d8de4ba6c727f31c915fe4d2dd481e3 --- /dev/null +++ b/common/data/com.kylinsec.Kiran.SessionDaemon.Display.xml @@ -0,0 +1,135 @@ + + + + + + + + The object path list. + + + + + Get the object paths for all the outputs. + + + + + + + + The dispaly style for these connected monitors. + + + + + The display styles contain mirrors(0), extend(1), custom(2) and auto(3). + The mirrors indicates all monitors have the same pos and size; + The extend indicates all monitors is extended horizontally; + The custom is set from local configuration display.xml; + The auto is set by the order of custom, extend, mirrors until success. + + + + + + + + The default display style. + + + + + Set the default display style. The display styles contain mirrors(0), extend(1), custom(2) and auto(3). + Used when the program starts running or new monitor device is added or existing monitor device is deleted. + + + + + + + + Apply all changes. + + + + + + + + Restore all changes. + + + + + + + + The name of the monitor. + + + + + Set the primary monitor by name. + + + + + + + + Save all changes to file. + + + + + + + + The window scaling factor. + + + + + Set the window scaling factor. + + + + + + + + + The default display style. + + + + + + + + + The name of the primary monitor. + + + + + + + + + The window scaling factor. + + + + + + + + The placeholder. + + + + + + diff --git a/common/src/kiran-appearance-monitor.cpp b/common/src/kiran-appearance-monitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2046757bfbb3a35084e8a75f38079b0719b3c86f --- /dev/null +++ b/common/src/kiran-appearance-monitor.cpp @@ -0,0 +1,265 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#include "kiran-appearance-monitor.h" +#include "kiran_appearance_proxy.h" +#include "kiran_display_proxy.h" +#include "logging-category.h" + +#include + +#include +#include + +KiranAppearanceMonitor::KiranAppearanceMonitor(QObject *parent) + : QObject(parent) +{ + m_appearanceIface = new KiranAppearanceProxy("com.kylinsec.Kiran.SessionDaemon.Appearance", + "/com/kylinsec/Kiran/SessionDaemon/Appearance", + QDBusConnection::sessionBus(), + this); + + if (QDBusConnection::sessionBus().interface()->isServiceRegistered("com.kylinsec.Kiran.SessionDaemon.Appearance")) + { + //application font + QString tempFontName; + int tempFontSize; + QString fontValue = m_appearanceIface->GetFont(APPEARANCE_FONT_TYPE_APPLICATION); + if (parseFontValue(fontValue, tempFontName, tempFontSize)) + { + m_appFontName = tempFontName; + m_appFontSize = tempFontSize; + qDebug(kiranPlatformThemeCommon,"application font: %s %d",m_appFontName.toStdString().c_str(),m_appFontSize); + } + else + { + qWarning(kiranPlatformThemeCommon) << "appearance monitor: parse application font failed!"; + } + + //window titlebar font + fontValue = m_appearanceIface->GetFont(APPEARANCE_FONT_TYPE_WINDOW_TITLE); + if(parseFontValue(fontValue,tempFontName,tempFontSize)) + { + m_titleBarFontName = tempFontName; + m_titleBarFontSize = tempFontSize; + qDebug(kiranPlatformThemeCommon,"title bar font: %s %d",m_titleBarFontName.toStdString().c_str(),m_titleBarFontSize); + } + else + { + qWarning(kiranPlatformThemeCommon) << "parse titlebar font failed!"; + } + + //icon theme + auto themeReply = m_appearanceIface->GetTheme(APPEARANCE_THEME_TYPE_ICON); + themeReply.waitForFinished(); + if( !themeReply.isError() ) + { + m_iconTheme = themeReply.value(); + qDebug(kiranPlatformThemeCommon,"icon theme: %s",m_iconTheme.toStdString().c_str()); + } + else + { + qWarning(kiranPlatformThemeCommon) << "get icon theme failed," << themeReply.error(); + } + + //gtk theme + themeReply = m_appearanceIface->GetTheme(APPEARANCE_THEME_TYPE_GTK); + themeReply.waitForFinished(); + if( !themeReply.isError() ) + { + QString gtkThemeName = themeReply.value(); + + if( gtkThemeName.contains("dark",Qt::CaseInsensitive) ) + m_gtkThemeName = "kiran-dark"; + else + m_gtkThemeName = "kiran"; + + qDebug(kiranPlatformThemeCommon,"gtk theme: %s",m_gtkThemeName.toStdString().c_str()); + } + else + { + qWarning(kiranPlatformThemeCommon) << "get gtk theme failed," << themeReply.error(); + } + } + else + { + qCritical(kiranPlatformThemeCommon) << "kiran session daemon appearance service isn't registered!"; + } + + connect(m_appearanceIface, &KiranAppearanceProxy::FontChanged, + this, &KiranAppearanceMonitor::handleFontSettingChanged); + connect(m_appearanceIface,&KiranAppearanceProxy::ThemeChanged, + this,&KiranAppearanceMonitor::handleThemeSettingChanged); + + + m_displayIface = new KiranDisplayProxy("com.kylinsec.Kiran.SessionDaemon.Display", + "/com/kylinsec/Kiran/SessionDaemon/Display", + QDBusConnection::sessionBus(), + this); + if (QDBusConnection::sessionBus().interface()->isServiceRegistered("com.kylinsec.Kiran.SessionDaemon.Display")) + { + m_scaleFactor = m_displayIface->window_scaling_factor(); + } + else + { + qCritical(kiranPlatformThemeCommon) << "kiran session daemon display service isn't registered!"; + } + + connect(m_displayIface, &KiranDisplayProxy::window_scaling_factorChanged, + this, &KiranAppearanceMonitor::handleWindowScaleFactorChanged); +} + +KiranAppearanceMonitor *KiranAppearanceMonitor::instance() +{ + static QMutex mutex; + static QScopedPointer pInst; + + if (Q_UNLIKELY(!pInst)) + { + QMutexLocker locker(&mutex); + if (pInst.isNull()) + { + pInst.reset(new KiranAppearanceMonitor); + } + } + + return pInst.data(); +} + +KiranAppearanceMonitor::~KiranAppearanceMonitor() +{ + +} + +void KiranAppearanceMonitor::handleFontSettingChanged(int type, const QString &fontValue) +{ + QString fontName; + int fontSize; + switch (type) + { + case APPEARANCE_FONT_TYPE_APPLICATION: + if (!parseFontValue(fontValue, fontName, fontSize)) + { + return; + } + m_appFontSize = fontSize; + m_appFontName = fontName; + emit appFontChanged(appFont()); + break; + case APPEARANCE_FONT_TYPE_WINDOW_TITLE: + if (!parseFontValue(fontValue, fontName, fontSize)) + { + return; + } + m_titleBarFontSize = fontSize; + m_titleBarFontName = fontName; + emit titleBarFontChanged(titleBarFont()); + return; + default: + break; + } +} + +void KiranAppearanceMonitor::handleWindowScaleFactorChanged(int scaleFactor) +{ + m_scaleFactor = scaleFactor; + emit scaleFactorChanged(m_scaleFactor); +} + +bool KiranAppearanceMonitor::parseFontValue(const QString &font, QString &fontName, int &fontSize) +{ + int fontSizeIdx = font.lastIndexOf(' '); + if (fontSizeIdx <= 0) + { + return false; + } + + bool toIntOk = false; + fontName = font.left(fontSizeIdx); + fontSize = font.mid(fontSizeIdx + 1).toInt(&toIntOk); + + if (!toIntOk) + { + return false; + } + + QFontDatabase fontDatabase; + if( !fontDatabase.hasFamily(fontName) ) + { + qInfo() << "font data base don't has this font:" << fontName; + return false; + } + + return true; +} + +QFont KiranAppearanceMonitor::appFont() const +{ + QFont font = QFont(QString()); + font.setFamily(m_appFontName); + font.setPointSize(m_appFontSize); + return font; +} + +int KiranAppearanceMonitor::scaleFactor() const +{ + return m_scaleFactor; +} + +QFont KiranAppearanceMonitor::titleBarFont() const +{ + QFont font = QFont(QString()); + font.setFamily(m_titleBarFontName); + font.setPointSize(m_titleBarFontSize); + return font; +} + +QString KiranAppearanceMonitor::iconTheme() const +{ + return m_iconTheme; +} + +void KiranAppearanceMonitor::handleThemeSettingChanged(int type, const QString &themeName) +{ + if(type == APPEARANCE_THEME_TYPE_ICON) + { + m_iconTheme = themeName; + emit iconThemeChanged(m_iconTheme); + } + else if(type == APPEARANCE_THEME_TYPE_GTK) + { + QString gtkTheme; + + if( themeName.contains("dark",Qt::CaseInsensitive) ) + { + gtkTheme = "kiran-dark"; + } + else + { + gtkTheme = "kiran"; + } + + if(gtkTheme!=m_gtkThemeName) + { + qInfo() << "gtk theme changed:" << themeName; + m_gtkThemeName = gtkTheme; + emit gtkThemeChanged(m_gtkThemeName); + } + } +} + +QString KiranAppearanceMonitor::gtkTheme() const +{ + return m_gtkThemeName; +} diff --git a/common/src/kiran-appearance-monitor.h b/common/src/kiran-appearance-monitor.h new file mode 100644 index 0000000000000000000000000000000000000000..54b3a97a17faea3ae1fe0746ea106509ccb10976 --- /dev/null +++ b/common/src/kiran-appearance-monitor.h @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#pragma once + +#include +#include + +class KiranDisplayProxy; +class KiranAppearanceProxy; +class KiranAppearanceMonitor : public QObject +{ + Q_OBJECT +private: + explicit KiranAppearanceMonitor(QObject* parent= nullptr); +public: + static KiranAppearanceMonitor* instance(); + ~KiranAppearanceMonitor() override; + + QFont appFont() const; + QFont titleBarFont() const; + QString iconTheme() const; + QString gtkTheme() const; + int scaleFactor() const; + +signals: + void appFontChanged(QFont font); + void titleBarFontChanged(QFont font); + void scaleFactorChanged(int factor); + void iconThemeChanged(QString iconTheme); + void gtkThemeChanged(QString gtkTheme); + +private: + static bool parseFontValue(const QString& font,QString& fontName,int& fontSize); + +private slots: + void handleFontSettingChanged(int type,const QString& fontValue); + void handleWindowScaleFactorChanged(int scaleFactor); + void handleThemeSettingChanged(int type,const QString& themeName); + +private: + QString m_appFontName = "Noto Sans CJK"; + int m_appFontSize = 10; + + QString m_titleBarFontName = "Noto Sans CJK"; + int m_titleBarFontSize = 11; + + int m_scaleFactor = 0; + + QString m_iconTheme = "hicolor"; + QString m_gtkThemeName = "kiran"; + KiranDisplayProxy* m_displayIface; + KiranAppearanceProxy* m_appearanceIface; +}; \ No newline at end of file diff --git a/common/src/logging-category.cpp b/common/src/logging-category.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e65f6264810756451b1c0084f95dea8d3c9c5f5 --- /dev/null +++ b/common/src/logging-category.cpp @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#include "logging-category.h" + +Q_LOGGING_CATEGORY(kiranPlatformThemeCommon,"kiran.platformtheme.common",QtWarningMsg) diff --git a/common/src/logging-category.h b/common/src/logging-category.h new file mode 100644 index 0000000000000000000000000000000000000000..676783e6b96154ce6728e841d2c689476149bcaa --- /dev/null +++ b/common/src/logging-category.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#pragma once + +#include + +Q_DECLARE_LOGGING_CATEGORY(kiranPlatformThemeCommon) \ No newline at end of file diff --git a/platformtheme/CMakeLists.txt b/platformtheme/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a8462d5f7d534c874d1655aff38e624da244dbc8 --- /dev/null +++ b/platformtheme/CMakeLists.txt @@ -0,0 +1,37 @@ +set(TARGET_NAME "qkiran") + +find_package(PkgConfig REQUIRED) +find_package(Qt5 COMPONENTS Widgets Gui Svg DBus) +find_package(Qt5ThemeSupport REQUIRED) + +pkg_search_module(KLOG_QT5 REQUIRED klog-qt5) +pkg_search_module(QGSETTINGS REQUIRED gsettings-qt) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +file(GLOB_RECURSE SRC "*.cpp" "*.h" "*.ui") + +add_library(${TARGET_NAME} SHARED + ${SRC}) + +target_include_directories(${TARGET_NAME} PUBLIC + ${CMAKE_CURRENT_BINARY_DIR} + ${Qt5Gui_PRIVATE_INCLUDE_DIRS} + ${Qt5Widgets_PRIVATE_INCLUDE_DIRS} + ${Qt5ThemeSupport_INCLUDE_DIRS} + ${QGSETTINGS_INCLUDE_DIRS} + ${KIRAN_CC_DAEMON_INCLUDE_DIRS}) + +target_link_libraries(${TARGET_NAME} + common + kiran-style-helper + Qt5::Widgets + Qt5::Gui + Qt5::DBus + Qt5::Svg + ${Qt5ThemeSupport_LIBRARIES} + ${QGSETTINGS_LIBRARIES}) + +install(TARGETS ${TARGET_NAME} DESTINATION ${QT5_PLATFORM_THEME_INSTALL_DIR}) \ No newline at end of file diff --git a/platformtheme/kiran-theme-plugin.cpp b/platformtheme/kiran-theme-plugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..20c0198a1f2bc55cd63053eef8d1a0c41cd17ad4 --- /dev/null +++ b/platformtheme/kiran-theme-plugin.cpp @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "kiran-theme-plugin.h" +#include "kiran-theme.h" + +#include "logging-category.h" +#include + +QPlatformTheme* KiranThemePlugin::create(const QString& key, const QStringList& paramList) +{ + static QSet keySet = {"kiran","gnome","gtk3","qgnomeplatform"}; + + if(keySet.contains(key)) + { + qDebug(kiranPlatformTheme) << "create kiran style for" << QCoreApplication::applicationName(); + return new KiranTheme(paramList); + } + + return nullptr; +} diff --git a/platformtheme/kiran-theme-plugin.h b/platformtheme/kiran-theme-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..6dbfe0cd0a4f3842e5cf5a5fa1f610d39d61c460 --- /dev/null +++ b/platformtheme/kiran-theme-plugin.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_PLATFORMTHEME_KIRAN_THEME_PLUGIN_H_ +#define KIRAN_QT5_PLATFORMTHEME_PLATFORMTHEME_KIRAN_THEME_PLUGIN_H_ + +#include + +class KiranThemePlugin : public QPlatformThemePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QPlatformThemeFactoryInterface_iid FILE "qkiran.json") +public: + explicit KiranThemePlugin(QObject *parent=nullptr) :QPlatformThemePlugin(parent){}; + ~KiranThemePlugin() override = default; + + QPlatformTheme *create(const QString &key, const QStringList ¶mList) override; + +private: +}; + +#endif //KIRAN_QT5_PLATFORMTHEME_PLATFORMTHEME_KIRAN_THEME_PLUGIN_H_ diff --git a/platformtheme/kiran-theme.cpp b/platformtheme/kiran-theme.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2f4368e0674ffc58b8a4412406d888a6629d2ef5 --- /dev/null +++ b/platformtheme/kiran-theme.cpp @@ -0,0 +1,351 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#include "kiran-theme.h" +#include "kiran-appearance-monitor.h" +#include "logging-category.h" + +#include +#include +#include +#include +#include +#define private public +#include +#undef private + +#include +#include +#include +#include +#include +#include +#include +#include +#include "kiran-palette.h" + +KiranTheme::KiranTheme(const QStringList& paramList) + : QGenericUnixTheme() +{ + init(); +} + +KiranTheme::~KiranTheme() +{ +} + +bool KiranTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const +{ + return QGenericUnixTheme::usePlatformNativeDialog(type); +} + +QPlatformDialogHelper* KiranTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const +{ + return QGenericUnixTheme::createPlatformDialogHelper(type); +} + +QIconEngine* KiranTheme::createIconEngine(const QString& iconName) const +{ + return QGenericUnixTheme::createIconEngine(iconName); +} + +QPixmap KiranTheme::standardPixmap(QPlatformTheme::StandardPixmap sp, const QSizeF& size) const +{ + return QGenericUnixTheme::standardPixmap(sp, size); +} + +QIcon KiranTheme::fileIcon(const QFileInfo& fileInfo, QPlatformTheme::IconOptions iconOptions) const +{ + return QGenericUnixTheme::fileIcon(fileInfo, iconOptions); +} + +QVariant KiranTheme::themeHint(QPlatformTheme::ThemeHint hint) const +{ + switch (hint) + { + case SystemIconThemeName: + return QStringList() << m_settingsMonitor->iconTheme(); + case SystemIconFallbackThemeName: + return QStringList() << "hicolor"; + case IconThemeSearchPaths: + return QStringList() << ".local/share/icons" + << "/usr/share/icons" + << "/usr/local/share/icons"; + case StyleNames: + return QStringList{"kiran","fusion"}; + default: + break; + } + return QGenericUnixTheme::themeHint(hint); +} + +const QPalette* KiranTheme::palette(QPlatformTheme::Palette type) const +{ + if (type != SystemPalette) + { + return QGenericUnixTheme::palette(type); + } + + static QPalette kiranThemePalette; + KiranPalette::instance()->polishPalette(&kiranThemePalette); + return &kiranThemePalette; +} + +void KiranTheme::init() +{ + qDebug(kiranPlatformTheme) << "init kiran theme..."; + m_settingsMonitor = KiranAppearanceMonitor::instance(); + + m_scaleFactor = m_settingsMonitor->scaleFactor(); + qDebug(kiranPlatformTheme) << "\tscale factor:" << m_scaleFactor; + + m_systemFont.setFamily(m_settingsMonitor->appFont().family()); + m_systemFont.setPointSize(m_settingsMonitor->appFont().pointSize()); + QApplication::setFont(m_systemFont); + qDebug(kiranPlatformTheme) << "\tapplication font:" << m_settingsMonitor->appFont().family() << m_settingsMonitor->appFont().pointSize(); + + m_titleBarFont.setFamily(m_settingsMonitor->titleBarFont().family()); + m_titleBarFont.setPointSize(m_settingsMonitor->titleBarFont().pointSize()); + qDebug(kiranPlatformTheme) << "\ttitle bar font:" << m_settingsMonitor->titleBarFont().family() << m_settingsMonitor->titleBarFont().pointSize(); + + QObject::connect(m_settingsMonitor, &KiranAppearanceMonitor::appFontChanged, this, &KiranTheme::handleAppFontChanged); + QObject::connect(m_settingsMonitor, &KiranAppearanceMonitor::titleBarFontChanged, this, &KiranTheme::handleTitleBarFontChanged); + QObject::connect(m_settingsMonitor, &KiranAppearanceMonitor::iconThemeChanged, this, &KiranTheme::handleIconThemeChanged); + QObject::connect(m_settingsMonitor, &KiranAppearanceMonitor::scaleFactorChanged, this, &KiranTheme::handleScaleFactorChanged); + QObject::connect(m_settingsMonitor, &KiranAppearanceMonitor::gtkThemeChanged, this, &KiranTheme::handleGtkThemeChanged); + QObject::connect(qApp, &QApplication::screenAdded, this, &KiranTheme::handleScreenAdded); + + handleScaleFactorChanged(m_scaleFactor); +} + +const QFont* KiranTheme::font(QPlatformTheme::Font type) const +{ + switch (type) + { + case QPlatformTheme::TitleBarFont: + { + static QFont titleBarFont = QFont(QString()); + titleBarFont.setFamily(m_titleBarFont.family()); + titleBarFont.setPointSize(m_titleBarFont.pointSize()); + return &titleBarFont; + } + case SystemFont: + case FixedFont: + { + static QFont tempFont = QFont(QString()); + tempFont.setFamily(m_systemFont.family()); + tempFont.setPointSize(m_systemFont.pointSize()); + return &tempFont; + } + default: + break; + } + + return QGenericUnixTheme::font(type); +} + +// NOTE:也可通过删除QGuiApplication::app_font,对qapp下的所有window发送ApplicationFontChanged事件 +// 但是若是设置了样式表(setStyleSheet)会抑制字体的传递。该方法可能耗资源更少 +void KiranTheme::handleAppFontChanged() +{ + qDebug(kiranPlatformTheme) << "application font changed:" + << m_settingsMonitor->appFont().family() + << m_settingsMonitor->appFont().pointSize(); + + m_systemFont.setFamily(m_settingsMonitor->appFont().family()); + m_systemFont.setPointSize(m_settingsMonitor->appFont().pointSize()); + QApplication::setFont(m_systemFont); +} + +void KiranTheme::handleTitleBarFontChanged() +{ + qDebug(kiranPlatformTheme) << "title bar font changed:" + << m_settingsMonitor->titleBarFont().family() + << m_settingsMonitor->titleBarFont().pointSize(); + + m_titleBarFont.setFamily(m_settingsMonitor->titleBarFont().family()); + m_titleBarFont.setPointSize(m_settingsMonitor->titleBarFont().pointSize()); + + // 遍历所有窗口,判断是否是KiranWidget中的自定义标题栏修改其标题栏字体 + for (auto window : qGuiApp->allWindows()) + { + if (window->inherits("QWidgetWindow")) + { + auto widgetWindow = static_cast(window); + if (widgetWindow->widget() && widgetWindow->widget()->inherits("KiranTitlebarWindow")) + { + auto titleBarWidget = widgetWindow->widget(); + auto titleLabel = titleBarWidget->findChild("KiranTitlebarTitle"); + titleLabel->setFont(m_titleBarFont); + } + } + } +} + +void KiranTheme::handleScaleFactorChanged(int factor) +{ + qDebug(kiranPlatformTheme) << "scale factor changed:" + << m_settingsMonitor->scaleFactor(); + + // 缩放的话,同时也应该调整设置到窗口管理器的边缘阴影属性 + if (enableRealTimeScaling()) + { + if (factor == 0) // 自动缩放 + { + // QPlatformScreen* screenHandle = screen->handle(); + // qInfo() << screenHandle->name(); + // qInfo() << "\t logical dpi:" << screenHandle->logicalDpi(); + // qInfo() << "\t pixel density:" << screenHandle->pixelDensity(); + // TODO:清空全局分辨率,对每个屏幕单独设置分辨率 + } + else if (factor == 1 || factor == 2) // 手动固定分辨率 + { + qDebug(kiranPlatformTheme) << "update scale factor:" << factor; + if (qFuzzyCompare(QHighDpiScaling::m_factor, factor)) + { + return; + } + + // 清空屏幕的子缩放率 + { + for (QScreen* screen : qGuiApp->screens()) + { + if (!qFuzzyCompare(QHighDpiScaling::screenSubfactor(screen->handle()), 1)) + { + QHighDpiScaling::setScreenFactor(screen, 1); + } + } + } + + // 设置全局缩放率 + QHighDpiScaling::setGlobalFactor(factor); + + { + for (QScreen* s : qGuiApp->screens()) + { + Q_EMIT s->geometryChanged(s->geometry()); + } + } + + // 更新窗口大小 + { + for (QWindow* window : QGuiApplication::allWindows()) + { + if (window->type() == Qt::ForeignWindow || window->type() == Qt::Desktop) + { + return; + } + + if (!window->handle() || !window->isTopLevel()) + return; + + const QRect currentGeo = QWindowPrivate::get(window)->geometry; + if (!currentGeo.isValid()) + return; + + // qInfo() << window->type(); + // qInfo() << "current geo:" << currentGeo; + QRect nativeGeo = window->handle()->geometry(); + qreal scale = QHighDpiScaling::factor(window); + nativeGeo.setSize(currentGeo.size() * scale); + // qInfo() << "native geo:" << nativeGeo; + + window->handle()->setGeometry(nativeGeo); + QGuiApplication::sendEvent(window, new QEvent(QEvent::UpdateRequest)); + } + } + } + else + { + qWarning(kiranPlatformTheme) << "not support this factor:" << factor; + return; + } + } + else + { + qWarning(kiranPlatformTheme) << "disable real time scaling,ignore scale factor changed!"; + } +} + +bool KiranTheme::enableRealTimeScaling() +{ + static bool enable = !qEnvironmentVariableIsSet("QT_DEVICE_PIXEL_RATIO") && + !qEnvironmentVariableIsSet("QT_SCALE_FACTOR") && + !qEnvironmentVariableIsSet("QT_AUTO_SCREEN_SCALE_FACTOR") && + !qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS") && + !QCoreApplication::testAttribute(Qt::AA_DisableHighDpiScaling) && + !QCoreApplication::testAttribute(Qt::AA_EnableHighDpiScaling) && + qGuiApp->platformName().endsWith("xcb"); + return enable; +} + +void KiranTheme::handleScreenAdded(QScreen* screen) +{ + if (m_scaleFactor == 0) // 计算并自动调整该屏幕的缩放 + { + // TODO:根据QPlatformTheme中的pixel density设置该屏幕子分辨率 + } + else if (m_scaleFactor == 1 || m_scaleFactor == 2) + { + // TODO:应用全局分辨率至屏幕上 + } +} + +void KiranTheme::handleIconThemeChanged() +{ + qInfo() << "update icon theme:" << m_settingsMonitor->iconTheme(); + QIconLoader::instance()->updateSystemTheme(); + + // 通知所有窗口重绘制 + QEvent update(QEvent::UpdateRequest); + for (QWindow* window : qGuiApp->allWindows()) + { + if (window->type() == Qt::Desktop) + continue; + + qApp->sendEvent(window, &update); + } +} + +void KiranTheme::handleGtkThemeChanged(const QString& gtkTheme) +{ + // NOTE: SchemeLoader会接收KiranAppearanceMonitor的GTK主题改变信号,重新加载配色方案 + // 此处只需等到下一个事件循环,通知QGuiApplication重新更新palette + QTimer::singleShot(0, [this] + { + // 此事件会促使QGuiApplication重新从QPlatformTheme中获取系统级别的QPalette + QWindowSystemInterfacePrivate::ThemeChangeEvent event(nullptr); + + ///NOTE: + ///QGuiApplicationPrivate::processThemeChanged -> QGuiApplicationPrivate::notifyThemeChanged + ///5.11.1 + /// QGuiApplicationPrivate::notifyThemeChanged若非特殊设置过Palette,是直接清空QGuiApplicajtionPrivate::app_pal, + /// 然后重新从QPlatformTheme之中取出system palette,重新构建QGuiApplicajtionPrivate::app_pal + ///5.15.2 + /// QGuiApplicationPrivate::app_pal初始化 + /// 默认初始化一个QPalette,若QAGuiApplicationPrivate::app_pal不存在,则调用qpalette.cpp之中qt_palette_from_color构造一个 + /// QGuiApplicationPrivate::app_pal会采用上述的QPalette作为默认值 + /// + /// 后续调用processThemeChanged->updatePalette,都只是QGuiApplicationPrivate::app_pal从QPlatformTheme::palette(system)之中取出缺失的 + /// 所以QGuiApplicationPrivate::app_pal第一次初始化后,第一次主题应用能更新系统级别QPalette,但是之后主题变更应用不了 + + ///FIXME: + ///1.后续看是否有更好的方式能应用更新系统级别QPalette + ///2.加入判断是否外部自定义设置Application palette,若外部自定义设置了,则应不做处理 + QGuiApplicationPrivate::app_pal = new QPalette(*palette(SystemPalette)); + + // 该接口原本用于windows通知窗口主题变化时使用,现用来通知调用QGuiApplicationPrivate::notifyThemeChanged + QGuiApplicationPrivate::processThemeChanged(&event); + emit qApp->paletteChanged(*palette(SystemPalette)); }); +} diff --git a/platformtheme/kiran-theme.h b/platformtheme/kiran-theme.h new file mode 100644 index 0000000000000000000000000000000000000000..2f22f15748ee655d436c1d9d2366de445165c747 --- /dev/null +++ b/platformtheme/kiran-theme.h @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_PLATFORMTHEME_KIRAN_THEME_H_ +#define KIRAN_QT5_PLATFORMTHEME_PLATFORMTHEME_KIRAN_THEME_H_ +#include +#include +#include + +class KiranAppearanceMonitor; +class KiranTheme : public QObject,public QGenericUnixTheme +{ + Q_OBJECT +public: + explicit KiranTheme(const QStringList& paramList); + ~KiranTheme() override; + + //是否使用系统本地提示框 + bool usePlatformNativeDialog(DialogType type) const override; + //创建本地提示框帮助类 + QPlatformDialogHelper* createPlatformDialogHelper(DialogType type) const override; + QIconEngine* createIconEngine(const QString& iconName) const override; + QPixmap standardPixmap(StandardPixmap sp, const QSizeF& size) const override; + QIcon fileIcon(const QFileInfo& fileInfo, QPlatformTheme::IconOptions iconOptions) const override; + + QVariant themeHint(ThemeHint hint) const override; + + const QPalette* palette(Palette type) const override; + const QFont* font(Font type) const override; + +private: + void init(); + static bool enableRealTimeScaling(); + +private slots: + void handleAppFontChanged(); + void handleTitleBarFontChanged(); + void handleIconThemeChanged(); + void handleScaleFactorChanged(int factor); + void handleScreenAdded(QScreen* screen); + void handleGtkThemeChanged(const QString& gtkTheme); + +private: + KiranAppearanceMonitor* m_settingsMonitor; + int m_scaleFactor; + QFont m_systemFont; + QFont m_titleBarFont; +}; + +#endif //KIRAN_QT5_PLATFORMTHEME_PLATFORMTHEME_KIRAN_THEME_H_ diff --git a/platformtheme/logging-category.cpp b/platformtheme/logging-category.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ae072a8e77fc279dd67e56fb0a7d93e821e5d840 --- /dev/null +++ b/platformtheme/logging-category.cpp @@ -0,0 +1,7 @@ +// +// Created by lxh on 2022/3/18. +// + +#include "logging-category.h" + +Q_LOGGING_CATEGORY(kiranPlatformTheme,"kiran.platformtheme",QtWarningMsg) diff --git a/platformtheme/logging-category.h b/platformtheme/logging-category.h new file mode 100644 index 0000000000000000000000000000000000000000..33ac457261fa9d9c8c487350dbd49f4cc2355b79 --- /dev/null +++ b/platformtheme/logging-category.h @@ -0,0 +1,8 @@ +#ifndef KIRAN_QT5_INTEGRATION_PLATFORMTHEME_LOGGIN_CATEGORY_H_ +#define KIRAN_QT5_INTEGRATION_PLATFORMTHEME_LOGGIN_CATEGORY_H_ + +#include + +Q_DECLARE_LOGGING_CATEGORY(kiranPlatformTheme) + +#endif //KIRAN_QT5_INTEGRATION_PLATFORMTHEME_LOGGIN_CATEGORY_H_ diff --git a/platformtheme/qkiran.json b/platformtheme/qkiran.json new file mode 100644 index 0000000000000000000000000000000000000000..794d5ad449996fd08edc6c39a17e3b8e429dee71 --- /dev/null +++ b/platformtheme/qkiran.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "kiran"] +} diff --git a/style-helper/CMakeLists.txt b/style-helper/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..004222a5ec761a7da6619ac16b5b9544bc581ac8 --- /dev/null +++ b/style-helper/CMakeLists.txt @@ -0,0 +1,75 @@ +set(TARGET_NAME "kiran-style-helper") + +set(KIRAN_STYLE_HELPER_VERSION 2.3.0) +set(KIRAN_STYLE_HELPER_SO_VERSION 2) + +# light theme +set(KIRAN_LIGHT_COLORS ${CMAKE_CURRENT_BINARY_DIR}/kiran-light/kiran-light-colors.css) +add_custom_command( + OUTPUT ${KIRAN_LIGHT_COLORS} + COMMAND ./build-qt-theme.sh + -c data/color-scheme/light.colors + -b data/color-scheme/base.colors + -t kiran-light + -d ${CMAKE_CURRENT_BINARY_DIR}/kiran-light + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +# dark theme +set(KIRAN_DARK_COLORS ${CMAKE_CURRENT_BINARY_DIR}/kiran-dark/kiran-dark-colors.css) +add_custom_command( + OUTPUT ${KIRAN_DARK_COLORS} + COMMAND ./build-qt-theme.sh + -c data/color-scheme/dark.colors + -b data/color-scheme/base.colors + -t kiran-dark + -d ${CMAKE_CURRENT_BINARY_DIR}/kiran-dark + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +set(KIRANSTYLE_HELPER_QRC ${CMAKE_CURRENT_BINARY_DIR}/kiranstyle-helper.qrc) +configure_file(kiranstyle-helper.qrc.in ${KIRANSTYLE_HELPER_QRC}) + +message("${CMAKE_CURRENT_SOURCE_DIR}") +find_package(Qt5 COMPONENTS Core Gui Widgets) +file(GLOB_RECURSE SRC "src/*.cpp" "src/*.h" "src/*.ui") +file(GLOB_RECURSE INCLUDE "include/*.h") + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +add_library(${TARGET_NAME} SHARED + ${SRC} + ${KIRANSTYLE_HELPER_QRC} + ${INCLUDE} + ${KIRAN_DARK_COLORS} + ${KIRAN_LIGHT_COLORS}) + +set_target_properties(${TARGET_NAME} PROPERTIES + VERSION ${KIRAN_STYLE_HELPER_VERSION} + SOVERSION ${KIRAN_STYLE_HELPER_SO_VERSION}) + +target_include_directories(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR} + ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) + +target_include_directories(${TARGET_NAME} PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +target_link_libraries(${TARGET_NAME} + common + Qt5::Core + Qt5::Gui + Qt5::Widgets) + +set(KIRAN_STYLE_HELPER_LIB_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/) +install(TARGETS ${TARGET_NAME} DESTINATION ${KIRAN_STYLE_HELPER_LIB_DIR}) + +set(KIRAN_STYLE_HELPER_INCLUDE_DIR ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/kiran-style) +install(FILES ${INCLUDE} DESTINATION ${KIRAN_STYLE_HELPER_INCLUDE_DIR}) + +configure_file(kiran-style-helper.pc.in kiran-style-helper.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kiran-style-helper.pc + DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig/) \ No newline at end of file diff --git a/style-helper/build-qt-theme.sh b/style-helper/build-qt-theme.sh new file mode 100755 index 0000000000000000000000000000000000000000..3d4b43526cec973ff114498a017e49e6f219f006 --- /dev/null +++ b/style-helper/build-qt-theme.sh @@ -0,0 +1,51 @@ +#!/bin/bash +#该脚本主要负责: +# 1. 调用python脚本根据colors描述文件提取相应的颜色信息并进行相应的颜色转换(ColorEffect)到_global.scss +# 2. 通过sassc将该项目中内置的scss与_global.scss,_functions.scss生成对应的颜色表一起转换至css之中 + +set -e + +# Usage: render_theme +# example: render_theme dark.colors base.colors /tmp/dark/ dark +render_theme () { + # step-1: 提取colorscheme与base-colorscheme颜色表中所定义的数据提取到generate-dir下的_global.scss之中 + python3 extract_color_from_scheme.py -c "$1" -b "$2" -d "$3" + # step-2: 通过sassc将项目内置的scss与_global.scss,functions.scss转换为一个新的css文件 + sassc -I "$3" ./data/qt5.scss "$3/$4-colors.css" +} + +COLOR_SCHEME_FILE="" +BASE_SCHEME_FILE="" +THEME_NAME="" +THEME_BUILD_DIR="" + +while [ "$#" -gt 0 ]; do + case "$1" in + -h|--help) + echo "help" + echo "$0: build kiran qt theme" + echo "Usage: $0 .." + exit 0 + ;; + -c|--color-scheme-file) + shift + COLOR_SCHEME_FILE="$1" + ;; + -b|--base-scheme-file) + shift + BASE_SCHEME_FILE="$1" + ;; + -t|--theme-name) + shift + THEME_NAME="$1" + ;; + -d|--dest-dir) + shift + THEME_BUILD_DIR="$1" + ;; + esac + shift +done + +render_theme "${COLOR_SCHEME_FILE}" "${BASE_SCHEME_FILE}" "${THEME_BUILD_DIR}" "${THEME_NAME}" + diff --git a/style-helper/data/_colors-public.scss b/style-helper/data/_colors-public.scss new file mode 100644 index 0000000000000000000000000000000000000000..85377977d1df9c828d0a3d84bf9e65b5d96bf7f1 --- /dev/null +++ b/style-helper/data/_colors-public.scss @@ -0,0 +1,131 @@ +@charset "UTF-8"; + +//apps rely on some named colors to be exported +/* GTK NAMED COLORS + ---------------- + use responsibly! */ + + +//changed!!! +@define-color theme_selected_background_normal #{"" + internal_selection(background)}; +@define-color theme_selected_foreground_normal #{"" + internal_selection(foreground)}; +@define-color theme_selected_background_insensitive #{"" + internal_insensitive(internal_selection(background))}; +@define-color theme_selected_foreground_insensitive #{"" + internal_insensitive(internal_selection(foreground))}; +@define-color theme_selected_background_backdrop #{"" + internal_backdrop(internal_selection(background))}; +@define-color theme_selected_foreground_backdrop #{"" + internal_backdrop(internal_selection(foreground))}; + +// Sass thinks we're using the colors in the variables as strings and may shoot +// warning, it's innocuous and can be defeated by using "" + $var +/* --------------------------------------------------------------- Default Colors --------------------------------------------------------------- */ +@define-color theme_default_background_normal #{"" + internal_default(background)}; +@define-color theme_default_background_insensitive #{"" + internal_insensitive(internal_default(background))}; +@define-color theme_default_background_backdrop #{"" + internal_backdrop(internal_default(background))}; +@define-color theme_default_background_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_default(background)))}; +@define-color theme_default_foreground_normal #{"" + internal_default(foreground)}; +@define-color theme_default_foreground_insensitive #{"" + internal_insensitive(internal_default(foreground))}; +@define-color theme_default_foreground_backdrop #{"" + internal_backdrop(internal_default(foreground))}; +@define-color theme_default_foreground_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_default(foreground)))}; +@define-color theme_default_border_normal #{"" + internal_default(border)}; +@define-color theme_default_border_insensitive #{"" + internal_insensitive(internal_default(border))}; +@define-color theme_default_border_backdrop #{"" + internal_backdrop(internal_default(border))}; +@define-color theme_default_border_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_default(border)))}; +@define-color theme_default_warning #{"" + internal_default(warning)}; +@define-color theme_default_warning_backdrop #{"" + internal_backdrop(internal_default(warning))}; +@define-color theme_default_error #{"" + internal_default(error)}; +@define-color theme_default_error_backdrop #{"" + internal_backdrop(internal_default(error))}; +@define-color theme_default_success #{"" + internal_default(success)}; +@define-color theme_default_success_backdrop #{"" + internal_backdrop(internal_default(success))}; + + +/* these are pretty self explicative */ +@define-color warning_color #{"" +internal_default(warning)}; +@define-color error_color #{"" +internal_default(error)}; +@define-color success_color #{"" +internal_default(success)}; + + +/* --------------------------------------------------------------- Widget Colors --------------------------------------------------------------- */ +@define-color theme_widget_background_normal #{"" + internal_widget(background)}; +@define-color theme_widget_background_hover #{"" + internal_widget(background-hover)}; +@define-color theme_widget_background_active #{"" + internal_widget(background-active)}; +@define-color theme_widget_background_checked #{"" + internal_widget(background-checked)}; +@define-color theme_widget_background_insensitive #{"" + internal_insensitive(internal_widget(background))}; +@define-color theme_widget_background_backdrop #{"" + internal_backdrop(internal_widget(background))}; +@define-color theme_widget_background_backdrop_checked #{"" + internal_backdrop(internal_widget(background-checked))}; +@define-color theme_widget_background_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_widget(background)))}; +@define-color theme_widget_foreground_normal #{"" + internal_widget(foreground)}; +@define-color theme_widget_foreground_hover #{"" + internal_widget(foreground-hover)}; +@define-color theme_widget_foreground_active #{"" + internal_widget(foreground-active)}; +@define-color theme_widget_foreground_checked #{"" + internal_widget(foreground-checked)}; +@define-color theme_widget_foreground_insensitive #{"" + internal_insensitive(internal_widget(foreground))}; +@define-color theme_widget_foreground_backdrop #{"" + internal_backdrop(internal_widget(foreground))}; +@define-color theme_widget_foreground_backdrop_checked #{"" + internal_backdrop(internal_widget(foreground-checked))}; +@define-color theme_widget_foreground_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_widget(foreground)))}; +@define-color theme_widget_border_normal #{"" + internal_widget(border)}; +@define-color theme_widget_border_hover #{"" + internal_widget(border-hover)}; +@define-color theme_widget_border_focus #{"" + internal_widget(border-focus)}; +@define-color theme_widget_border_insensitive #{"" + internal_insensitive(internal_widget(border))}; +@define-color theme_widget_border_backdrop #{"" + internal_backdrop(internal_widget(border))}; +@define-color theme_widget_border_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_widget(border)))}; + + +/* --------------------------------------------------------------- Bare Colors --------------------------------------------------------------- */ +@define-color theme_bare_background_normal #{"" + internal_bare(background)}; +@define-color theme_bare_background_hover #{"" + internal_bare(background-hover)}; +@define-color theme_bare_background_active #{"" + internal_bare(background-active)}; +@define-color theme_bare_background_checked #{"" + internal_bare(background-checked)}; +@define-color theme_bare_background_insensitive #{"" + internal_insensitive(internal_bare(background))}; +@define-color theme_bare_background_backdrop #{"" + internal_backdrop(internal_bare(background))}; +@define-color theme_bare_background_backdrop_checked #{"" + internal_backdrop(internal_bare(background-checked))}; +@define-color theme_bare_background_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_bare(background)))}; +@define-color theme_bare_foreground_normal #{"" + internal_bare(foreground)}; +@define-color theme_bare_foreground_hover #{"" + internal_bare(foreground-hover)}; +@define-color theme_bare_foreground_active #{"" + internal_bare(foreground-active)}; +@define-color theme_bare_foreground_checked #{"" + internal_bare(foreground-checked)}; +@define-color theme_bare_foreground_insensitive #{"" + internal_insensitive(internal_bare(foreground))}; +@define-color theme_bare_foreground_backdrop #{"" + internal_backdrop(internal_bare(foreground))}; +@define-color theme_bare_foreground_backdrop_checked #{"" + internal_backdrop(internal_bare(foreground-checked))}; +@define-color theme_bare_foreground_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_bare(foreground)))}; + +/* --------------------------------------------------------------- Window Colors --------------------------------------------------------------- */ +@define-color theme_window_background_normal #{"" + internal_window(background)}; +@define-color theme_window_background_insensitive #{"" + internal_insensitive(internal_window(background))}; +@define-color theme_window_background_backdrop #{"" + internal_backdrop(internal_window(background))}; +@define-color theme_window_background_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_window(background)))}; +@define-color theme_window_foreground_normal #{"" + internal_window(foreground)}; +@define-color theme_window_foreground_insensitive #{"" + internal_insensitive(internal_window(foreground))}; +@define-color theme_window_foreground_backdrop #{"" + internal_backdrop(internal_window(foreground))}; +@define-color theme_window_foreground_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_window(foreground)))}; +@define-color theme_window_border_normal #{"" + internal_window(border)}; +@define-color theme_window_border_insensitive #{"" + internal_insensitive(internal_window(border))}; +@define-color theme_window_border_backdrop #{"" + internal_backdrop(internal_window(border))}; +@define-color theme_window_border_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_window(border)))}; + +/* --------------------------------------------------------------- View Colors --------------------------------------------------------------- */ +@define-color theme_view_background_normal #{"" + internal_view(background)}; +@define-color theme_view_background_insensitive #{"" + internal_insensitive(internal_view(background))}; +@define-color theme_view_background_backdrop #{"" + internal_backdrop(internal_view(background))}; +@define-color theme_view_background_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_view(background)))}; +@define-color theme_view_foreground_normal #{"" + internal_view(foreground)}; +@define-color theme_view_foreground_insensitive #{"" + internal_insensitive(internal_view(foreground))}; +@define-color theme_view_foreground_backdrop #{"" + internal_backdrop(internal_view(foreground))}; +@define-color theme_view_foreground_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_view(foreground)))}; +@define-color theme_view_border_normal #{"" + internal_view(border)}; +@define-color theme_view_border_focus #{"" + internal_view(border-focus)}; +@define-color theme_view_border_insensitive #{"" + internal_insensitive(internal_view(border))}; +@define-color theme_view_border_backdrop #{"" + internal_backdrop(internal_view(border))}; +@define-color theme_view_border_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_view(border)))}; + + +/* -------------------------------------------------------------- Titlebar Colors -------------------------------------------------------------- */ +@define-color theme_titlebar_background_normal #{"" + internal_titlebar(background)}; +@define-color theme_titlebar_background_insensitive #{"" + internal_insensitive(internal_titlebar(background))}; +@define-color theme_titlebar_background_backdrop #{"" + internal_backdrop(internal_titlebar(background))}; +@define-color theme_titlebar_background_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_titlebar(background)))}; +@define-color theme_titlebar_foreground_normal #{"" + internal_titlebar(foreground)}; +@define-color theme_titlebar_foreground_insensitive #{"" + internal_insensitive(internal_titlebar(foreground))}; +@define-color theme_titlebar_foreground_backdrop #{"" + internal_backdrop(internal_titlebar(foreground))}; +@define-color theme_titlebar_foreground_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_titlebar(foreground)))}; +@define-color theme_titlebar_border_normal #{"" + internal_titlebar(border)}; +@define-color theme_titlebar_border_insensitive #{"" + internal_insensitive(internal_titlebar(border))}; +@define-color theme_titlebar_border_backdrop #{"" + internal_backdrop(internal_titlebar(border))}; +@define-color theme_titlebar_border_backdrop_insensitive #{"" + internal_insensitive(internal_backdrop(internal_titlebar(border)))}; diff --git a/style-helper/data/_functions.scss b/style-helper/data/_functions.scss new file mode 100644 index 0000000000000000000000000000000000000000..be74dbb02f8c0c3405706d3bf1a3934ea5fa8194 --- /dev/null +++ b/style-helper/data/_functions.scss @@ -0,0 +1,408 @@ +@charset "UTF-8"; + +// GTK functions + +// escape string entirely +@function gtk($str) { + @return unquote("#{$str}"); +} + +// alpha function +@function gtkalpha($c, $a) { + @return unquote("alpha(#{$c},#{$a})"); +} + +// shade function +@function gtkshade($c, $a) { + @return unquote("shade(#{$c},#{$a})"); +} + +// mix function +@function gtkmix($c1, $c2, $m) { + @return unquote("mix(#{$c1},#{$c2},#{$m})"); +} + +// +// Theme color functions +// + +// Default Color +@function internal_default($c: background) { + @if $c == background { + @return $DefaultBackgroundNormal; + } + @if $c == foreground { + @return $DefaultForegroundNormal; + } + @if $c == border { + @return $DefaultBorder; + } + @if $c == warning { + @return $DefaultWarning; + } + @if $c == error { + @return $DefaultError; + } + @if $c == success { + @return $DefaultSuccess; + } + @if $c == link { + @return $DefaultLink; + } + @if $c == link-visited { + @return $DefaultLinkVisited; + } +} + +// Widget Color +@function internal_widget($c: background) { + @if $c == background { + @return $WidgetBackgroundNormal; + } + @if $c == foreground { + @return $WidgetForegroundNormal; + } + @if $c == background-hover { + @return $WidgetBackgroundHover; + } + @if $c == background-active { + @return $WidgetBackgroundActive; + } + @if $c == background-checked { + @return $WidgetBackgroundChecked; + } + @if $c == foreground-hover { + @return $WidgetForegroundHover; + } + @if $c == foreground-active { + @return $WidgetForegroundActive; + } + @if $c == foreground-checked { + @return $WidgetForegroundChecked; + } + @if $c == border { + @return $WidgetBorderNormal; + } + @if $c == border-hover { + @return $WidgetBorderHover; + } + @if $c == border-focus { + @return $WidgetBorderFocus; + } +} + +// Bare(No border) Widget Color +@function internal_bare($c: background) { + @if $c == background { + @return $BareBackgroundNormal; + } + @if $c == foreground { + @return $BareForegroundNormal; + } + @if $c == background-hover { + @return $BareBackgroundHover; + } + @if $c == background-active { + @return $BareBackgroundActive; + } + @if $c == background-checked { + @return $BareBackgroundChecked; + } + @if $c == foreground-hover { + @return $BareForegroundHover; + } + @if $c == foreground-active { + @return $BareForegroundActive; + } + @if $c == foreground-checked { + @return $BareForegroundChecked; + } +} + +// Selection Color +@function internal_selection($c: background) { + @if $c == background { + @return $SelectionBackgroundNormal; + } + @if $c == foreground { + @return $SelectionForegroundNormal; + } + @if $c == hover { + @return $SelectionDecorationHover; + } + @if $c == backdrop { + @return transparentize($SelectionBackgroundNormal, 0.5); + } +} + +// View Color +@function internal_view($c: background) { + @if $c == background { + @return $ViewBackgroundNormal; + } + @if $c == foreground { + @return $ViewForegroundNormal; + } + @if $c == border { + @return $ViewBorderNormal; + } + @if $c == border-focus { + @return $ViewBorderFocus; + } + +} + +// Window Color +@function internal_window($c: background) { + @if $c == background { + @return $WindowBackgroundNormal; + } + @if $c == foreground { + @return $WindowForegroundNormal; + } + @if $c == border { + @return $WindowBorderNormal; + } +} + +// Titlebar color +@function internal_titlebar($c: foreground) { + @if $c == foreground { + @return $WMForegroundNormal; + } + @if $c == background { + @return $WMBackgroundNormal; + } + @if $c == border { + @return $WMBorderNormal; + } +} + +@function internal_tooltip($c: foreground) { + @if $c == foreground { + @return $TooltipForegroundNormal; + } + @if $c == background { + @return $TooltipBackgroundNormal; + } + @if $c == border { + @return mix($TooltipForegroundNormal, $TooltipBackgroundNormal, 25%); + } +} + +@function internal_button_gradient($c, $state: background) { + @if $state == background { + @return linear-gradient( + to bottom, + change-color($c, $lightness: min(lightness($c) * 1.01, 100%)), + change-color($c, $lightness: min(lightness($c) / 1.03, 100%)) + ); + } @else { + @return linear-gradient( + to bottom, + change-color($c, $lightness: min(lightness($c) * 1.03, 100%)), + change-color($c, $lightness: min(lightness($c) / 1.1, 100%)) + ); + } +} + +@function internal_special_button($c: color) { + @if $c == default { + @return $SpecialButtonDefaultNormal; + } + @if $c == default-hover { + @return $SpecialButtonDefaultHover; + } + @if $c == default-active { + @return $SpecialButtonDefaultActive; + } + @if $c == warnning { + @return $SpecialButtonWarnningNormal; + } + @if $c == warnning-hover { + @return $SpecialButtonWarnningHover; + } + @if $c == warnning-active { + @return $SpecialButtonWarnningActive; + } +} + +$border_color: mix($WindowBackgroundNormal, $WindowForegroundNormal, 75%); +$menu_color: internal_window(background); + +$arrow_down: 'go-down-symbolic'; +$arrow_left: 'go-previous-symbolic'; +$arrow_right: 'go-next-symbolic'; +$arrow_up: 'go-up-symbolic'; + +$trough_color: gtkmix(gtk("@theme_bg_color"), gtk("@theme_fg_color"), 0.7); +$trough_color_backdrop: gtkmix( + gtk("@theme_unfocused_bg_color"), + gtk("@theme_unfocused_fg_color"), + 0.7 +); +$trough_color_insensitive: gtkmix( + gtk("@insensitive_bg_color"), + gtk("@insensitive_fg_color"), + 0.7 +); +$trough_color_backdrop_insensitive: gtkmix( + gtk("@insensitive_unfocused_bg_color"), + gtk("@insensitive_unfocused_fg_color"), + 0.7 +); + +$scrollbar_color: gtkmix($trough_color, gtk("@theme_text_color"), 0.5); +$scrollbar_color_backdrop: gtkmix( + $trough_color_backdrop, + gtk("@theme_unfocused_text_color"), + 0.5 +); + +// +// Color effects +// +// +// Color maps for color effect functions +// +$Color: ( + Disabled: $DisabledColor, + Inactive: $InactiveColor, +); +$ColorAmount: ( + Disabled: $DisabledColorAmount, + Inactive: $InactiveColorAmount, +); +$ColorEffect: ( + Disabled: $DisabledColorEffect, + Inactive: $InactiveColorEffect, +); +$ContrastAmount: ( + Disabled: $DisabledContrastAmount, + Inactive: $InactiveContrastAmount, +); +$ContrastEffect: ( + Disabled: $DisabledContrastEffect, + Inactive: $InactiveContrastEffect, +); +$IntensityAmount: ( + Disabled: $DisabledIntensityAmount, + Inactive: $InactiveIntensityAmount, +); +$IntensityEffect: ( + Disabled: $DisabledIntensityEffect, + Inactive: $InactiveIntensityEffect, +); + +$r: 3px; // standard radius + +@function internal_insensitive($c) { + @return internal_Color( + internal_Intensity(internal_Contrast($c, Disabled), Disabled), + Disabled + ); +} + +@function internal_backdrop($c) { + @if $InactiveEnable == True { + @return internal_Color( + internal_Intensity(internal_Contrast($c, Inactive), Inactive), + Inactive + ); + } @else { + @return $c; + } +} + +@function internal_Contrast($c, $state) { + @if map-get($ContrastEffect, $state) == 0 { + @return $c; + } + + @if map-get($ContrastEffect, $state) == 1 or map-get($ContrastEffect, $state) ==2 { + @if lightness(internal_window()) > lightness(internal_window(foreground)) { + @if lightness($c) < lightness(internal_window()) or + ($c == internal_titlebar(foreground) and lightness(internal_titlebar(background)) <= lightness(internal_titlebar(foreground))) + { + @return transparentize($c, map-get($ContrastAmount, $state)); + } @else { + @return $c; + } + } + @if lightness(internal_window()) <= lightness(internal_window(foreground)) { + @if lightness($c) > lightness(internal_window()) or + ($c == internal_titlebar(foreground) and lightness(internal_titlebar(background)) > lightness(internal_titlebar(foreground))) + { + @return transparentize($c, map-get($ContrastAmount, $state)); + } @else { + @return $c; + } + } + } +} + +@function internal_Color($c, $state) { + @if map-get($ColorEffect, $state) ==0 { + @return $c; + } + + @if map-get($ColorEffect, $state) ==1 { + @if map-get($ColorAmount, $state) >=0 { + @return desaturate($c, percentage(map-get($ColorAmount, $state))); + } @else { + @return saturate($c, percentage(abs(map-get($ColorAmount, $state)))); + } + } + + @if map-get($ColorEffect, $state) ==2 or map-get($ColorEffect, $state) ==3 { + // 2 and 3 seem to do the same + @return mix( + map-get($Color, $state), + $c, + percentage(map-get($ColorAmount, $state)) + ); + } +} + +@function internal_Intensity($c, $state) { + @if map-get($IntensityEffect, $state) == 0 { + @return $c; + } + @if map-get($IntensityEffect, $state) == 1 { + @if map-get($IntensityAmount, $state) >= 0 { + @return mix(white, $c, percentage(map-get($IntensityAmount, $state))); + } @else { + @return mix( + black, + $c, + percentage(abs(map-get($IntensityAmount, $state))) + ); + } + } + + @if map-get($IntensityEffect, $state) == 2 { + @if map-get($IntensityAmount, $state) == -1 { + @return change-color($c, $lightness: 100%); + } @else { + @return change-color( + $c, + $lightness: + min(lightness($c) / (1 + map-get($IntensityAmount, $state)), 100%) + ); + } + } + + @if map-get($IntensityEffect, $state) == 3 { + @return change-color( + $c, + $lightness: + min(lightness($c) * (1 + map-get($IntensityAmount, $state)), 100%) + ); + } +} + +@function internal_alpha2solid($c, $background) { + $amount: alpha($c); + $c: rgb(red($c), green($c), blue($c)); + @return mix($c, $background, $amount); +} \ No newline at end of file diff --git a/style-helper/data/color-scheme/base.colors b/style-helper/data/color-scheme/base.colors new file mode 100644 index 0000000000000000000000000000000000000000..3b616342810e1c1a286d8c190abe4b402b2359c6 --- /dev/null +++ b/style-helper/data/color-scheme/base.colors @@ -0,0 +1,8 @@ +[Global:Radius] +SLittle=2px +Little=4px +Middle=6px +Big=8px +SBig=10px +SSBig=12px +Menu=8px \ No newline at end of file diff --git a/style-helper/data/color-scheme/dark.colors b/style-helper/data/color-scheme/dark.colors new file mode 100644 index 0000000000000000000000000000000000000000..68aa2369a7763a8117560ce4258764e7db838e69 --- /dev/null +++ b/style-helper/data/color-scheme/dark.colors @@ -0,0 +1,82 @@ +[ColorEffects:Disabled] +Color=56,56,56 +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.1 +IntensityEffect=2 + +[ColorEffects:Inactive] +ChangeSelectionColor=true +Color=112,111,110 +ColorAmount=0.1 +ColorEffect=2 +ContrastAmount=0.05 +ContrastEffect=2 +Enable=true +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Default] +BackgroundNormal=0x222222 +ForegroundNormal=0xFFFFFF +BareNormal=0x595959 +Border=0x393939 +Selection=0x2EB3FF +Warning=0xFA4949 +Error=0xCC0000 +Success=darken(0x73D216, 0.1) +Link=lighten(Default::Selection, 0.1) +LinkVisited=lighten(Default::Selection, 0.2) + +[Colors:Widget] +BackgroundNormal=lighten(Default::BackgroundNormal, 0.3) +ForegroundNormal=Default::ForegroundNormal +BackgroundHover=mix(Widget::BackgroundNormal, Widget::ForegroundNormal, 0.9) +BackgroundActive=mix(Widget::BackgroundNormal, Widget::ForegroundNormal, 0.8) +BackgroundChecked=Default::Selection +ForegroundHover=mix(Widget::BackgroundNormal, Widget::ForegroundNormal, 0.3) +ForegroundActive=mix(Widget::BackgroundNormal, Widget::ForegroundNormal, 0.4) +ForegroundChecked=Default::Selection +BorderNormal=Default::Border +BorderHover=mix(Widget::BorderNormal, Widget::ForegroundNormal, 0.3) +BorderFocus=Default::Selection + +[Colors:Bare] +BackgroundNormal=Default::BareNormal +ForegroundNormal=Default::ForegroundNormal +BackgroundHover=mix(Bare::BackgroundNormal, Bare::ForegroundNormal, 0.9) +BackgroundActive=mix(Bare::BackgroundNormal, Bare::ForegroundNormal, 0.8) +BackgroundChecked=Default::Selection +ForegroundHover=mix(Bare::BackgroundNormal, Bare::ForegroundNormal, 0.3) +ForegroundActive=mix(Bare::BackgroundNormal, Bare::ForegroundNormal, 0.4) +ForegroundChecked=Default::Selection + +[Colors:Window] +BackgroundNormal=Default::BackgroundNormal +ForegroundNormal=Default::ForegroundNormal +BorderNormal=Default::Border + +[Colors:Selection] +BackgroundNormal=Default::Selection +ForegroundNormal=Default::ForegroundNormal + +[Colors:View] +BackgroundNormal=Default::BackgroundNormal +ForegroundNormal=Default::ForegroundNormal +BorderNormal=Default::Border +BorderFocus=Default::Selection + +[WM] +BackgroundNormal=Default::BackgroundNormal +ForegroundNormal=Default::ForegroundNormal +BorderNormal=Default::Border + +[Colors:SpecialButton] +DefaultNormal=Default::Selection +DefaultHover=mix(Default::Selection, Default::ForegroundNormal, 0.9) +DefaultActive=mix(Default::Selection, Default::ForegroundNormal,0.8) +WarnningNormal=Default::Error +WarnningHover=mix(Default::Error, Default::ForegroundNormal,0.9) +WarnningActive=mix(Default::Error, Default::ForegroundNormal,0.8) \ No newline at end of file diff --git a/style-helper/data/color-scheme/light.colors b/style-helper/data/color-scheme/light.colors new file mode 100644 index 0000000000000000000000000000000000000000..4f498b0688c9d003fb6a5cc12425bfcc86b7b588 --- /dev/null +++ b/style-helper/data/color-scheme/light.colors @@ -0,0 +1,84 @@ +[ColorEffects:Disabled] +Color=56,56,56 +ColorAmount=0 +ColorEffect=0 +ContrastAmount=0.65 +ContrastEffect=1 +IntensityAmount=0.1 +IntensityEffect=2 + +[ColorEffects:Inactive] +ChangeSelectionColor=true +Color=112,111,110 +ColorAmount=0.1 +ColorEffect=2 +ContrastAmount=0.05 +ContrastEffect=2 +Enable=true +IntensityAmount=0 +IntensityEffect=0 + +[Colors:Default] +BackgroundNormal=0xFFFFFF +ForegroundNormal=0x222222 +BareNormal=0xCCCCCC +Border=0xCCCCCC +Selection=0x2EB3FF +Warning=0xFA4949 +Error=0xCC0000 +Success=0x73D216 +Link=darken(Default::Selection, 0.1) +LinkVisited=darken(Default::Selection, 0.2) + +[Colors:Widget] +BackgroundNormal=darken(Default::BackgroundNormal, 0.05) +ForegroundNormal=Default::ForegroundNormal +BackgroundHover=mix(Widget::BackgroundNormal, Widget::ForegroundNormal, 0.9) +BackgroundActive=mix(Widget::BackgroundNormal, Widget::ForegroundNormal, 0.8) +BackgroundChecked=Default::Selection +ForegroundHover=mix(Widget::BackgroundNormal, Widget::ForegroundNormal, 0.3) +ForegroundActive=mix(Widget::BackgroundNormal, Widget::ForegroundNormal, 0.4) +ForegroundChecked=Default::Selection +BorderNormal=Default::Border +BorderHover=mix(Widget::BorderNormal, Widget::ForegroundNormal, 0.3) +BorderFocus=Default::Selection + +[Colors:Bare] +BackgroundNormal=Default::BareNormal +ForegroundNormal=Default::ForegroundNormal +BackgroundHover=mix(Bare::BackgroundNormal, Bare::ForegroundNormal, 0.9) +BackgroundActive=mix(Bare::BackgroundNormal, Bare::ForegroundNormal, 0.8) +BackgroundChecked=Default::Selection +ForegroundHover=mix(Bare::BackgroundNormal, Bare::ForegroundNormal, 0.3) +ForegroundActive=mix(Bare::BackgroundNormal, Bare::ForegroundNormal, 0.4) +ForegroundChecked=Default::Selection + +[Colors:Window] +BackgroundNormal=Default::BackgroundNormal +ForegroundNormal=Default::ForegroundNormal +BorderNormal=Default::Border +BackgroundAlternate=227,229,231 + +[Colors:Selection] +BackgroundNormal=Default::Selection +ForegroundNormal=Default::ForegroundNormal + + +[Colors:View] +BackgroundNormal=Default::BackgroundNormal +ForegroundNormal=Default::ForegroundNormal +BorderNormal=Default::Border +BorderFocus=Default::Selection + +[WM] +BackgroundNormal=Default::BackgroundNormal +ForegroundNormal=Default::ForegroundNormal +BorderNormal=Default::Border + +[Colors:SpecialButton] +DefaultNormal=Default::Selection +DefaultHover=mix(Default::Selection, Default::ForegroundNormal, 0.9) +DefaultActive=mix(Default::Selection, Default::ForegroundNormal,0.8) +WarnningNormal=Default::Error +WarnningHover=mix(Default::Error, Default::ForegroundNormal,0.9) +WarnningActive=mix(Default::Error, Default::ForegroundNormal,0.8) \ No newline at end of file diff --git a/style-helper/data/images/arrow-down-disabled.svg b/style-helper/data/images/arrow-down-disabled.svg new file mode 100755 index 0000000000000000000000000000000000000000..bd4c53096c6f546033090081b9c897d2dd0406fe --- /dev/null +++ b/style-helper/data/images/arrow-down-disabled.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/style-helper/data/images/arrow-down.svg b/style-helper/data/images/arrow-down.svg new file mode 100755 index 0000000000000000000000000000000000000000..e498f688359048cab13c143784861d85d107d7fc --- /dev/null +++ b/style-helper/data/images/arrow-down.svg @@ -0,0 +1,64 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/style-helper/data/images/arrow-left.png b/style-helper/data/images/arrow-left.png new file mode 100755 index 0000000000000000000000000000000000000000..a782c5d39ffd64cd538d08f7e16e1a931a987f52 Binary files /dev/null and b/style-helper/data/images/arrow-left.png differ diff --git a/style-helper/data/images/arrow-right-disabled.png b/style-helper/data/images/arrow-right-disabled.png new file mode 100755 index 0000000000000000000000000000000000000000..89abe842256727a4c17ea7cd154af0883b443d68 Binary files /dev/null and b/style-helper/data/images/arrow-right-disabled.png differ diff --git a/style-helper/data/images/arrow-right.png b/style-helper/data/images/arrow-right.png new file mode 100755 index 0000000000000000000000000000000000000000..d6fac88bd147b8a5a98b3480cd56bcd3b2266fa7 Binary files /dev/null and b/style-helper/data/images/arrow-right.png differ diff --git a/style-helper/data/images/arrow-up.svg b/style-helper/data/images/arrow-up.svg new file mode 100755 index 0000000000000000000000000000000000000000..738733537f7fcda4b0df86954ba6e9dca6c288ff --- /dev/null +++ b/style-helper/data/images/arrow-up.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/arrow.svg b/style-helper/data/images/arrow.svg new file mode 100755 index 0000000000000000000000000000000000000000..45c090373fd5d943d536072a2da899b63fabf85d --- /dev/null +++ b/style-helper/data/images/arrow.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/check-checked-active.svg b/style-helper/data/images/check-checked-active.svg new file mode 100755 index 0000000000000000000000000000000000000000..239820cc09b474da07c136ddddba56d924f45708 --- /dev/null +++ b/style-helper/data/images/check-checked-active.svg @@ -0,0 +1,16 @@ + + + + + + + diff --git a/style-helper/data/images/check-checked-disabled.svg b/style-helper/data/images/check-checked-disabled.svg new file mode 100755 index 0000000000000000000000000000000000000000..1412912e8f1da16bde713e058818e8bc598dccb4 --- /dev/null +++ b/style-helper/data/images/check-checked-disabled.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/check-checked-hover.svg b/style-helper/data/images/check-checked-hover.svg new file mode 100755 index 0000000000000000000000000000000000000000..ec8094d9379188bbd6cc0cc861475538b5c1b43a --- /dev/null +++ b/style-helper/data/images/check-checked-hover.svg @@ -0,0 +1,16 @@ + + + + + + + diff --git a/style-helper/data/images/check-checked-normal.svg b/style-helper/data/images/check-checked-normal.svg new file mode 100755 index 0000000000000000000000000000000000000000..98c355a964c35996ccac4e3e55ddb60f13c76b98 --- /dev/null +++ b/style-helper/data/images/check-checked-normal.svg @@ -0,0 +1,16 @@ + + + + + + + diff --git a/style-helper/data/images/check-indeterminate-active.svg b/style-helper/data/images/check-indeterminate-active.svg new file mode 100644 index 0000000000000000000000000000000000000000..9523e0479b83bc4a3d231f1c2638e16913f0ef9a --- /dev/null +++ b/style-helper/data/images/check-indeterminate-active.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/check-indeterminate-disabled.svg b/style-helper/data/images/check-indeterminate-disabled.svg new file mode 100644 index 0000000000000000000000000000000000000000..e4e6cb676515fce4d8e6dee20a14c025a9ae3d37 --- /dev/null +++ b/style-helper/data/images/check-indeterminate-disabled.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/check-indeterminate-hover.svg b/style-helper/data/images/check-indeterminate-hover.svg new file mode 100644 index 0000000000000000000000000000000000000000..baf180ace87de73b835004cf90b19e434c7057e4 --- /dev/null +++ b/style-helper/data/images/check-indeterminate-hover.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/check-indeterminate-normal.svg b/style-helper/data/images/check-indeterminate-normal.svg new file mode 100644 index 0000000000000000000000000000000000000000..68cb4c1c1a19c16a47300fdd7ce6896c774f5f3d --- /dev/null +++ b/style-helper/data/images/check-indeterminate-normal.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/check-unchecked-active.svg b/style-helper/data/images/check-unchecked-active.svg new file mode 100755 index 0000000000000000000000000000000000000000..d292465d7fa91b3329015a114d6898a5d329a3f3 --- /dev/null +++ b/style-helper/data/images/check-unchecked-active.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/check-unchecked-disabled.svg b/style-helper/data/images/check-unchecked-disabled.svg new file mode 100755 index 0000000000000000000000000000000000000000..65fb3d7858c28fe4cf61bb3597b1318c4c6407d9 --- /dev/null +++ b/style-helper/data/images/check-unchecked-disabled.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/check-unchecked-hover.svg b/style-helper/data/images/check-unchecked-hover.svg new file mode 100755 index 0000000000000000000000000000000000000000..375ff76f0a5f5cfe50324cbc73443ef6d0b68fdb --- /dev/null +++ b/style-helper/data/images/check-unchecked-hover.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/check-unchecked-normal.svg b/style-helper/data/images/check-unchecked-normal.svg new file mode 100755 index 0000000000000000000000000000000000000000..40ba2078ab7d15febae3e3760d2073d73efc75fd --- /dev/null +++ b/style-helper/data/images/check-unchecked-normal.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/checkbox-checked.svg b/style-helper/data/images/checkbox-checked.svg new file mode 100755 index 0000000000000000000000000000000000000000..f3c574164175f4e348b0f0deb98cc2603de89fb3 --- /dev/null +++ b/style-helper/data/images/checkbox-checked.svg @@ -0,0 +1,16 @@ + + + + + + + diff --git a/style-helper/data/images/checkbox-unchecked.svg b/style-helper/data/images/checkbox-unchecked.svg new file mode 100755 index 0000000000000000000000000000000000000000..307e7d0672a2a856be5cc16427fcf898f47a1ea4 --- /dev/null +++ b/style-helper/data/images/checkbox-unchecked.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/style-helper/data/images/expander-collapsed.png b/style-helper/data/images/expander-collapsed.png new file mode 100755 index 0000000000000000000000000000000000000000..59383f499157a862f5c76aa0f646ae2a79a4dda2 Binary files /dev/null and b/style-helper/data/images/expander-collapsed.png differ diff --git a/style-helper/data/images/expander-expanded.png b/style-helper/data/images/expander-expanded.png new file mode 100755 index 0000000000000000000000000000000000000000..e849fbdd533f6b18c8dbbcc8e6ea5d5ccb831211 Binary files /dev/null and b/style-helper/data/images/expander-expanded.png differ diff --git a/style-helper/data/images/panel-grid-horizontal-symbolic.svg b/style-helper/data/images/panel-grid-horizontal-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..a97283de0f724d277723d4a8846c28474a418fab --- /dev/null +++ b/style-helper/data/images/panel-grid-horizontal-symbolic.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/style-helper/data/images/panel-grid-vertical-symbolic.svg b/style-helper/data/images/panel-grid-vertical-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..9a11919b4feaf41ab10df1b76c2de1a7ceb3aa65 --- /dev/null +++ b/style-helper/data/images/panel-grid-vertical-symbolic.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/style-helper/data/images/radio-checked-active.svg b/style-helper/data/images/radio-checked-active.svg new file mode 100755 index 0000000000000000000000000000000000000000..d32fb2a1e98d4ce6d9fa7a590a2bf04f2587ec9a --- /dev/null +++ b/style-helper/data/images/radio-checked-active.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/radio-checked-disabled.svg b/style-helper/data/images/radio-checked-disabled.svg new file mode 100755 index 0000000000000000000000000000000000000000..30ae7907de20cab3e7f81e6671e9fc7350d959da --- /dev/null +++ b/style-helper/data/images/radio-checked-disabled.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/radio-checked-hover.svg b/style-helper/data/images/radio-checked-hover.svg new file mode 100755 index 0000000000000000000000000000000000000000..2f291d6f5a00a9be55421243c082aebf892a5916 --- /dev/null +++ b/style-helper/data/images/radio-checked-hover.svg @@ -0,0 +1,15 @@ + + + + + + + diff --git a/style-helper/data/images/radio-checked-normal.svg b/style-helper/data/images/radio-checked-normal.svg new file mode 100755 index 0000000000000000000000000000000000000000..49713b9fad3188f7b59b3cfb2426c415f58329f3 --- /dev/null +++ b/style-helper/data/images/radio-checked-normal.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/radio-selected.svg b/style-helper/data/images/radio-selected.svg new file mode 100755 index 0000000000000000000000000000000000000000..59d1563fca2f2d56ec6aec84db1e1ece740501ec --- /dev/null +++ b/style-helper/data/images/radio-selected.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/style-helper/data/images/radio-unchecked-active.svg b/style-helper/data/images/radio-unchecked-active.svg new file mode 100755 index 0000000000000000000000000000000000000000..e621e0b9586ff3fb49775a9c15904e2ddb3821ee --- /dev/null +++ b/style-helper/data/images/radio-unchecked-active.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/radio-unchecked-disabled.svg b/style-helper/data/images/radio-unchecked-disabled.svg new file mode 100755 index 0000000000000000000000000000000000000000..7e9f0db4851709b0d80cb5f9d8ffe142a2c499bd --- /dev/null +++ b/style-helper/data/images/radio-unchecked-disabled.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/style-helper/data/images/radio-unchecked-hover.svg b/style-helper/data/images/radio-unchecked-hover.svg new file mode 100755 index 0000000000000000000000000000000000000000..46f08123d5b0732dc33aead676ceed4eb1bbbf64 --- /dev/null +++ b/style-helper/data/images/radio-unchecked-hover.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/radio-unchecked-normal.svg b/style-helper/data/images/radio-unchecked-normal.svg new file mode 100755 index 0000000000000000000000000000000000000000..328d1e10ac7e17d7e66bbc3298b0785098df46ac --- /dev/null +++ b/style-helper/data/images/radio-unchecked-normal.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/scale-disabled.png b/style-helper/data/images/scale-disabled.png new file mode 100755 index 0000000000000000000000000000000000000000..da39b9c4bf574713986a4373e06355cb60c0276e Binary files /dev/null and b/style-helper/data/images/scale-disabled.png differ diff --git a/style-helper/data/images/scale-normal.png b/style-helper/data/images/scale-normal.png new file mode 100755 index 0000000000000000000000000000000000000000..e2efa1280e111174943603a0160257c843516c15 Binary files /dev/null and b/style-helper/data/images/scale-normal.png differ diff --git a/style-helper/data/images/scale-pressed.png b/style-helper/data/images/scale-pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..cdd1df9e36e8c046bcb74087a583bc2f36432dbf Binary files /dev/null and b/style-helper/data/images/scale-pressed.png differ diff --git a/style-helper/data/images/search-light.svg b/style-helper/data/images/search-light.svg new file mode 100755 index 0000000000000000000000000000000000000000..f17fe6f0e87b7f25deee875fd08300a2e72cd99e --- /dev/null +++ b/style-helper/data/images/search-light.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/search.svg b/style-helper/data/images/search.svg new file mode 100755 index 0000000000000000000000000000000000000000..7839ed2a6cec9bb965d23749689ba0e4b983d667 --- /dev/null +++ b/style-helper/data/images/search.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/style-helper/data/images/thumbnail.png b/style-helper/data/images/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..6c85629ac381da7dab6ade28676ef34bea70e42c Binary files /dev/null and b/style-helper/data/images/thumbnail.png differ diff --git a/style-helper/data/images/window-close-symbolic.svg b/style-helper/data/images/window-close-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..5dc32abc7c70ff8ba8c44338014f1ed3bed047ac --- /dev/null +++ b/style-helper/data/images/window-close-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/window-maximum-symbolic.svg b/style-helper/data/images/window-maximum-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..1177c1a0a03cf866a370f25335577e3832738895 --- /dev/null +++ b/style-helper/data/images/window-maximum-symbolic.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/window-minimum-symbolic.svg b/style-helper/data/images/window-minimum-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..5ea3228d45889ee77fcb947a825e141d606668c9 --- /dev/null +++ b/style-helper/data/images/window-minimum-symbolic.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/images/window-unmaximum-symbolic.svg b/style-helper/data/images/window-unmaximum-symbolic.svg new file mode 100644 index 0000000000000000000000000000000000000000..e909a38c1928aac5d08ae57078d0dff501e39170 --- /dev/null +++ b/style-helper/data/images/window-unmaximum-symbolic.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style-helper/data/qt5.scss b/style-helper/data/qt5.scss new file mode 100644 index 0000000000000000000000000000000000000000..82369bfaea3e80b2c149783bea4a822b9351dd6f --- /dev/null +++ b/style-helper/data/qt5.scss @@ -0,0 +1,521 @@ +@import "global"; +@import "functions"; + +// default +$default_background_normal: #{"" + internal_default(background)}; +$default_background_insensitive: #{"" + internal_insensitive(internal_default(background))}; +$default_background_backdrop: #{"" + internal_backdrop(internal_default(background))}; +$default_background_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_default(background)))}; +$default_foreground_normal: #{"" + internal_default(foreground)}; +$default_foreground_insensitive: #{"" + internal_insensitive(internal_default(foreground))}; +$default_foreground_backdrop: #{"" + internal_backdrop(internal_default(foreground))}; +$default_foreground_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_default(foreground)))}; +$default_border_normal: #{"" + internal_default(border)}; +$default_border_insensitive: #{"" + internal_insensitive(internal_default(border))}; +$default_border_backdrop: #{"" + internal_backdrop(internal_default(border))}; +$default_border_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_default(border)))}; +$default_warning: #{"" + internal_default(warning)}; +$default_warning_backdrop: #{"" + internal_backdrop(internal_default(warning))}; +$default_error: #{"" + internal_default(error)}; +$default_error_backdrop: #{"" + internal_backdrop(internal_default(error))}; +$default_success: #{"" + internal_default(success)}; +$default_success_backdrop: #{"" + internal_backdrop(internal_default(success))}; + +// selection +$selection_background_normal: #{"" + internal_selection(background)}; +$selection_foreground_normal: #{"" + internal_selection(foreground)}; +$selection_background_insensitive: #{"" + internal_insensitive(internal_selection(background))}; +$selection_foreground_insensitive: #{"" + internal_insensitive(internal_selection(foreground))}; +$selection_background_backdrop: #{"" + internal_backdrop(internal_selection(background))}; +$selection_foreground_backdrop: #{"" + internal_backdrop(internal_selection(foreground))}; + +// widget +$widget_background_normal: #{"" + internal_widget(background)}; +$widget_background_hover: #{"" + internal_widget(background-hover)}; +$widget_background_active: #{"" + internal_widget(background-active)}; +$widget_background_checked: #{"" + internal_widget(background-checked)}; +$widget_background_insensitive: #{"" + internal_insensitive(internal_widget(background))}; +$widget_background_backdrop: #{"" + internal_backdrop(internal_widget(background))}; +$widget_background_backdrop_checked: #{"" + internal_backdrop(internal_widget(background-checked))}; +$widget_background_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_widget(background)))}; +$widget_foreground_normal: #{"" + internal_widget(foreground)}; +$widget_foreground_hover: #{"" + internal_widget(foreground-hover)}; +$widget_foreground_active: #{"" + internal_widget(foreground-active)}; +$widget_foreground_checked: #{"" + internal_widget(foreground-checked)}; +$widget_foreground_insensitive: #{"" + internal_insensitive(internal_widget(foreground))}; +$widget_foreground_backdrop: #{"" + internal_backdrop(internal_widget(foreground))}; +$widget_foreground_backdrop_checked: #{"" + internal_backdrop(internal_widget(foreground-checked))}; +$widget_foreground_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_widget(foreground)))}; +$widget_border_normal: #{"" + internal_widget(border)}; +$widget_border_hover: #{"" + internal_widget(border-hover)}; +$widget_border_focus: #{"" + internal_widget(border-focus)}; +$widget_border_insensitive: #{"" + internal_insensitive(internal_widget(border))}; +$widget_border_backdrop: #{"" + internal_backdrop(internal_widget(border))}; +$widget_border_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_widget(border)))}; + +// bare +$bare_background_normal: #{"" + internal_bare(background)}; +$bare_background_hover: #{"" + internal_bare(background-hover)}; +$bare_background_active: #{"" + internal_bare(background-active)}; +$bare_background_checked: #{"" + internal_bare(background-checked)}; +$bare_background_insensitive: #{"" + internal_insensitive(internal_bare(background))}; +$bare_background_backdrop: #{"" + internal_backdrop(internal_bare(background))}; +$bare_background_backdrop_checked: #{"" + internal_backdrop(internal_bare(background-checked))}; +$bare_background_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_bare(background)))}; +$bare_foreground_normal: #{"" + internal_bare(foreground)}; +$bare_foreground_hover: #{"" + internal_bare(foreground-hover)}; +$bare_foreground_active: #{"" + internal_bare(foreground-active)}; +$bare_foreground_checked: #{"" + internal_bare(foreground-checked)}; +$bare_foreground_insensitive: #{"" + internal_insensitive(internal_bare(foreground))}; +$bare_foreground_backdrop: #{"" + internal_backdrop(internal_bare(foreground))}; +$bare_foreground_backdrop_checked: #{"" + internal_backdrop(internal_bare(foreground-checked))}; +$bare_foreground_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_bare(foreground)))}; + +// window +$window_background_normal: #{"" + internal_window(background)}; +$window_background_insensitive: #{"" + internal_insensitive(internal_window(background))}; +$window_background_backdrop: #{"" + internal_backdrop(internal_window(background))}; +$window_background_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_window(background)))}; +$window_foreground_normal: #{"" + internal_window(foreground)}; +$window_foreground_insensitive: #{"" + internal_insensitive(internal_window(foreground))}; +$window_foreground_backdrop: #{"" + internal_backdrop(internal_window(foreground))}; +$window_foreground_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_window(foreground)))}; +$window_border_normal: #{"" + internal_window(border)}; +$window_border_insensitive: #{"" + internal_insensitive(internal_window(border))}; +$window_border_backdrop: #{"" + internal_backdrop(internal_window(border))}; +$window_border_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_window(border)))}; + +// view +$view_background_normal: #{"" + internal_view(background)}; +$view_background_insensitive: #{"" + internal_insensitive(internal_view(background))}; +$view_background_backdrop: #{"" + internal_backdrop(internal_view(background))}; +$view_background_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_view(background)))}; +$view_foreground_normal: #{"" + internal_view(foreground)}; +$view_foreground_insensitive: #{"" + internal_insensitive(internal_view(foreground))}; +$view_foreground_backdrop: #{"" + internal_backdrop(internal_view(foreground))}; +$view_foreground_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_view(foreground)))}; +$view_border_normal: #{"" + internal_view(border)}; +$view_border_focus: #{"" + internal_view(border-focus)}; +$view_border_insensitive: #{"" + internal_insensitive(internal_view(border))}; +$view_border_backdrop: #{"" + internal_backdrop(internal_view(border))}; +$view_border_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_view(border)))}; + +// title bar +$titlebar_background_normal: #{"" + internal_titlebar(background)}; +$titlebar_background_insensitive: #{"" + internal_insensitive(internal_titlebar(background))}; +$titlebar_background_backdrop: #{"" + internal_backdrop(internal_titlebar(background))}; +$titlebar_background_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_titlebar(background)))}; +$titlebar_foreground_normal: #{"" + internal_titlebar(foreground)}; +$titlebar_foreground_insensitive: #{"" + internal_insensitive(internal_titlebar(foreground))}; +$titlebar_foreground_backdrop: #{"" + internal_backdrop(internal_titlebar(foreground))}; +$titlebar_foreground_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_titlebar(foreground)))}; +$titlebar_border_normal: #{"" + internal_titlebar(border)}; +$titlebar_border_insensitive: #{"" + internal_insensitive(internal_titlebar(border))}; +$titlebar_border_backdrop: #{"" + internal_backdrop(internal_titlebar(border))}; +$titlebar_border_backdrop_insensitive: #{"" + internal_insensitive(internal_backdrop(internal_titlebar(border)))}; + +// flags +$special_button_default_normal: #{"" + internal_special_button(default)}; +$special_button_default_hover: #{"" + internal_special_button(default-hover)}; +$special_button_default_active: #{"" + internal_special_button(default-active)}; +$special_button_default_insensitive: #{"" + internal_insensitive(internal_special_button(default))}; + +$special_button_warnning_normal: #{"" + internal_special_button(warnning)}; +$special_button_warnning_hover: #{"" + internal_special_button(warnning-hover)}; +$special_button_warnning_active: #{"" + internal_special_button(warnning-active)}; +$special_button_warnning_insensitive: #{"" + internal_insensitive(internal_special_button(warnning))}; + +// === Flag === +//NOTE:此处需要修改,.colors之中定义的Success为绿色,应该是错误的,而且其他类型也并未有其他状态的值 +Flag { + Warning: $default_warning; + Error: $default_error; + Success: $default_success; +} + +// === QPalette-兼容QPalette === +Palette:active { + Window: $window_background_normal; + WindowText: $window_foreground_normal; + + Base: $view_background_normal; + Text: $view_foreground_normal; + + Button: $widget_background_normal; + ButtonText: $widget_foreground_normal; + + Highlight: $selection_background_normal; + //HighlightedText: $selection_foreground_normal; + HighlightedText: #ffffff; + + ToolTipBase: $window_background_normal; + ToolTipText: $window_foreground_normal; +} + +//QPalette inacitve窗口的调色盘,暂时未使用 +Palette { + Window: $window_background_backdrop; + WindowText: $window_foreground_backdrop; + + Base: $view_background_backdrop; + Text: $view_foreground_backdrop; + + Button: $widget_background_backdrop; + ButtonText: $widget_foreground_backdrop; + + Highlight: $selection_background_backdrop; + HighlightedText: $selection_foreground_backdrop; + + ToolTipBase: $window_background_backdrop; + ToolTipText: $window_foreground_backdrop; +} + +Palette:disabled { + Window: $window_background_insensitive; + WindowText: $window_foreground_insensitive; + + Base: $view_background_insensitive; + Text: $view_foreground_insensitive; + + Button: $widget_background_insensitive; + ButtonText: $widget_foreground_insensitive; + + Highlight: $selection_background_insensitive; + HighlightedText: $selection_foreground_insensitive; + + ToolTipBase: $window_background_insensitive; + ToolTipText: $window_foreground_insensitive; +} + +// === Window === +Window { + Background: $window_background_normal; + Foreground: $window_foreground_normal; + Border: $window_border_normal; +} + +Window:disabled { + Background: $window_background_insensitive; + Foreground: $window_foreground_insensitive; + Border: $window_border_insensitive; +} + +// === Bare === +Bare { + Background: $bare_background_normal; + Foreground: $bare_foreground_normal; +} + +Bare:pressed { + Background: $bare_background_active; + Foreground: $bare_foreground_active; +} + +Bare:hover { + Background: $bare_background_hover; + Foreground: $bare_foreground_hover; +} + +Bare:checked { + Background: $bare_background_checked; + Foreground: $bare_foreground_checked; +} + +Bare:disabled { + Background: $bare_background_insensitive; + Foreground: $bare_foreground_insensitive; +} + +// === widget === +Widget { + Background: $widget_background_normal; + Foreground: $widget_foreground_normal; + Border: $widget_border_normal; +} + +Widget:hover { + Background: $widget_background_hover; + Foreground: $widget_foreground_hover; + //Border: $widget_border_hover; +} + +Widget:pressed { + Background: $widget_background_active; + Foreground: $widget_foreground_active; + Border: $widget_border_focus; +} + +Widget:checked, +Widget:selected { + Background: $widget_background_checked; + Foreground: $widget_foreground_checked; + Border: $widget_border_focus; +} + +Widget:disabled { + Background: $widget_background_insensitive; + Foreground: $widget_foreground_insensitive; + Border: $widget_border_insensitive; +} + +// === view === +View { + Background: $view_background_normal; + Foreground: $view_foreground_normal; + Border: $view_border_normal; +} + +View:focus { + Border: $view_border_focus; +} + +View:disabled { + Background: $view_background_insensitive; + Foreground: $view_foreground_insensitive; + Border: $view_border_insensitive; +} + +// === selection === +Selection { + Background: $selection_background_normal; + Foreground: $selection_foreground_normal; +} + +Selection:disabled { + Background: $selection_background_insensitive; + Foreground: $selection_foreground_insensitive; +} + +// === title bar === +TitleBar { + Background: $titlebar_background_normal; + Foreground: $titlebar_foreground_normal; + Border: $titlebar_border_normal; +} + +TitleBar:disabled { + Background: $titlebar_background_insensitive; + Foreground: $titlebar_foreground_insensitive; + Border: $titlebar_border_insensitive; +} + + +/** --- 提供给KiranStyle直接使用的相关属性 --- **/ +Frame +{ + Background: $window_background_normal; + Border: $window_border_normal; +} + +Frame:disabled +{ + Background: $window_background_insensitive; + Border: $window_border_insensitive; +} + +Button +{ + Background: $widget_background_normal; + Border: $widget_border_normal; +} + +Button:hover +{ + Background: $widget_background_hover; +} + +Button:pressed +{ + Background: $widget_background_active; +} + +Button:disabled +{ + Background: $widget_background_insensitive; + Border: $widget_border_insensitive; +} + +SpecialButton +{ + DefaultBackground:$special_button_default_normal; + WarnningBackground:$special_button_warnning_normal; +} + +SpecialButton:hover +{ + DefaultBackground:$special_button_default_hover; + WarnningBackground:$special_button_warnning_hover; +} + +SpecialButton:pressed +{ + DefaultBackground:$special_button_default_active; + WarnningBackground:$special_button_warnning_active; +} + +SpecialButton:disabled +{ + DefaultBackground:$special_button_default_insensitive; + WarnningBackground:$special_button_warnning_insensitive; +} + +Edit +{ + Background: $view_background_normal; + Foreground: $view_foreground_normal; + Border: $view_border_normal; +} + +Edit:focus +{ + Border: $view_border_focus; +} + +Edit:disabled +{ + Background: $view_background_insensitive; + Border: $view_border_insensitive; +} + +Indicator +{ + Arrow: $widget_foreground_normal; +} + +Indicator:disabled +{ + Arrow: $widget_foreground_insensitive; +} + +Combo +{ + Background: $widget_background_normal; + Border: $widget_border_normal; +} + +Combo:hover +{ + Background: $widget_background_hover; +} + +Combo:pressed +{ + Background: $widget_background_active; +} + +Combo:disabled +{ + Background: $widget_background_insensitive; + Border: $widget_border_insensitive; +} + +ItemView +{ + Branch: $widget_border_normal; +} + +ItemView:disabled +{ + Branch: $widget_border_insensitive; +} + +MenuBar +{ + ItemBackground: $window_background_normal; + EmptyAreaBackground: $window_background_normal; +} + +MenuBar:hover +{ + ItemBackground: $widget_background_hover; +} + +MenuBar:pressed +{ + ItemBackground: $widget_background_active; +} + +MenuBar:disabled +{ + ItemBackground: $window_background_insensitive; + EmptyAreaBackground: $window_background_insensitive; +} + +Progress +{ + Groove: $bare_background_normal; + Content: $bare_foreground_checked; +} + +Progress:disabled +{ + Groove: $bare_background_insensitive; + Content: $bare_foreground_insensitive; +} + +Scroll +{ + Slider: $bare_background_normal; +} + +Scroll:hover +{ + Slider: $bare_background_hover; +} + +Scroll:pressed +{ + Slider: $bare_background_checked; +} + +Scroll:disabled +{ + Slider: $bare_background_insensitive; +} + +Slider +{ + Groove: $bare_background_normal; + Content: $bare_foreground_checked; + HandleBorder: $bare_foreground_normal; + HandleBackground: $bare_foreground_checked; +} + +Slider:pressed +{ + HandleBorder: $bare_foreground_normal; + HandleBackground: $bare_foreground_normal; +} + +Slider:disabled +{ + Groove: $bare_background_insensitive; + Content: $bare_foreground_insensitive; + HandleBorder: $bare_foreground_insensitive; + HandleBackground: $bare_foreground_insensitive; +} + +SpinBox +{ + Background: $widget_background_normal; + Border: $widget_border_normal; + SignColor: $widget_foreground_normal; +} + +SpinBox:hover +{ + Background: $widget_background_hover; + Border: $widget_border_hover; +} + +SpinBox:pressed +{ + Background: $widget_background_active; +} + +SpinBox:disabled +{ + Background: $widget_background_insensitive; + Border: $widget_border_insensitive; + SignColor: $widget_foreground_insensitive; +} \ No newline at end of file diff --git a/style-helper/extract_color_from_scheme.py b/style-helper/extract_color_from_scheme.py new file mode 100644 index 0000000000000000000000000000000000000000..f52ff4b4e43d3404fcda8675e9eecfbe6fca9b8d --- /dev/null +++ b/style-helper/extract_color_from_scheme.py @@ -0,0 +1,253 @@ +import os +import argparse +import sys +import cairo +import re +import colorsys + +def make_sure_path_exists(path): + try: + os.makedirs(path) + except FileExistsError as exception: + pass + pass + +class ReadGlobals(): + def __init__(self,base_file_name): + self._colors = {} + self._colors = self.read_globals(base_file_name) + + def read_globals(self,filename): + with open(filename,'r',encoding="utf-8") as lines: + while True: + prefix_name = str() + for line in lines: + match_groups = re.search('([a-zA-Z]+)\]$', line) + prefixs = ['Radius', 'Disabled', 'Inactive', 'Default', 'Widget', 'Bare', 'Window', 'Selection', 'View', 'WM','SpecialButton'] + if match_groups != None and len(match_groups.groups()) == 1 and match_groups.groups()[0] in prefixs: + prefix_name = match_groups.groups()[0] + break + if len(prefix_name) == 0: + break + + for line in lines: + if line == '\n': + break + key = '{0}{1}'.format(prefix_name, line.strip().split('=')[0]) + value = line.strip().split('=')[1] + if value == '': + continue + self._colors[key] = self.trans_value(value) + return self._colors + + def _get_function_args(self, func_expr): + args = list() + cur_index = func_expr.find('(') + while cur_index != -1: + last_index = func_expr.find(',', cur_index + 1) + if last_index == -1: + last_index = func_expr.find(')', cur_index + 1) + + if last_index != -1 and last_index > cur_index + 1: + arg = func_expr[cur_index + 1:last_index].strip() + if len(arg) > 0: + args.append(arg) + + cur_index = last_index + return args + + def trans_value(self, value): + # function + if str.startswith(value, 'lighten'): + color, amount = self._get_function_args(value) + color = Color.int_to_rgb(self.trans_value(color)) + return Color.rgb_to_int(Color.lighter(color, float(amount))) + elif str.startswith(value, 'darken'): + color, amount = self._get_function_args(value) + color = Color.int_to_rgb(self.trans_value(color)) + return Color.rgb_to_int(Color.darker(color, float(amount))) + elif str.startswith(value, 'mix'): + color1, color2, weight = self._get_function_args(value) + color1 = Color.int_to_rgb(self.trans_value(color1)) + color2 = Color.int_to_rgb(self.trans_value(color2)) + return Color.rgb_to_int(Color.mix(color1, color2, float(weight))) + elif str.endswith(value, 'px'): + return value + # bool value + elif value.lower() == 'true' or value.lower() == 'false': + return bool(value) + # variable + elif not re.match('^\d', value): + return self._colors[value.replace(':', '')] + # float value + elif value.find('.') != -1: + return float(value) + # color value + elif value.find(',') != -1: + r = int(value.split(',')[0], 0) + g = int(value.split(',')[1], 0) + b = int(value.split(',')[2], 0) + return Color.rgb255_to_int((r, g, b)) + # integer value + else: + return int(value, 0) +class Color(object): + def __init__(self, colordict, name, name2=None, amount=0): + color = colordict[name] + self.colordict = colordict + + r, g, b = Color.int_to_rgb255(color) + if name2 is not None: + color2 = colordict[name2] + r2, g2, b2 = Color.int_to_rgb255(color2) + r = r * amount + float(r2) * (1 - amount) + g = g * amount + float(g2) * (1 - amount) + b = b * amount + float(b2) * (1 - amount) + + self.rgb255 = (int(r), int(g), int(b)) + self.rgb = (r / 255, g / 255, b / 255) + self.html = '#%02x%02x%02x' % self.rgb255 + self.insensitive = self._color_effect(self._intensity_effect(self.rgb, 'Disabled'), 'Disabled') + self.insensitive_alpha = self._contrast_effect(self.rgb, 'Disabled') + + if not self.colordict['InactiveEnable']: + self.inactive = self.rgb + self.inactive_alpha = 1.0 + else: + self.inactive = self._color_effect(self._intensity_effect(self.rgb, 'Inactive'), 'Inactive') + self.inactive_alpha = self._contrast_effect(self.rgb, 'Inactive') + self.inactive_insensitive = self._color_effect(self._intensity_effect(self.inactive, 'Disabled'), 'Disabled') + self.inactive_insensitive_alpha = max(self.inactive_alpha - (1 - self.insensitive_alpha), 0) + + @staticmethod + def int_to_rgb255(num): + r = ((num >> 16) & 0xff) + g = ((num >> 8) & 0xff) + b = (num & 0xff) + return (r, g, b) + + @staticmethod + def int_to_rgb(num): + r = float((num >> 16) & 0xff) / 255 + g = float((num >> 8) & 0xff) / 255 + b = float(num & 0xff) / 255 + return (r, g, b) + + @staticmethod + def rgb_to_int(rgb): + return (int(rgb[0] * 255) << 16) + (int(rgb[1] * 255) << 8) + int(rgb[2] * 255) + + @staticmethod + def rgb255_to_int(rgb): + return (rgb[0] << 16) + (rgb[1] << 8) + rgb[2] + + @staticmethod + def mix(color, mix_color, amount): + r = color[0] * amount + mix_color[0] * (1 - amount) + g = color[1] * amount + mix_color[1] * (1 - amount) + b = color[2] * amount + mix_color[2] * (1 - amount) + return (r, g, b) + + @staticmethod + def lighter(color, amount): + h, s, v = colorsys.rgb_to_hsv(color[0], color[1], color[2]) + v = min((1 + amount) * v, 1) + r, g, b = colorsys.hsv_to_rgb(h, s, v) + return (r, g, b) + + @staticmethod + def darker(color, amount): + h, s, v = colorsys.rgb_to_hsv(color[0], color[1], color[2]) + if amount == -1: + v = 1 + else: + v = min(v / (1 + amount), 1) + r, g, b = colorsys.hsv_to_rgb(h, s, v) + return (r, g, b) + + @staticmethod + def desaturate(color, amount): + h, s, v = colorsys.rgb_to_hsv(color[0], color[1], color[2]) + s = min(s * (1 - amount), 1) + r, g, b = colorsys.hsv_to_rgb(h, s, v) + return (r, g, b) + + def _intensity_effect(self, color, state): + effect = int(self.colordict[state + 'IntensityEffect']) + amount = float(self.colordict[state + 'IntensityAmount']) + if effect == 0: + (r, g, b) = color + elif effect == 1: + if amount >= 0: + (r, g, b) = self.mix((1.0, 1.0, 1.0), color, amount) + else: + (r, g, b) = self.mix((0.0, 0.0, 0.0), color, amount) + elif effect == 2: + (r, g, b) = self.darker(color, amount) + elif effect == 3: + (r, g, b) = self.lighter(color, amount) + return (r, g, b) + + def _color_effect(self, color, state): + effect = int(self.colordict[state + 'ColorEffect']) + amount = float(self.colordict[state + 'ColorAmount']) + effect_color = self.int_to_rgb(self.colordict[state + 'Color']) + if effect == 0: + (r, g, b) = color + elif effect == 1: + (r, g, b) = self.desaturate(color, amount) + else: + (r, g, b) = self.mix(effect_color, color, amount) + return (r, g, b) + + def _contrast_effect(self, color, state): + effect = int(self.colordict[state + 'ContrastEffect']) + amount = float(self.colordict[state + 'ContrastAmount']) + if effect == 0: + return 1.0 + else: + return 1.0 - amount + + def lighten_color(self, amount): + h, s, v = colorsys.rgb_to_hsv(self.rgb[0], self.rgb[1], self.rgb[2]) + v = (1 + amount) * v + r, g, b = colorsys.hsv_to_rgb(h, s, v) + self.rgb = (r, g, b) + self.rgb255 = (int(r * 255), int(g * 255), int(b * 255)) + + def gradient(self, state='', alpha=1.0): + if state == 'active': + stop1 = self.lighter(self.rgb, 0.03) + stop2 = self.darker(self.rgb, 0.10) + linear = cairo.LinearGradient(1, 1, 1, 19) + linear.add_color_stop_rgba(0.0, stop1[0], stop1[1], stop1[2], alpha) + linear.add_color_stop_rgba(1.0, stop2[0], stop2[1], stop2[2], alpha) + else: + stop1 = self.lighter(self.rgb, 0.01) + stop2 = self.darker(self.rgb, 0.03) + linear = cairo.LinearGradient(1, 1, 1, 19) + linear.add_color_stop_rgba(0.0, stop1[0], stop1[1], stop1[2], alpha) + linear.add_color_stop_rgba(1.0, stop2[0], stop2[1], stop2[2], alpha) + return linear +# ___________________________________________________________________________________ +parser = argparse.ArgumentParser(description='Generates Kiran assets according to the specified color ' 'scheme.') +parser.add_argument('--colorscheme', '-c', action='store', help='color scheme to use') +parser.add_argument('--basecolorscheme', '-b', action='store', help='base color scheme') +parser.add_argument('--generate-dir', '-d', action='store', help='generate color directory') + +args = parser.parse_args() + +generate_path = args.generate_dir +make_sure_path_exists(generate_path) + +_colors = ReadGlobals(args.basecolorscheme).read_globals(args.colorscheme) + +globalScss = open(os.path.join(generate_path, '_global.scss'), 'w') +for key in sorted(_colors): + if key == 'DisabledColor' or key == 'InactiveColor': + globalScss.write('${0}: #{1:x};\n'.format(key, _colors[key])) + elif type(_colors[key]) == type(str()) or ('Disabled' in key or 'Inactive' in key): + globalScss.write('${0}: {1};\n'.format(key, _colors[key])) + else: + globalScss.write('${0}: #{1:x};\n'.format(key, _colors[key])) +globalScss.close() \ No newline at end of file diff --git a/style-helper/include/kiran-palette.h b/style-helper/include/kiran-palette.h new file mode 100644 index 0000000000000000000000000000000000000000..0a5392f3e53e9ccf7de0bcde8666b23b0f1eee75 --- /dev/null +++ b/style-helper/include/kiran-palette.h @@ -0,0 +1,150 @@ +#pragma once + +#include +#include + +class KiranPalettePrivate; +class QPalette; +class QWidget; +class QStyleOption; + +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; +} +} // namespace Kiran + +// TODO: 添加public接口可手动更改palette类型修改当前颜色表不根据系统走 + +// KiranStyle提供的更便捷的方法取出KiranStyle里相关颜色的类 +class KiranPalette : public QObject +{ + Q_OBJECT + friend class SchemeLoaderFetcher; + +private: + explicit KiranPalette(QObject* parent = nullptr); + +public: + // 扩展状态类型,尽量避免使用传入QStyleOption进行匹配 + enum ColorState + { + Normal = 0x00000001, // 普通状态 + Active = 0x00000002, // 激活 + Checked = 0x00000004, // 选中状态 + Hover = 0x00000008, // 悬浮状态 + Disabled = 0x00000010 // 禁用状态 + }; + Q_ENUM(ColorState); + Q_DECLARE_FLAGS(ColorStateFlags, ColorState) + Q_FLAG(ColorStateFlags) + + // WARNING:WidgetType,WidgetColorRule,FlagColorRule枚举值切勿随意更改 + // 该枚举与SchemeLoader::SchemePropertyName相关联 + enum WidgetType + { + // 窗口类型或提示框类型 + Window = 0x00000020, + // 无边框控件 + Bare = 0x00000030, + // 默认控件类型 + Widget = 0x00000040, + // 视图类型,含有可选中的文本 + View = 0x00000050, + // 选中高亮的类型 + Selection = 0x00000060, + // 标题栏 + TitleBar = 0x00000070 + }; + Q_ENUM(WidgetType) + + enum WidgetColorRule + { + // 背景色 + Background = 0x00000000, + // 前景色 + Foreground = 0x00000001, + // 边框 + Border = 0x00000002 + }; + Q_ENUM(WidgetColorRule) + + enum FlagColorRule + { + // 告警-标志色 + Warning = 0x00000010, + // 错误-标志色 + Error = 0x00000011, + // 成功-标志色 + Success = 0x00000012 + }; + Q_ENUM(FlagColorRule) + + static KiranPalette* instance(); + ~KiranPalette(); + + /** + * 将Kiran调色盘转换至Qt调色盘 + * \param palette + */ + void polishPalette(QPalette* palette); + + /** + * 获取当前主题调色盘类型 + * \return 调色盘类型 + */ + KiranStyle::PaletteType paletteType(); + + /** + * 应用程序指定主题调色盘类型 + * Warning: 指定主题调色盘后,不会根据系统主题变更更改主题调色盘 + * \param type 调色盘类型 + */ + void setDesignatedPaletteType(KiranStyle::PaletteType type); + + /** + * 根据控件类型,控件状态,颜色类型取出颜色表之中预定的颜色 + * \param state 颜色状态,例如:悬浮,禁用,正常等 + * \param type 控件类型,详情见WidgetType枚举值定义 + * \param rule 控件颜色类型,例如:背景,前景,边框 + * \return 颜色表之中预定义的颜色 + */ + QColor color(ColorStateFlags state, WidgetType type, WidgetColorRule rule); + + /** + * 根据控件状态,标志颜色类型取出颜色表之中预定的颜色 + * \param rule 标志颜色类型,例如:成功,失败,警告等 + * \return 颜色表之中预定义的颜色 + */ + QColor color(FlagColorRule rule); + + /** + * 根据传入QWidget指针以及StyleOption进行伪状态猜测并进行匹配 + * 匹配顺序(找到一项即返回): + * 1. 全匹配: QStyleOption之中存在高优先状态(eg:禁用,按下,悬浮,聚焦) 和 传入的specialState 和 从派生关系与QStyleOption检测出的状态 + * 2. 匹配: 高优先级状态: QStyleOption之中存在高优先状态(eg:禁用,按下,悬浮,聚焦) + * 3. 匹配: 传入的specialState 和 从派生关系与QStyleOption检测出的扩展状态 + * 4. 匹配: 传入的specialState + * 5. 匹配: 不带伪状态选择器匹配 + * \param widget 控件 + * \param option QStyle绘制的选项 + * \param type 控件类型 + * \param rule 控件颜色类型 + * \param specialState 指定加入匹配的状态 + * \return 颜色表之中预定义的颜色 + */ + QColor color(const QWidget* widget, const QStyleOption* option, WidgetType type, WidgetColorRule rule, ColorState specialState = Normal); + + void dump(); + +signals: + void themeChanged(KiranStyle::PaletteType paletteType); + +private: + Kiran::Style::SchemeLoader* getSchemeLoader(); + +private: + KiranPalettePrivate* d_ptr; +}; diff --git a/style-helper/include/kiran-style-global.h b/style-helper/include/kiran-style-global.h new file mode 100644 index 0000000000000000000000000000000000000000..e8a5d3bcf793c99d28aeab7cfc3d8b7e5ec81e85 --- /dev/null +++ b/style-helper/include/kiran-style-global.h @@ -0,0 +1,11 @@ +#pragma once + +namespace KiranStyle +{ +enum PaletteType +{ + PALETTE_LIGHT, + PALETTE_DARK, + PALETTE_LAST +}; +} \ No newline at end of file diff --git a/style-helper/include/kiran-style-property.h b/style-helper/include/kiran-style-property.h new file mode 100644 index 0000000000000000000000000000000000000000..0367d189f646e6298401830e9c8c15d1e4ffeca5 --- /dev/null +++ b/style-helper/include/kiran-style-property.h @@ -0,0 +1,59 @@ +#pragma once + +class QPushButton; +class QProgressBar; +namespace Kiran +{ +namespace Style +{ +enum ButtonType +{ + BUTTON_Normal, /** < 普通按钮 */ + BUTTON_Default, /** < 默认按钮,突出显示 */ + BUTTON_Warning /** < 警告按钮,突出警示显示 */ +}; +enum ProgressBarTextPosition +{ + PROGRESS_TEXT_LEFT, /** < 水平-左侧 垂直-上侧 */ + PROGRESS_TEXT_CENTER, /** < 中间 */ + PROGRESS_TEXT_RIGHT /** < 水平-右侧 垂直-下侧 */ +}; +/** + * @brief + * KiranStyle自定义属性读写接口,可定制部分KiranStyle绘制细节,KiranStyle读出属性值进行特殊的绘制 + * 若控件当前style不为kiranstyle,则不会生效 + */ +namespace PropertyHelper +{ +/** + * @brief 获取按钮类型 + * @param btn 按钮控件,从该按钮中取出属性 + * @return 该按钮的Style绘制类型,采取什么风格进行绘制 + * @see Kiran::Style::ButtonType + */ +ButtonType getButtonType(const QPushButton *btn); +/** + * @brief 设置按钮类型 + * @param btn 按钮 + * @param type 按钮的Style绘制类型,采取什么风格进行绘制 + * @see Kiran::Style::ButtonType + */ +void setButtonType(QPushButton *btn, ButtonType type); + +/** + * @brief 设置QProgressBar进度文本显示位置 + * @param progressBar 进度条 + * @param textPosition 文本绘制位置 + * @see Kiran::Style::ProgressBarTextPosition + */ +void setProgressBarTextPosition(QProgressBar *progressBar, ProgressBarTextPosition textPosition); +/** + * @brief 获取QProgressBar文本绘制位置 + * @param progressBar 进度条 + * @return 文本绘制位置 + * @see Kiran::Style::ProgressBarTextPosition + */ +ProgressBarTextPosition getProgressBarTextPosition(const QProgressBar *progressBar); +} // namespace PropertyHelper +} // namespace Style +} // namespace Kiran \ No newline at end of file diff --git a/style-helper/kiran-style-helper.pc.in b/style-helper/kiran-style-helper.pc.in new file mode 100644 index 0000000000000000000000000000000000000000..e0380305c6f73a382a9d4288c65943a8cb567703 --- /dev/null +++ b/style-helper/kiran-style-helper.pc.in @@ -0,0 +1,9 @@ +LibDir=@KIRAN_STYLE_HELPER_LIB_DIR@ +IncludeDir=@KIRAN_STYLE_HELPER_INCLUDE_DIR@ + +Name: kiran-style-helper +Description: kiran style helper +Version: @KIRAN_STYLE_HELPER_VERSION@ +Requires: Qt5Core Qt5Gui Qt5Widgets +Libs: -L${LibDir} -l@TARGET_NAME@ +Cflags: -I${IncludeDir} \ No newline at end of file diff --git a/style-helper/kiranstyle-helper.qrc.in b/style-helper/kiranstyle-helper.qrc.in new file mode 100644 index 0000000000000000000000000000000000000000..066041fb17089857768439d4120d5b088c8b661f --- /dev/null +++ b/style-helper/kiranstyle-helper.qrc.in @@ -0,0 +1,53 @@ + + + @KIRAN_DARK_COLORS@ + @KIRAN_LIGHT_COLORS@ + + + + @CMAKE_CURRENT_SOURCE_DIR@/data/images/arrow.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/arrow-down.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/arrow-down-disabled.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/arrow-left.png + @CMAKE_CURRENT_SOURCE_DIR@/data/images/arrow-right.png + @CMAKE_CURRENT_SOURCE_DIR@/data/images/arrow-right-disabled.png + @CMAKE_CURRENT_SOURCE_DIR@/data/images/arrow-up.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/checkbox-checked.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/checkbox-unchecked.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-checked-active.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-checked-disabled.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-checked-hover.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-checked-normal.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-indeterminate-active.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-indeterminate-disabled.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-indeterminate-hover.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-indeterminate-normal.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-unchecked-active.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-unchecked-disabled.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-unchecked-hover.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/check-unchecked-normal.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/expander-collapsed.png + @CMAKE_CURRENT_SOURCE_DIR@/data/images/expander-expanded.png + @CMAKE_CURRENT_SOURCE_DIR@/data/images/panel-grid-horizontal-symbolic.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/panel-grid-vertical-symbolic.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/radio-checked-active.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/radio-checked-disabled.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/radio-checked-hover.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/radio-checked-normal.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/radio-selected.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/radio-unchecked-active.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/radio-unchecked-disabled.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/radio-unchecked-hover.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/radio-unchecked-normal.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/scale-disabled.png + @CMAKE_CURRENT_SOURCE_DIR@/data/images/scale-normal.png + @CMAKE_CURRENT_SOURCE_DIR@/data/images/scale-pressed.png + @CMAKE_CURRENT_SOURCE_DIR@/data/images/search.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/search-light.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/thumbnail.png + @CMAKE_CURRENT_SOURCE_DIR@/data/images/window-close-symbolic.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/window-maximum-symbolic.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/window-minimum-symbolic.svg + @CMAKE_CURRENT_SOURCE_DIR@/data/images/window-unmaximum-symbolic.svg + + diff --git a/style-helper/src/kiran-palette-private.cpp b/style-helper/src/kiran-palette-private.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b2bf7cd0f972bfb0efe399cfa75f77af6557ffbd --- /dev/null +++ b/style-helper/src/kiran-palette-private.cpp @@ -0,0 +1,63 @@ +#include "kiran-palette-private.h" +#include + +KiranPalettePrivate::KiranPalettePrivate(QObject* parent, KiranPalette* ptr) + : QObject(parent), + q_ptr(ptr), + m_customSpecialType(false), + m_type(KiranStyle::PALETTE_DARK), + m_schemeLoader(new Kiran::Style::SchemeLoader) +{ + //监听Kiran桌面主题变化信号 + connect(KiranAppearanceMonitor::instance(),&KiranAppearanceMonitor::gtkThemeChanged, + this,&KiranPalettePrivate::handleGtkThemeChanged); + handleGtkThemeChanged(KiranAppearanceMonitor::instance()->gtkTheme()); +} + +KiranPalettePrivate::~KiranPalettePrivate() +{ + delete m_schemeLoader; +} + +void KiranPalettePrivate::handleGtkThemeChanged(const QString& gtkTheme) +{ + //手动指定深浅主题 + if( m_customSpecialType ) + { + return; + } + else + { + KiranStyle::PaletteType type; + if( gtkTheme.compare("kiran") == 0 ) + { + type = KiranStyle::PALETTE_LIGHT; + } + else + { + type = KiranStyle::PALETTE_DARK; + } + updatePaletteType(type,false); + } +} + +QString KiranPalettePrivate::getSchemeFile(KiranStyle::PaletteType type) +{ + // clang-format off + static QString schemeFile[KiranStyle::PALETTE_LAST] = { + QStringLiteral(":/style-helper/colors/light.css"), + QStringLiteral(":/style-helper/colors/dark.css") + }; + // clang-format on + return schemeFile[type]; +} + +void KiranPalettePrivate::updatePaletteType(KiranStyle::PaletteType type, bool customSpecial) +{ + m_customSpecialType = customSpecial; + m_type = type; + QString schemeFile = getSchemeFile(type); + m_schemeLoader->load(schemeFile); + + emit q_ptr->themeChanged(type); +} diff --git a/style-helper/src/kiran-palette-private.h b/style-helper/src/kiran-palette-private.h new file mode 100644 index 0000000000000000000000000000000000000000..9f3dec0632b65743790a76843de5f09c20e66238 --- /dev/null +++ b/style-helper/src/kiran-palette-private.h @@ -0,0 +1,24 @@ +#pragma once +#include "kiran-palette.h" +#include "scheme-loader.h" +#include + +class KiranPalettePrivate : public QObject +{ + Q_DECLARE_PUBLIC(KiranPalette) +public: + KiranPalettePrivate(QObject* parent,KiranPalette* ptr);; + ~KiranPalettePrivate(); + + QString getSchemeFile(KiranStyle::PaletteType type); + void updatePaletteType(KiranStyle::PaletteType type,bool customSpecial=false); + +private slots: + void handleGtkThemeChanged(const QString& gtkTheme); + +private: + KiranPalette* q_ptr; + Kiran::Style::SchemeLoader* m_schemeLoader = nullptr; + bool m_customSpecialType = false; + KiranStyle::PaletteType m_type; +}; \ No newline at end of file diff --git a/style-helper/src/kiran-palette.cpp b/style-helper/src/kiran-palette.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3e8bd7c967e14836b923bf46f48df9fbd9671e80 --- /dev/null +++ b/style-helper/src/kiran-palette.cpp @@ -0,0 +1,150 @@ +#include "kiran-palette.h" +#include "kiran-palette-private.h" +#include "scheme-loader.h" +#include "kiran-appearance-monitor.h" + +#include +#include +#include +#include +#include + +using namespace Kiran::Style; + +static const QMap pseudoClassMap = { + {KiranPalette::Normal,Kiran::Style::PseudoClass_Unspecified}, + {KiranPalette::Active,Kiran::Style::PseudoClass_Active}, + {KiranPalette::Checked,Kiran::Style::PseudoClass_Checked}, + {KiranPalette::Hover,Kiran::Style::PseudoClass_Hover}, + {KiranPalette::Disabled,Kiran::Style::PseudoClass_Disabled} +}; + +KiranPalette* KiranPalette::instance() +{ + static QMutex mutex; + static QScopedPointer pInst; + + if (Q_UNLIKELY(!pInst)) + { + QMutexLocker locker(&mutex); + if (pInst.isNull()) + { + pInst.reset(new KiranPalette); + } + } + + return pInst.data(); +} + +KiranPalette::KiranPalette(QObject* parent) + : QObject(parent), + d_ptr(new KiranPalettePrivate(this,this)) +{ + +} + +KiranPalette::~KiranPalette() +{ + +} + +void KiranPalette::polishPalette(QPalette* palette) +{ + d_ptr->m_schemeLoader->polish(palette); +} + +KiranStyle::PaletteType KiranPalette::paletteType() +{ + return d_ptr->m_type; +} + +void KiranPalette::setDesignatedPaletteType(KiranStyle::PaletteType type) +{ + d_ptr->updatePaletteType(type,true); +} + +QColor KiranPalette::color(ColorStateFlags states,WidgetType type,WidgetColorRule rule) +{ + QColor defaultColor; + + static QMetaEnum metaEnum = QMetaEnum::fromType(); + const char* key = metaEnum.valueToKey(type+rule); + if( key == nullptr ) + { + qWarning() << "KiranPalette: can't convert -> SchemeLoader::SchemePropertyName!" << type << rule; + return defaultColor; + } + auto propertyEnum = static_cast(type+rule); + + int64_t pseudoClass = PseudoClass_Unspecified; + for(auto iter=pseudoClassMap.begin();iter!=pseudoClassMap.end();iter++) + { + if( states & iter.key() ) + { + pseudoClass |= iter.value(); + } + } + + return d_ptr->m_schemeLoader->getColor(propertyEnum,pseudoClass); +} + +QColor KiranPalette::color(FlagColorRule rule) +{ + QColor defaultColor; + + static QMetaEnum metaEnum = QMetaEnum::fromType(); + const char* key = metaEnum.valueToKey(rule); + if( key == nullptr ) + { + qWarning() << "KiranPalette: can't convert -> SchemeLoader::SchemePropertyName!" << rule; + return defaultColor; + } + auto propertyEnum = static_cast(rule); + + return d_ptr->m_schemeLoader->getColor(propertyEnum,PseudoClass_Unspecified); +} + +QColor KiranPalette::color(const QWidget* widget, + const QStyleOption* option, + KiranPalette::WidgetType type, + KiranPalette::WidgetColorRule rule, + KiranPalette::ColorState specialState) +{ + QColor defaultColor; + + static QMetaEnum metaEnum = QMetaEnum::fromType(); + const char* key = metaEnum.valueToKey(type+rule); + if( key == nullptr ) + { + qWarning() << "KiranPalette: can't convert -> SchemeLoader::SchemePropertyName!"; + return defaultColor; + } + auto propertyEnum = static_cast(type+rule); + + auto pseudoIter = pseudoClassMap.find(specialState); + if( pseudoIter == pseudoClassMap.end() ) + { + return defaultColor; + } + int64_t pseudoClass = pseudoIter.value(); + + QColor color = d_ptr->m_schemeLoader->getColor(widget,option,propertyEnum,pseudoClass); + if( !color.isValid() ) + { + qWarning() << "can't get color for:\n" + << "\twidget: " << widget << "\n" + << "\tproperty: " << type << rule << "-->" << propertyEnum << "\n" + << "\tspecial state:" << specialState << "-->" << pseudoClass << "\n"; + } + return color; +} + +void KiranPalette::dump() +{ + d_ptr->m_schemeLoader->dump(); +} + +Kiran::Style::SchemeLoader* KiranPalette::getSchemeLoader() +{ + return d_ptr->m_schemeLoader; +} diff --git a/style-helper/src/kiran-style-property.cpp b/style-helper/src/kiran-style-property.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7424651a8d45c0995cdc7c861fa533281323b73 --- /dev/null +++ b/style-helper/src/kiran-style-property.cpp @@ -0,0 +1,59 @@ +#include "kiran-style-property.h" +#include +#include +#include + +/// pushbutton +#define KIRAN_STYLE_PROPERTY_BUTTON_TYPE "_kiran_button_type" +/// spinbox +#define KIRAN_STYLE_PROPERTY_SPINBOX_POSITION "_kiran_spinbox_position" +/// progress bar +#define KIRAN_STYLE_PROPERTY_PROGRESSBAR_TEXT_POSITION "_kiran_progressbar_text_position" + +Kiran::Style::ButtonType Kiran::Style::PropertyHelper::getButtonType(const QPushButton *btn) +{ + ButtonType buttonType = BUTTON_Normal; + + QVariant var = btn->property(KIRAN_STYLE_PROPERTY_BUTTON_TYPE); + if( var.isValid() ) + { + bool toInt = false; + auto temp = static_cast(var.toInt(&toInt)); + if(toInt) + { + buttonType = temp; + } + } + + return buttonType; +} + +void Kiran::Style::PropertyHelper::setButtonType(QPushButton *btn, + Kiran::Style::ButtonType type) +{ + btn->setProperty(KIRAN_STYLE_PROPERTY_BUTTON_TYPE,type); +} + +void Kiran::Style::PropertyHelper::setProgressBarTextPosition(QProgressBar *progressBar, + Kiran::Style::ProgressBarTextPosition textPosition) +{ + progressBar->setProperty(KIRAN_STYLE_PROPERTY_PROGRESSBAR_TEXT_POSITION,textPosition); +} + +Kiran::Style::ProgressBarTextPosition Kiran::Style::PropertyHelper::getProgressBarTextPosition(const QProgressBar *progressBar) +{ + ProgressBarTextPosition progressBarTextPosition = PROGRESS_TEXT_RIGHT; + + QVariant var = progressBar->property(KIRAN_STYLE_PROPERTY_PROGRESSBAR_TEXT_POSITION); + if( var.isValid() ) + { + bool toInt = false; + auto temp = static_cast(var.toInt(&toInt)); + if(toInt) + { + progressBarTextPosition = temp; + } + } + + return progressBarTextPosition; +} diff --git a/style-helper/src/scheme-loader.cpp b/style-helper/src/scheme-loader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd70bae8a5722e7d861830917788502ca4b0f9a3 --- /dev/null +++ b/style-helper/src/scheme-loader.cpp @@ -0,0 +1,532 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "scheme-loader.h" +#include "kiran-appearance-monitor.h" + +#include +#include +#include +#include +#include + +using namespace Kiran::Style; +using namespace QCss; + +static const QMap pseudoMap = { + {QCss::PseudoClass_Unknown, "unknow"}, + {QCss::PseudoClass_Enabled, "enabled"}, + {QCss::PseudoClass_Disabled, "disabled"}, + {QCss::PseudoClass_Pressed, "pressed"}, + {QCss::PseudoClass_Focus, "focus"}, + {QCss::PseudoClass_Hover, "hover"}, + {QCss::PseudoClass_Checked, "checked"}, + {QCss::PseudoClass_Unchecked, "unchecked"}, + {QCss::PseudoClass_Indeterminate, "indeterminate"}, + {QCss::PseudoClass_Unspecified, "unspecifie"}, + {QCss::PseudoClass_Selected, "selected"}, + {QCss::PseudoClass_Horizontal, "horizontal"}, + {QCss::PseudoClass_Vertical, "vertical"}, + {QCss::PseudoClass_Window, "window"}, + {QCss::PseudoClass_Children, "chindren"}, + {QCss::PseudoClass_Sibling, "sibling"}, + {QCss::PseudoClass_Default, "default"}, + {QCss::PseudoClass_First, "first"}, + {QCss::PseudoClass_Last, "last"}, + {QCss::PseudoClass_Middle, "middle"}, + {QCss::PseudoClass_OnlyOne, "only one"}, + {QCss::PseudoClass_PreviousSelected, "previousSelected"}, + {QCss::PseudoClass_NextSelected, "NextSelected"}, + {QCss::PseudoClass_Flat, "flat"}, + {QCss::PseudoClass_Left, "left"}, + {QCss::PseudoClass_Right, "right"}, + {QCss::PseudoClass_Top, "top"}, + {QCss::PseudoClass_Bottom, "bottom"}, + {QCss::PseudoClass_Exclusive, "exclusive"}, + {QCss::PseudoClass_NonExclusive, "nonExclusive"}, + {QCss::PseudoClass_Frameless, "frameless"}, + {QCss::PseudoClass_ReadOnly, "readOnly"}, + {QCss::PseudoClass_Active, "active"}, + {QCss::PseudoClass_Closable, "closable"}, + {QCss::PseudoClass_Movable, "movable"}, + {QCss::PseudoClass_Floatable, "floatable"}, + {QCss::PseudoClass_Minimized, "minimized"}, + {QCss::PseudoClass_Maximized, "maximized"}, + {QCss::PseudoClass_On, "on"}, + {QCss::PseudoClass_Off, "off"}, + {QCss::PseudoClass_Editable, "editable"}, + {QCss::PseudoClass_Item, "item"}, + {QCss::PseudoClass_Closed, "closed"}, + {QCss::PseudoClass_Open, "open"}, + {QCss::PseudoClass_EditFocus, "editFocus"}, + {QCss::PseudoClass_Alternate, "alternate"}, + {QCss::PseudoClass_Any, "any"}, +}; + +bool FuzzyMatch(const QVector& selectors, quint64 pseudoClass); + +Kiran::Style::SchemeLoader::SchemeLoader(QObject* parent) + : QObject(parent), + m_isValid(false), + m_styleScheme(new QCss::StyleSheet) +{ + +} + +SchemeLoader::~SchemeLoader() +{ + delete m_styleScheme; +} + +QVariant SchemeLoader::fetchPropertyValue(const QWidget* widget, + const QStyleOption* opt, + SchemeLoader::SchemePropertyName name, + SchemeLoader::SchemeValueType valueType, + quint64 specialPseudlClasss) +{ + /// 获取没伪选择器的样式 + QVariant defaultValue = fetchPropertyValue(name, QCss::PseudoClass_Unspecified, valueType); + + /// 获取当前控件的其他状态 用于辅助增加伪状态选择匹配的准确性 + quint64 styleOptionPseudoClass = convertStyleOption2PseudoClass(widget, opt); + quint64 extendPresudoClass = styleOptionPseudoClass | specialPseudlClasss; + + /// 关键状态,做为匹配的关键因素 + quint64 keyClass = 0; + if (!(opt->state & QStyle::State_Enabled)) + { /// disable状态 + keyClass = QCss::PseudoClass_Disabled; + } + else if (opt->state & QStyle::State_Sunken) + { /// 凹陷或按下 + keyClass = QCss::PseudoClass_Pressed; + } + else if (opt->state & QStyle::State_MouseOver) + { /// 鼠标悬浮 + keyClass = QCss::PseudoClass_Hover; + } + else if (opt->state & QStyle::State_HasFocus) + { /// 聚焦  + keyClass = QCss::PseudoClass_Focus; + } + +#if 0 + QMetaEnum enumInfo = QMetaEnum::fromType(); + qInfo() << "fetch style detail:" << "\n" + << " widget class: " << widget->metaObject()->className() << "\n" + << " widget: " << widget->objectName() << "\n" + << " selector: " << enumInfo.valueToKey(name) << "\n" + << " special pseudo:" << pseudoClass2String(specialPseudlClasss) << "\n" + << " key pseudo: " << pseudoClass2String(keyClass) << "\n" + << " extend pseudo: " << pseudoClass2String(extendPresudoClass); +#endif + + if (keyClass != 0) + { + QVariant bestFitVar = fetchPropertyValue(name, keyClass | extendPresudoClass, valueType); + if (bestFitVar.isValid()) + { + return bestFitVar; + } + QVariant keyVar = fetchPropertyValue(name, keyClass, valueType); + if (keyVar.isValid()) + { + return keyVar; + } + } + + if (extendPresudoClass != 0) + { + QVariant extentVar = fetchPropertyValue(name, extendPresudoClass, valueType); + if (extentVar.isValid()) + { + return extentVar; + } + } + + if (specialPseudlClasss != 0) + { + QVariant specialVar = fetchPropertyValue(name, specialPseudlClasss, valueType); + if (specialVar.isValid()) + { + return specialVar; + } + } + + return defaultValue; +} + +QVariant SchemeLoader::fetchPropertyValue(SchemeLoader::SchemePropertyName propertyName, + quint64 pseudoClass, + SchemeLoader::SchemeValueType valueType) +{ + QReadLocker readLocker(&m_rwLock); + QVariant var = searchCacheEntry(propertyName, pseudoClass); + if (var.isValid()) + { + return var; + } + + //使用元对象中的信息将selector转换成字符串 + QMetaEnum metaEnum = QMetaEnum::fromType(); + QString strSelector = metaEnum.valueToKey(propertyName); + + //将字符串拆分成类型选择器和属性名进行匹配 + const QStringList selectors = strSelector.split("_"); + const QString type = selectors.at(0); + const QString property = selectors.at(1); + + auto values = m_styleScheme->nameIndex.values(type); + for (const StyleRule& rule : values) + { + if (!FuzzyMatch(rule.selectors, pseudoClass)) + { + continue; + } + + for (const Declaration& declartion : rule.declarations) + { + if (declartion.d->property != property) + { + continue; + } + QVariant propertyValue; + switch (valueType) + { + case SCHEME_VALUE_COLOR: + { + propertyValue = declartion.colorValue(); +#if 0 + QString pseudoClassStr = pseudoClass2String(pseudoClass); + qInfo() << "fuzzy match selector:" << type << pseudoClassStr << property << declartion.colorValue(); +#endif + break; + } + case SCHEME_VALUE_INT: + { + int i = 0; + declartion.intValue(&i); + propertyValue = i; + break; + } + case SCHEME_VALUE_URL: + propertyValue = declartion.uriValue(); + break; + case SCHEME_VALUE_REAL: + { + qreal real = 0.0; + declartion.realValue(&real); + propertyValue = real; + break; + } + default: + qWarning() << "this type is not supported at this time"; + continue; + } + readLocker.unlock(); + QWriteLocker writeLocker(&m_rwLock); + insertCacheEntry(propertyName, pseudoClass, propertyValue); + return propertyValue; + } + } + + readLocker.unlock(); + QWriteLocker writeLocker(&m_rwLock); + insertCacheEntry(propertyName, pseudoClass, QVariant()); + return QVariant(); +} + +void SchemeLoader::dump() +{ + QMultiHash::iterator iter; + QReadLocker readLocker(&m_rwLock); + for (iter = m_styleScheme->nameIndex.begin(); + iter != m_styleScheme->nameIndex.end(); + iter++) + { + qInfo("//=== %s ====/", iter.key().toStdString().c_str()); + StyleRule rule = iter.value(); + for (auto iter : rule.selectors) + { + qInfo() << "\tselector:" << pseudoClass2String(iter.pseudoClass()) << iter.pseudoClass(); + } + for (const Declaration& declaration : rule.declarations) + { + qInfo("\tproperty:%s", declaration.d->property.toStdString().c_str()); + for (const Value& value : declaration.d->values) + { + qInfo() << "\t\ttype: " << value.type; + qInfo() << "\t\tvalue:" << value.variant; + } + } + } +} + +bool SchemeLoader::load(const QString& schemeFile) +{ + QWriteLocker writeLocker(&m_rwLock); + + //clean cache + m_isValid = false; + m_styleSchemeCache.clear(); + + QCss::Parser parser(schemeFile, true); + if (!parser.parse(m_styleScheme)) + { + qWarning() << "parse style scheme failed!" << parser.errorSymbol().text; + return false; + } + + m_isValid = true; + qInfo() << "load" << schemeFile << "succeed!"; + return true; +} + +QString SchemeLoader::pseudoClass2String(quint64 pseudoClass) +{ + QStringList pseudoClassList; + for (auto iter = pseudoMap.begin(); + iter != pseudoMap.end(); + iter++) + { + if (pseudoClass & iter.key()) + { + pseudoClassList.append(iter.value()); + } + } + return pseudoClassList.join(","); +} + +bool SchemeLoader::isValid() +{ + return m_isValid; +} + +#define PALETTE_COLOR(palette, colorGroup, colorRole, pseudoClass) \ + { \ + palette->setColor(colorGroup, QPalette::colorRole, getColor(Palette_##colorRole, pseudoclass)); \ + } + +void SchemeLoader::polish(QPalette* palette) +{ + // clang-format off + static const QVector< QPair > states = { + {QPalette::Active,QCss::PseudoClass_Active}, + {QPalette::Disabled,QCss::PseudoClass_Disabled}, +// {QPalette::Inactive,QCss::PseudoClass_Unspecified} //不给inactive单独做状态 + {QPalette::Inactive,QCss::PseudoClass_Active} + }; + // clang-format on + + for (const auto& pair : states) + { + QPalette::ColorGroup group = pair.first; + quint64 pseudoclass = pair.second; + + PALETTE_COLOR(palette, group, Window, pseudoclass); + PALETTE_COLOR(palette, group, WindowText, pseudoclass); + + PALETTE_COLOR(palette, group, Base, pseudoclass); + PALETTE_COLOR(palette, group, Text, pseudoclass); + + //FIXME:暂时交替色使用同样的颜色 + palette->setColor(group,QPalette::AlternateBase,palette->color(group,QPalette::Window)); + + PALETTE_COLOR(palette, group, Button, pseudoclass); + PALETTE_COLOR(palette, group, ButtonText, pseudoclass); + + PALETTE_COLOR(palette, group, Highlight, pseudoclass); + PALETTE_COLOR(palette, group, HighlightedText, pseudoclass); + + PALETTE_COLOR(palette, group, ToolTipBase, pseudoclass); + PALETTE_COLOR(palette, group, ToolTipText, pseudoclass); + } +} + +QColor SchemeLoader::getColor(const QWidget* widget, const QStyleOption* opt, SchemeLoader::SchemePropertyName name, quint64 specialPseudoClass) +{ + QVariant var = fetchPropertyValue(widget, opt, name, SCHEME_VALUE_COLOR, specialPseudoClass); + if (var.isValid()) + { + return var.value(); + } + return QColor(); +} + +QColor SchemeLoader::getColor(SchemeLoader::SchemePropertyName propertyName, quint64 pseudoClass) +{ + QVariant var = fetchPropertyValue(propertyName, pseudoClass, SCHEME_VALUE_COLOR); + if (var.isValid()) + { + return var.value(); + } + return QColor(); +} + +QString SchemeLoader::getUrl(const QWidget* widget, const QStyleOption* opt, SchemeLoader::SchemePropertyName name, quint64 specialPseudoClass) +{ + QVariant var = fetchPropertyValue(widget,opt,name,SCHEME_VALUE_URL); + if(var.isValid()) + { + return var.value(); + } + return QString(); +} + +QString SchemeLoader::getUrl(SchemeLoader::SchemePropertyName name, quint64 pseudoClass) +{ + QVariant var = fetchPropertyValue(name, pseudoClass, SCHEME_VALUE_URL); + if (var.isValid()) + { + return var.value(); + } + return QString(); +} + + +/// 模糊匹配,优先完全匹配,再着判断待匹配伪状态包含选择器的伪状态 +/// \param selectors 选择器 +/// \param pseudoClass 待匹配的伪状态 +/// \param fuzzyMatchSelector 模糊匹配到的选择器 +/// \return 是否匹配 +bool FuzzyMatch(const QVector& selectors, + quint64 pseudoClass) +{ + bool isMatch = false; + + ///完整匹配 + for (const Selector& selector : selectors) + { + if (selector.pseudoClass() == pseudoClass) + { + isMatch = true; + break; + } + } + + ///包含 + if (!isMatch) + { + for (const Selector& selector : selectors) + { + if ((selector.pseudoClass() | pseudoClass) == pseudoClass) + { + isMatch = true; + break; + } + } + } + + return isMatch; +} + +quint64 SchemeLoader::convertStyleOption2PseudoClass(const QWidget* widget, + const QStyleOption* opt) +{ + quint64 pesudoClass = 0; + + if (opt->state & QStyle::State_Enabled) + { + pesudoClass |= QCss::PseudoClass_Enabled; + } + + if (opt->state & QStyle::State_Active) + pesudoClass |= QCss::PseudoClass_Active; + + if (opt->state & QStyle::State_Window) + pesudoClass |= QCss::PseudoClass_Window; + + if (opt->state & QStyle::State_On) + pesudoClass |= (QCss::PseudoClass_On | QCss::PseudoClass_Checked); + + if (opt->state & QStyle::State_Off) + pesudoClass |= (QCss::PseudoClass_Off | QCss::PseudoClass_Unchecked); + + if (opt->state & QStyle::State_NoChange) + pesudoClass |= QCss::PseudoClass_Indeterminate; + + if (opt->state & QStyle::State_Selected) + pesudoClass |= QCss::PseudoClass_Selected; + + if (opt->state & QStyle::State_Horizontal) + pesudoClass |= QCss::PseudoClass_Horizontal; + else + pesudoClass |= QCss::PseudoClass_Vertical; + + if (opt->state & (QStyle::State_Open | QStyle::State_On | QStyle::State_Sunken)) + { + if( widget && ( widget->inherits("QPushButton") || widget->inherits("QToolButton") ) ) + { + pesudoClass |= QCss::PseudoClass_Pressed; + } + else + { + pesudoClass |= QCss::PseudoClass_Open; + } + } + else + { + pesudoClass |= QCss::PseudoClass_Closed; + } + + if (opt->state & QStyle::State_Children) + pesudoClass |= QCss::PseudoClass_Children; + + if (opt->state & QStyle::State_Sibling) + pesudoClass |= QCss::PseudoClass_Sibling; + + if (opt->state & QStyle::State_ReadOnly) + pesudoClass |= QCss::PseudoClass_ReadOnly; + + if (opt->state & QStyle::State_Item) + pesudoClass |= QCss::PseudoClass_Item; + + if (opt->state & QStyle::State_HasFocus) + pesudoClass |= QCss::PseudoClass_Focus; + + return pesudoClass; +} + +//NOTE:此处未上锁,上层调用上锁 +void SchemeLoader::insertCacheEntry(SchemeLoader::SchemePropertyName propertyName, + quint64 pseudoStatus, + QVariant var) +{ + QMetaEnum propertyMateEnum = QMetaEnum::fromType(); + QString selectorString = propertyMateEnum.valueToKey(propertyName); + + QString key = QString("%1:%2").arg(selectorString).arg(pseudoStatus); + + if (m_styleSchemeCache.find(key) == m_styleSchemeCache.end()) + { + m_styleSchemeCache.insert(key, var); + } +} + +//NOTE:此处未上锁,上层调用需上锁 +QVariant SchemeLoader::searchCacheEntry(SchemeLoader::SchemePropertyName propertyName, + quint64 pseudoStatus) +{ + QMetaEnum propertyMateEnum = QMetaEnum::fromType(); + QString selectorString = propertyMateEnum.valueToKey(propertyName); + + QString key = QString("%1:%2").arg(selectorString).arg(pseudoStatus); + + QVariant var; + auto iter = m_styleSchemeCache.find(key); + if (iter != m_styleSchemeCache.end()) + { + var = iter.value(); + } + return var; +} \ No newline at end of file diff --git a/style-helper/src/scheme-loader.h b/style-helper/src/scheme-loader.h new file mode 100644 index 0000000000000000000000000000000000000000..c10d4fdabf47b0dcfe6c3219ce8f61ea2ebb15b2 --- /dev/null +++ b/style-helper/src/scheme-loader.h @@ -0,0 +1,264 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_LIB_SRC_SCHEME_LOADER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_LIB_SRC_SCHEME_LOADER_H_ + +#include +#include +#include +#include +#include +#include +#include "kiran-style-global.h" + +class QPalette; +class QWidget; +class QStyleOption; + +namespace QCss +{ +class StyleSheet; +} + +namespace Kiran +{ +namespace Style +{ +enum PseudoClassType +{ + PseudoClass_Unknown = Q_UINT64_C(0x0000000000000000), + PseudoClass_Enabled = Q_UINT64_C(0x0000000000000001), + PseudoClass_Disabled = Q_UINT64_C(0x0000000000000002), + PseudoClass_Pressed = Q_UINT64_C(0x0000000000000004), + PseudoClass_Focus = Q_UINT64_C(0x0000000000000008), + PseudoClass_Hover = Q_UINT64_C(0x0000000000000010), + PseudoClass_Checked = Q_UINT64_C(0x0000000000000020), + PseudoClass_Unchecked = Q_UINT64_C(0x0000000000000040), + PseudoClass_Indeterminate = Q_UINT64_C(0x0000000000000080), + PseudoClass_Unspecified = Q_UINT64_C(0x0000000000000100), + PseudoClass_Selected = Q_UINT64_C(0x0000000000000200), + PseudoClass_Horizontal = Q_UINT64_C(0x0000000000000400), + PseudoClass_Vertical = Q_UINT64_C(0x0000000000000800), + PseudoClass_Window = Q_UINT64_C(0x0000000000001000), + PseudoClass_Children = Q_UINT64_C(0x0000000000002000), + PseudoClass_Sibling = Q_UINT64_C(0x0000000000004000), + PseudoClass_Default = Q_UINT64_C(0x0000000000008000), + PseudoClass_First = Q_UINT64_C(0x0000000000010000), + PseudoClass_Last = Q_UINT64_C(0x0000000000020000), + PseudoClass_Middle = Q_UINT64_C(0x0000000000040000), + PseudoClass_OnlyOne = Q_UINT64_C(0x0000000000080000), + PseudoClass_PreviousSelected = Q_UINT64_C(0x0000000000100000), + PseudoClass_NextSelected = Q_UINT64_C(0x0000000000200000), + PseudoClass_Flat = Q_UINT64_C(0x0000000000400000), + PseudoClass_Left = Q_UINT64_C(0x0000000000800000), + PseudoClass_Right = Q_UINT64_C(0x0000000001000000), + PseudoClass_Top = Q_UINT64_C(0x0000000002000000), + PseudoClass_Bottom = Q_UINT64_C(0x0000000004000000), + PseudoClass_Exclusive = Q_UINT64_C(0x0000000008000000), + PseudoClass_NonExclusive = Q_UINT64_C(0x0000000010000000), + PseudoClass_Frameless = Q_UINT64_C(0x0000000020000000), + PseudoClass_ReadOnly = Q_UINT64_C(0x0000000040000000), + PseudoClass_Active = Q_UINT64_C(0x0000000080000000), + PseudoClass_Closable = Q_UINT64_C(0x0000000100000000), + PseudoClass_Movable = Q_UINT64_C(0x0000000200000000), + PseudoClass_Floatable = Q_UINT64_C(0x0000000400000000), + PseudoClass_Minimized = Q_UINT64_C(0x0000000800000000), + PseudoClass_Maximized = Q_UINT64_C(0x0000001000000000), + PseudoClass_On = Q_UINT64_C(0x0000002000000000), + PseudoClass_Off = Q_UINT64_C(0x0000004000000000), + PseudoClass_Editable = Q_UINT64_C(0x0000008000000000), + PseudoClass_Item = Q_UINT64_C(0x0000010000000000), + PseudoClass_Closed = Q_UINT64_C(0x0000020000000000), + PseudoClass_Open = Q_UINT64_C(0x0000040000000000), + PseudoClass_EditFocus = Q_UINT64_C(0x0000080000000000), + PseudoClass_Alternate = Q_UINT64_C(0x0000100000000000), + // The Any specifier is never generated, but can be used as a wildcard in searches. + PseudoClass_Any = Q_UINT64_C(0x0000ffffffffffff) +}; +//提供给KiranStyle以及KiranWidget内部获取颜色表定义 +class SchemeLoaderPrivate; +class SchemeLoader : public QObject +{ + Q_OBJECT +public: + //WARNING:枚举值切勿随意更改 + //该枚举与KiranPalette之中的WidgetType,WidgetColorRule,FlagColorRule相关联 + + //clang-format off + enum SchemePropertyName + { + // === Palette === // + Palette_Window = 0x00000000, + Palette_WindowText = 0x00000001, + Palette_Base = 0x00000002, + Palette_Text = 0x00000003, + Palette_Button = 0x00000004, + Palette_ButtonText = 0x00000005, + Palette_Highlight = 0x00000006, + Palette_HighlightedText = 0x00000007, + Palette_ToolTipBase = 0x00000008, + Palette_ToolTipText = 0x00000009, + + // === Flag === /// + Flag_Warning = 0x00000010, + Flag_Error = 0x00000011, + Flag_Success = 0x00000012, + + // === Window === /// + Window_Background = 0x00000020, + Window_Foreground = 0x00000021, + Window_Border = 0x00000022, + + // === Bare === /// + Bare_Background = 0x00000030, + Bare_Foreground = 0x00000031, + + // === Widget === /// + Widget_Background = 0x00000040, + Widget_Foreground = 0x00000041, + Widget_Border = 0x00000042, + + // === View === /// + View_Background = 0x00000050, + View_Foreground = 0x00000051, + View_Border = 0x00000052, + + // === Selection === /// + Selection_Background = 0x00000060, + Selection_Foreground = 0x00000061, + + // === TitleBar === /// + TitleBar_Background = 0x00000070, + TitleBar_Foreground = 0x00000071, + TitleBar_Border = 0x00000072, + + /// --- 提供给KiranStyle直接使用的相关属性 --- /// + Frame_Background = 0x00001000, + Frame_Border , + + Button_Background , + Button_Border , + + SpecialButton_DefaultBackground , + SpecialButton_WarnningBackground , + + Edit_Background , + Edit_Border , + + Indicator_Arrow , + + Combo_Background , + Combo_Border , + + ItemView_Branch , + + MenuBar_ItemBackground , + MenuBar_EmptyAreaBackground , + + Progress_Groove , + Progress_Content , + + Scroll_Slider , + + Slider_Groove , + Slider_Content , + Slider_HandleBorder , + Slider_HandleBackground , + + SpinBox_Background , + SpinBox_Border , + SpinBox_SignColor , + + //TODO:定义kiranstyle私有的属性枚举 + }; + //clang-format on + Q_ENUM(SchemePropertyName) + +private: + enum SchemeValueType + { + SCHEME_VALUE_COLOR, + SCHEME_VALUE_INT, + SCHEME_VALUE_URL, + SCHEME_VALUE_REAL + }; + +public: + explicit SchemeLoader(QObject* parent = nullptr); + ~SchemeLoader() override; + + bool isValid(); + void dump(); + /// 将伪类转成字符串 + /// \param qseudoClass 伪类 + /// \return 字符串描述 + static QString pseudoClass2String(quint64 pseudoClass); + + bool load(const QString& schemeFile); + void polish(QPalette* palette); + + // color + QColor getColor(const QWidget* widget, + const QStyleOption* opt, + SchemePropertyName name, + quint64 specialPseudoClass = PseudoClass_Unspecified); + + QColor getColor(SchemePropertyName propertyName, + quint64 pseudoClass); + // url + QString getUrl(const QWidget* widget, + const QStyleOption* opt, + SchemePropertyName name, + quint64 specialPseudoClass = PseudoClass_Unspecified); + + QString getUrl(SchemePropertyName name, + quint64 pseudoClass); + +private: + QVariant fetchPropertyValue(const QWidget* widget, + const QStyleOption* opt, + SchemePropertyName name, + SchemeValueType valueType, + quint64 specialPseudlClasss = PseudoClass_Unspecified); + + QVariant fetchPropertyValue(SchemePropertyName propertyName, + quint64 pseudoClass, + SchemeValueType valueType); + + /// 转换QStyleOption成伪状态选择器 + /// \param widget 控件,特别控件需要添加伪状态 + /// \param styleOption 样式选项,判断控件伪状态 + /// \return 转换后的伪状态 + quint64 convertStyleOption2PseudoClass(const QWidget* widget, const QStyleOption* opt); + + /// 存入缓存 + /// \param selector 选择器 + /// \param pseudoStatus 伪状态 + /// \param var 属性值 + void insertCacheEntry(SchemePropertyName propertyName, + quint64 pseudoStatus, + QVariant var); + + QVariant searchCacheEntry(SchemePropertyName propertyName, + quint64 pseudoStatus); + +private: + bool m_isValid = false; + QCss::StyleSheet* m_styleScheme; + QReadWriteLock m_rwLock; + QMap m_styleSchemeCache; +}; +} // namespace Style +} // namespace Kiran +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_LIB_SRC_SCHEME_LOADER_H_ diff --git a/style/CMakeLists.txt b/style/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6cb6fa6643656041ba9495760e78bb64feafb297 --- /dev/null +++ b/style/CMakeLists.txt @@ -0,0 +1,30 @@ +set(TARGET_NAME "kiranstyle") + +find_package(Qt5 COMPONENTS Widgets Svg DBus) +file(GLOB_RECURSE SRC "src/*.cpp" "src/*.h" "src/*.ui") + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +add_library( ${TARGET_NAME} SHARED + ${SRC} ) + +#获取kiran-style-helper之中的私有的include,kiranstyle之中的绘制需要引用到相关私有头文件 +get_target_property(KIRAN_STYLE_HELPER_INCLUDE_DIRS kiran-style-helper INCLUDE_DIRECTORIES) + +target_include_directories(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${Qt5Svg_PRIVATE_INCLUDE_DIRS} + ${Qt5Widgets_PRIVATE_INCLUDE_DIRS} + ${KIRAN_STYLE_HELPER_INCLUDE_DIRS} + src/) + +target_link_libraries(${TARGET_NAME} + kiran-style-helper + Qt5::Widgets + Qt5::DBus + Qt5::Svg) + +#kiran style 插件 +install(TARGETS ${TARGET_NAME} DESTINATION ${QT5_STYLE_INSTALL_DIR}) \ No newline at end of file diff --git a/style/kiran-style-plugin.json b/style/kiran-style-plugin.json new file mode 100644 index 0000000000000000000000000000000000000000..819fc0e968eafb55f23763e47be7b502a3d2c33f --- /dev/null +++ b/style/kiran-style-plugin.json @@ -0,0 +1 @@ +{ "Keys": [ "Kiran" ] } diff --git a/style/src/define.h b/style/src/define.h new file mode 100644 index 0000000000000000000000000000000000000000..3d632137ffc6c134af6d02e3b77087c0726952ca --- /dev/null +++ b/style/src/define.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#ifndef __STYLE_DEFINE_H__ +#define __STYLE_DEFINE_H__ + +#include +#include + +namespace Kiran +{ +namespace Style +{ +Q_NAMESPACE +enum ArrowOrientation +{ + Arrow_Up, + Arrow_Down, + Arrow_Left, + Arrow_Right +}; +enum Corner +{ + CornerTopLeft = 0x1, + CornerTopRight = 0x2, + CornerBottomLeft = 0x4, + CornerBottomRight = 0x8, + CornersTop = CornerTopLeft|CornerTopRight, + CornersBottom = CornerBottomLeft|CornerBottomRight, + CornersLeft = CornerTopLeft|CornerBottomLeft, + CornersRight = CornerTopRight|CornerBottomRight, + AllCorners = CornerTopLeft|CornerTopRight|CornerBottomLeft|CornerBottomRight, +}; +Q_DECLARE_FLAGS( Corners, Corner ) +} +} + +#endif \ No newline at end of file diff --git a/style/src/draw-helper/draw-button-helper.cpp b/style/src/draw-helper/draw-button-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..502dd25e8c01cdb5f6b17f02f0e64d38a5f285dd --- /dev/null +++ b/style/src/draw-helper/draw-button-helper.cpp @@ -0,0 +1,578 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-button-helper.h" +#include "kiran-palette.h" +#include "define.h" +#include "draw-common-helper.h" +#include "metrics.h" +#include "render-helper.h" +#include "kiran-style-property.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Kiran::Style; + +//根据按钮内容计算按钮尺寸 +QSize Kiran::Style::pushButtonSizeFromContents(const QStyle* style, + const QStyleOption* option, + const QSize& contentSize, + const QWidget* widget) +{ + const auto buttonOption(qstyleoption_cast(option)); + if (!buttonOption) return contentSize; + + QSize size; + + const bool hasText(!buttonOption->text.isEmpty()); + const bool flat(buttonOption->features & QStyleOptionButton::Flat); + bool hasIcon(!buttonOption->icon.isNull()); + + if (!(hasText || hasIcon)) + { + //没有文本以及图标,采用自定义按钮作为内容大小的起点 + size = contentSize; + } + else + { + //不管Qt在内容大小如何计算,完全根据按钮选项重新计算按钮大小,保证渲染阶段的一致性 + hasIcon &= (RenderHelper::showIconsOnPushButtons() || flat || !hasText); + + //文本大小 + if (hasText) + size = buttonOption->fontMetrics.size(Qt::TextShowMnemonic, buttonOption->text); + + //图标大小 + if (hasIcon) + { + QSize iconSize = buttonOption->iconSize; + if (!iconSize.isValid()) iconSize = QSize(style->pixelMetric(QStyle::PM_SmallIconSize, option, widget), style->pixelMetric(QStyle::PM_SmallIconSize, option, widget)); + + size.setHeight(qMax(size.height(), iconSize.height())); + size.rwidth() += iconSize.width(); + + if (hasText) size.rwidth() += Metrics::Button_ItemSpacing; + } + } + + //菜单 + const bool hasMenu(buttonOption->features & QStyleOptionButton::HasMenu); + if (hasMenu) + { + size.rwidth() += Metrics::MenuButton_IndicatorWidth; + if (hasText || hasIcon) size.rwidth() += Metrics::Button_ItemSpacing; + } + + //扩展按钮内边距 + size = RenderHelper::expandSize(size, Metrics::Button_MarginWidth, Metrics::Button_MarginHeight); + //扩张按钮边框宽度 + size = RenderHelper::expandSize(size, Metrics::Frame_FrameWidth); + + //确保按钮有最小的宽度 + if (hasText) + { + size.setWidth(qMax(size.width(), int(Metrics::Button_MinWidth))); + } + + //确保按钮有最小高度 + size.setHeight(qMax(size.height(), int(Metrics::Button_MinHeight))); + + return size; +} + +//根据ToolButton内容大小获取尺寸 +QSize Kiran::Style::toolButtonSizeFromContents(const QStyle* style, + const QStyleOption* option, + const QSize& contentSize, + const QWidget* widget) +{ + const auto toolButtonOption = qstyleoption_cast(option); + if (!toolButtonOption) return contentSize; + + QSize size = contentSize; + + const QStyle::State& state(option->state); + + //Auto Raise标志: true表示自动突出,false表示与父窗口齐平 + const bool autoRaise(state & QStyle::State_AutoRaise); + const bool hasPopupMenu(toolButtonOption->features & QStyleOptionToolButton::MenuButtonPopup); + const bool hasInlineIndicator(toolButtonOption->features & QStyleOptionToolButton::HasMenu && toolButtonOption->features & QStyleOptionToolButton::PopupDelay && !hasPopupMenu); + + //若存在内部指示器,宽度加上一个内部指示器的宽度 + if (hasInlineIndicator) size.rwidth() += Metrics::ToolButton_InlineIndicatorWidth; + + //总边距宽度=按钮边距宽度+Frame宽度 + int marginWidth = Metrics::Button_MarginWidth + Metrics::Frame_FrameWidth; + //四周扩展边距宽度 + size = RenderHelper::expandSize(size, marginWidth); + + return size; +} + +bool Kiran::Style::toolButtonSubControlRect(const QStyle* style, + const QStyleOptionComplex* opt, + QStyle::SubControl sc, + const QWidget* widget, + QRect& controlRect) +{ + const auto toolButtonOption = qstyleoption_cast(opt); + if (!toolButtonOption) return false; + + bool hasPopupMenu(toolButtonOption->features & QStyleOptionToolButton::MenuButtonPopup); + const bool hasInlineIndicator(toolButtonOption->features & QStyleOptionToolButton::HasMenu && + toolButtonOption->features & QStyleOptionToolButton::PopupDelay && + !hasPopupMenu); + + const QRect& rect(opt->rect); + int menuButtonWidth(Metrics::MenuButton_IndicatorWidth); + + switch (sc) + { + case QStyle::SC_ToolButtonMenu: + { + if (!(hasPopupMenu || hasInlineIndicator)) + { + controlRect = QRect(); + break; + } + + QRect menuRect(rect); + //定位菜单按钮为ToolButton右侧 + menuRect.setLeft(rect.right() - menuButtonWidth + 1); + if (hasInlineIndicator) + { + menuRect.setTop(menuRect.bottom() - menuButtonWidth); + } + controlRect = QStyle::visualRect(opt->direction, opt->rect, menuRect); + break; + } + case QStyle::SC_ToolButton: + { + if (hasPopupMenu) + { + QRect contentsRect(rect); + contentsRect.setRight(rect.right() - menuButtonWidth); + controlRect = QStyle::visualRect(opt->direction, opt->rect, contentsRect); + } + else + { + controlRect = rect; + } + break; + } + default: + return false; + } + + return true; +} + +bool Kiran::Style::drawPEPanelButtonCommand(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + const auto buttonOption(qstyleoption_cast(option)); + if (!buttonOption) + return true; + + const QRect& rect(option->rect); + + const QStyle::State& state(option->state); + bool enabled(state & QStyle::State_Enabled); + bool windowActive(state & QStyle::State_Active); + bool mouseOver((state & QStyle::State_Active) && enabled && (state & QStyle::State_MouseOver)); + bool hasFocus((enabled && (state & QStyle::State_HasFocus)) && !(widget && widget->focusProxy())); + bool sunken(state & (QStyle::State_On | QStyle::State_Sunken)); + bool flat(buttonOption->features & QStyleOptionButton::Flat); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + QColor background,border; + if( qobject_cast(widget) && PropertyHelper::getButtonType(qobject_cast(widget))!=BUTTON_Normal ) + { + ButtonType type = PropertyHelper::getButtonType(qobject_cast(widget)); + SchemeLoader::SchemePropertyName backgroundPropertyName = SchemeLoader::SpecialButton_DefaultBackground; + if( type == BUTTON_Warning ) + backgroundPropertyName = SchemeLoader::SpecialButton_WarnningBackground; + + background = schemeLoader->getColor(widget,option,backgroundPropertyName); + } + else + { + background = schemeLoader->getColor(widget,option,SchemeLoader::Button_Background); + border = schemeLoader->getColor(widget,option,SchemeLoader::Button_Border); + } + + if (flat) + { + if (!sunken) + { + background = Qt::transparent; + } + } + + RenderHelper::renderFrame(painter, option->rect,1, 4, background, border); + return true; +} + +bool Kiran::Style::drawPEPanelButtonTool(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + const QPalette& palette(option->palette); + QRect rect(option->rect); + + const QStyle::State& state(option->state); + bool autoRaise(state & QStyle::State_AutoRaise); + bool enabled(state & QStyle::State_Enabled); + bool windowActive(state & QStyle::State_Active); + bool sunken(state & (QStyle::State_On | QStyle::State_Sunken)); + bool mouseOver((state & QStyle::State_Active) && enabled && (option->state & QStyle::State_MouseOver)); + bool hasFocus(enabled && (option->state & (QStyle::State_HasFocus | QStyle::State_Sunken))); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + if (!autoRaise || mouseOver || sunken) + { + // need to check widget for popup mode, because option is not set properly + const auto* toolButton(qobject_cast(widget)); + bool hasPopupMenu(toolButton && toolButton->popupMode() == QToolButton::MenuButtonPopup); + + // adjust frame in case of menu + if (hasPopupMenu) + { + painter->setClipRect(rect); + rect.adjust(0, 0, Metrics::Frame_FrameRadius + 2, 0); + rect = QStyle::visualRect(option->direction, option->rect, rect); + } + + auto background = schemeLoader->getColor(widget,option,SchemeLoader::Button_Background); + auto border = schemeLoader->getColor(widget,option,SchemeLoader::Button_Border); + RenderHelper::renderFrame(painter, rect,1, 4, background, border); + } + else + { + RenderHelper::renderFrame(painter, rect,1, 4, Qt::transparent); + } + + return true; +} + +bool Kiran::Style::drawControlToolButtonLabel(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + const auto toolButtonOption(qstyleoption_cast(option)); + if (!toolButtonOption) + return false; + + const QRect& rect = option->rect; + const QPalette& palette = option->palette; + + const QStyle::State& state(option->state); + bool enabled(state & QStyle::State_Enabled); + bool sunken(state & (QStyle::State_On | QStyle::State_Sunken)); + bool mouseOver((state & QStyle::State_Active) && enabled && (option->state & QStyle::State_MouseOver)); + bool flat(state & QStyle::State_AutoRaise); + + bool hasFocus(false); + if (flat) + hasFocus = enabled && !mouseOver && (option->state & QStyle::State_HasFocus); + else + hasFocus = enabled && !mouseOver && (option->state & (QStyle::State_HasFocus | QStyle::State_Sunken)); + + bool hasArrow = toolButtonOption->features & QStyleOptionToolButton::Arrow; + bool hasIcon(!(hasArrow || toolButtonOption->icon.isNull())); + bool hasText(!toolButtonOption->text.isEmpty()); + + QRect contentsRect(rect); + QSize iconSize(toolButtonOption->iconSize); + int textFlags(RenderHelper::mnemonicsTextFlags()); + QSize textSize(option->fontMetrics.size(textFlags, toolButtonOption->text)); + + QRect iconRect; + QRect textRect; + + if (hasText && (!(hasArrow || hasIcon) || toolButtonOption->toolButtonStyle == Qt::ToolButtonTextOnly)) //只显示文字 + { + textRect = contentsRect; + textFlags |= Qt::AlignCenter; + } + else if ((hasArrow || hasIcon) && (!hasText || toolButtonOption->toolButtonStyle == Qt::ToolButtonIconOnly)) //只显示图标 + { + iconRect = contentsRect; + } + else if (toolButtonOption->toolButtonStyle == Qt::ToolButtonTextUnderIcon) //文字位于图标之下 + { + int contentsHeight(iconSize.height() + textSize.height() + Metrics::ToolButton_ItemSpacing); + iconRect = QRect(QPoint(contentsRect.left() + (contentsRect.width() - iconSize.width()) / 2, contentsRect.top() + (contentsRect.height() - contentsHeight) / 2), iconSize); + textRect = QRect(QPoint(contentsRect.left() + (contentsRect.width() - textSize.width()) / 2, iconRect.bottom() + Metrics::ToolButton_ItemSpacing + 1), textSize); + textFlags |= Qt::AlignCenter; + } + else + { + // bool leftAlign(widget && widget->property(PropertyNames::toolButtonAlignment).toInt() == Qt::AlignLeft); + // if (leftAlign) + // iconRect = QRect(QPoint(contentsRect.left(), contentsRect.top() + (contentsRect.height() - iconSize.height()) / 2), iconSize); + // else + { + int contentsWidth(iconSize.width() + textSize.width() + Metrics::ToolButton_ItemSpacing); + iconRect = QRect(QPoint(contentsRect.left() + (contentsRect.width() - contentsWidth) / 2, contentsRect.top() + (contentsRect.height() - iconSize.height()) / 2), iconSize); + } + + textRect = QRect(QPoint(iconRect.right() + Metrics::ToolButton_ItemSpacing + 1, contentsRect.top() + (contentsRect.height() - textSize.height()) / 2), textSize); + + // handle right to left layouts + iconRect = QStyle::visualRect(option->direction, option->rect, iconRect); + textRect = QStyle::visualRect(option->direction, option->rect, textRect); + + textFlags |= Qt::AlignLeft | Qt::AlignVCenter; + } + + // make sure there is enough room for icon + if (iconRect.isValid()) iconRect = RenderHelper::centerRect(iconRect, iconSize); + + // render arrow or icon + if (hasArrow && iconRect.isValid()) + { + QStyleOptionToolButton copy(*toolButtonOption); + copy.rect = iconRect; + switch (toolButtonOption->arrowType) + { + case Qt::LeftArrow: + style->drawPrimitive(QStyle::PE_IndicatorArrowLeft, ©, painter, widget); + break; + case Qt::RightArrow: + style->drawPrimitive(QStyle::PE_IndicatorArrowRight, ©, painter, widget); + break; + case Qt::UpArrow: + style->drawPrimitive(QStyle::PE_IndicatorArrowUp, ©, painter, widget); + break; + case Qt::DownArrow: + style->drawPrimitive(QStyle::PE_IndicatorArrowDown, ©, painter, widget); + break; + default: + break; + } + } + else if (hasIcon && iconRect.isValid()) + { + // icon state and mode + const QIcon::State iconState(sunken ? QIcon::On : QIcon::Off); + QIcon::Mode iconMode; + if (!enabled) + iconMode = QIcon::Disabled; + else if (!flat && hasFocus) + iconMode = QIcon::Selected; + else if (mouseOver && flat) + iconMode = QIcon::Active; + else + iconMode = QIcon::Normal; + + QPixmap pixmap = toolButtonOption->icon.pixmap(iconSize, iconMode, iconState); + style->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); + } + + // render text + if (hasText && textRect.isValid()) + { + QPalette::ColorRole textRole(QPalette::ButtonText); + if (flat) + textRole = (hasFocus && sunken && !mouseOver) ? QPalette::HighlightedText : QPalette::WindowText; + else if (hasFocus && !mouseOver) + textRole = QPalette::HighlightedText; + + painter->setFont(toolButtonOption->font); + style->drawItemText(painter, textRect, textFlags, palette, enabled, toolButtonOption->text, textRole); + } + return true; +} + +bool Kiran::Style::drawCCToolButton(const QStyle* style, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) +{ + const auto* toolButtonOption(qstyleoption_cast(option)); + if (!toolButtonOption) return true; + + const QStyle::State& state(option->state); + bool enabled(state & QStyle::State_Enabled); + bool mouseOver((state & QStyle::State_Active) && enabled && (option->state & QStyle::State_MouseOver)); + bool hasFocus(enabled && (option->state & QStyle::State_HasFocus)); + bool sunken(state & (QStyle::State_On | QStyle::State_Sunken)); + bool flat(state & QStyle::State_AutoRaise); + + bool isDockWidgetTitleButton(widget && widget->inherits("QDockWidgetTitleButton")); + bool inTabBar(widget && qobject_cast(widget->parentWidget())); + bool isMenuTitle(RenderHelper::isMenuTitle(widget)); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + if (isMenuTitle) + { + QStyleOptionToolButton copy(*toolButtonOption); + copy.font.setBold(false); + copy.state = QStyle::State_Enabled; + + auto separatorColor = schemeLoader->getColor(widget,option,SchemeLoader::Widget_Border); + return true; + } + + // copy option and alter palette + QStyleOptionToolButton copy(*toolButtonOption); + + if (isDockWidgetTitleButton) + { + const QAbstractButton* button(qobject_cast(widget)); + if (button->isChecked() || button->isDown()) + { + copy.state |= QStyle::State_Enabled | QStyle::State_On | QStyle::State_Sunken; + } + if (button->underMouse()) + { + copy.state |= QStyle::State_Enabled | QStyle::State_MouseOver | QStyle::State_Active; + } + } + + bool hasPopupMenu(toolButtonOption->features & QStyleOptionToolButton::MenuButtonPopup); + const bool hasInlineIndicator( + toolButtonOption->features & QStyleOptionToolButton::HasMenu && toolButtonOption->features & QStyleOptionToolButton::PopupDelay && !hasPopupMenu); + + QRect buttonRect(style->subControlRect(QStyle::CC_ToolButton, option, QStyle::SC_ToolButton, widget)); + QRect menuRect(style->subControlRect(QStyle::CC_ToolButton, option, QStyle::SC_ToolButtonMenu, widget)); + + // frame + if (toolButtonOption->subControls & QStyle::SC_ToolButton || isDockWidgetTitleButton) + { + copy.rect = buttonRect; + if (inTabBar) + { + QRect rect(option->rect); + + auto background = schemeLoader->getColor(widget,option,SchemeLoader::Widget_Background); + + painter->setPen(background); + painter->setBrush(background); + + switch (toolButtonOption->arrowType) + { + case Qt::UpArrow: + painter->drawRect(rect.adjusted(1, 1, -2, -1)); + break; + case Qt::DownArrow: + painter->drawRect(rect.adjusted(1, 0, -2, -2)); + break; + case Qt::LeftArrow: + painter->drawRect(rect.adjusted(1, 1, -1, -2)); + break; + case Qt::RightArrow: + painter->drawRect(rect.adjusted(0, 1, -2, -2)); + break; + } + +#if 0 + //位于QTabBar之上的QToolButton不绘制边框 + painter->setPen(border); + switch (toolButtonOption->arrowType) + { + case Qt::DownArrow: + painter->drawLine(rect.bottomLeft(), rect.bottomRight()); + break; + case Qt::RightArrow: + painter->drawLine(rect.topRight(), rect.bottomRight()); + break; + } + switch (toolButtonOption->arrowType) + { + case Qt::UpArrow: + case Qt::DownArrow: + painter->drawLine(rect.topLeft(), rect.bottomLeft()); + painter->drawLine(rect.topLeft(), rect.bottomLeft()); + break; + case Qt::LeftArrow: + case Qt::RightArrow: + painter->drawLine(rect.topLeft(), rect.topRight()); + painter->drawLine(rect.bottomLeft(), rect.bottomRight()); + break; + } +#endif + } + else if (sunken && hasPopupMenu && !(toolButtonOption->activeSubControls & QStyle::SC_ToolButton)) + { + // Only menu button is active. so draw left hand side od button raised + QStyleOptionToolButton btn(copy); + btn.state |= QStyle::State_Raised; + btn.state &= ~QStyle::State_Sunken; + btn.state &= ~QStyle::State_AutoRaise; + style->drawPrimitive(QStyle::PE_PanelButtonTool, &btn, painter, widget); + } + else + { + style->drawPrimitive(QStyle::PE_PanelButtonTool, ©, painter, widget); + } + } + + // arrow + if (hasPopupMenu) + { + copy.rect = menuRect; + + if (!flat || mouseOver || sunken) + style->drawPrimitive(QStyle::PE_IndicatorButtonDropDown, ©, painter, widget); + + style->drawPrimitive(QStyle::PE_IndicatorArrowDown, ©, painter, widget); + } + + else if (hasInlineIndicator) + { + copy.rect = menuRect; + + style->drawPrimitive(QStyle::PE_IndicatorArrowDown, ©, painter, widget); + } + + // contents + { + // restore state + copy.state = state; + + // define contents rect + QRect contentsRect(buttonRect); + + // detect dock widget title button + // for dockwidget title buttons, do not take out margins, so that icon do not get scaled down + if (isDockWidgetTitleButton) + { + // cast to abstract button + // adjust state to have correct icon rendered + const QAbstractButton* button(qobject_cast(widget)); + if (button->isChecked() || button->isDown()) + { + copy.state |= QStyle::State_Enabled | QStyle::State_On | QStyle::State_Sunken; + } + if (button->underMouse()) + { + copy.state |= QStyle::State_Enabled | QStyle::State_MouseOver | QStyle::State_Active; + } + } + else if (!inTabBar && hasInlineIndicator) + { + int marginWidth(flat ? Metrics::ToolButton_MarginWidth : Metrics::Button_MarginWidth + Metrics::Frame_FrameWidth); + contentsRect = RenderHelper::insideMargin(contentsRect, marginWidth, 0); + contentsRect.setRight(contentsRect.right() - Metrics::ToolButton_InlineIndicatorWidth); + contentsRect = QStyle::visualRect(option->direction,option->rect, contentsRect); + } + + copy.rect = contentsRect; + + // render + style->drawControl(QStyle::CE_ToolButtonLabel, ©, painter, widget); + } + return true; +} \ No newline at end of file diff --git a/style/src/draw-helper/draw-button-helper.h b/style/src/draw-helper/draw-button-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..009db9d9d2e75385462516dda47b1abaf9da4c4c --- /dev/null +++ b/style/src/draw-helper/draw-button-helper.h @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_BUTTON_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_BUTTON_HELPER_H_ + +#include + +class QStyleOption; +class QPainter; +class QWidget; +class QStyleOptionComplex; +class QStyle; +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; + +//sizeFromContents +QSize pushButtonSizeFromContents(const QStyle* style,const QStyleOption* option,const QSize& contentSize,const QWidget* widget); +QSize toolButtonSizeFromContents(const QStyle* style,const QStyleOption* option,const QSize& contentSize,const QWidget* widget); + +//subControlRect +bool toolButtonSubControlRect(const QStyle* style, const QStyleOptionComplex* opt, QStyle::SubControl sc, const QWidget* widget, QRect& controlRect); + +//drawPrimitive +bool drawPEPanelButtonCommand(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); +bool drawPEPanelButtonTool(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); + +//drawControl +bool drawControlToolButtonLabel(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); + +//drawComplexControl +bool drawCCToolButton(const QStyle* style, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget); +} // namespace Style +} // namespace Kiran + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_BUTTON_HELPER_H_ diff --git a/style/src/draw-helper/draw-combo-box-helper.cpp b/style/src/draw-helper/draw-combo-box-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32c11573b02cc9ee5a3aa8b994d5f9d552bdd881 --- /dev/null +++ b/style/src/draw-helper/draw-combo-box-helper.cpp @@ -0,0 +1,289 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-combo-box-helper.h" +#include "draw-common-helper.h" +#include "metrics.h" +#include "render-helper.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include +#include + +using namespace Kiran::Style; + +QSize Kiran::Style::comboBoxSizeFromContents(const QStyle *style, const QStyleOption *option, const QSize &contentSize, const QWidget *widget) +{ + const auto comboBoxOption(qstyleoption_cast(option)); + if (!comboBoxOption) return contentSize; + + QSize size(contentSize); + + // 若存在Frame绘制,大小扩展开 FrameWidth + const bool flat(!comboBoxOption->frame); + const int frameWidth(style->pixelMetric(QStyle::PM_ComboBoxFrameWidth, option, widget)); + if (!flat) + { + size = RenderHelper::expandSize(size, frameWidth); + } + + size.rwidth() += Metrics::MenuButton_IndicatorWidth; + size.rwidth() += Metrics::Button_ItemSpacing; + + size.rwidth() += size.height(); + + // 确保有足够的高度绘制下拉按钮指示器 + size.setHeight(qMax(size.height(), int(Metrics::MenuButton_IndicatorWidth))); + +// size = RenderHelper::expandSize(size, Metrics::ComboBox_MarginWidth, Metrics::ComboBox_MarginHeight); + + //确保至少有最小大小 + size.setHeight(qMax(size.height(), int(Metrics::ComboBox_MinHeight))); + size.setWidth(qMax(size.width(), int(Metrics::ComboBox_MinWidth))); + + return size; +} + +bool Kiran::Style::comboBoxSubControlRect(const QStyle *style, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget, QRect &controlRect) +{ + const auto comboBoxOption(qstyleoption_cast(opt)); + if (!comboBoxOption) return false; + + const bool editable(comboBoxOption->editable); + const bool flat(editable && !comboBoxOption->frame); + + auto rect(opt->rect); + + const int frameWidth(style->pixelMetric(QStyle::PM_ComboBoxFrameWidth, opt, widget)); + + switch (sc) + { + case QStyle::SC_ComboBoxFrame: + controlRect = flat ? rect : QRect(); + break; + case QStyle::SC_ComboBoxListBoxPopup: + controlRect = rect; + break; + case QStyle::SC_ComboBoxArrow: + controlRect = QRect(rect.right()-rect.height()+1, + rect.top(), + rect.height(),rect.height()); + break; + case QStyle::SC_ComboBoxEditField: + { + controlRect = QRect(rect.left(), rect.top(),rect.width() - rect.height() - 4,rect.height()); + controlRect.adjust(frameWidth, frameWidth, 0, -frameWidth); + controlRect = QStyle::visualRect(opt->direction,opt->rect,controlRect); + break; + } + default: + return false; + } + return true; +} + +bool Kiran::Style::drawCCComboBox(const QStyle *style, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) +{ + const auto comboBoxOption(qstyleoption_cast(option)); + if (!comboBoxOption) + { + return true; + } + + const QStyle::State &state(option->state); + bool enabled(state & QStyle::State_Enabled); + bool windowActive(state & QStyle::State_Active); + bool editable(comboBoxOption->editable); + bool arrowActive(comboBoxOption->activeSubControls & QStyle::SC_ComboBoxArrow); + bool flat(!comboBoxOption->frame); + bool sunken; + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + + if (editable) + { + sunken = arrowActive && enabled && ( state & (QStyle::State_On|QStyle::State_Sunken) ); + } + else + { + sunken = enabled && ( state & (QStyle::State_On|QStyle::State_Sunken) ); + } + + const auto comboBox = qobject_cast(widget); + if( !comboBox ) + return true; + + const bool empty(comboBox && !comboBox->count()); + + //TODO: 当ComboBox为空时,应当特别绘制 + // frame + if (option->subControls & QStyle::SC_ComboBoxFrame) + { + if (editable) + { + //FIXME:下拉框编辑状态悬浮判断没有区域 + flat |= (option->rect.height() <= 2 * Metrics::Frame_FrameWidth + Metrics::MenuButton_IndicatorWidth); + if (flat) + { + const auto &background = option->palette.color(QPalette::Base); + painter->setBrush(background); + painter->setPen(Qt::NoPen); + painter->drawRect(option->rect); + } + else + { + QRectF arrowRect = style->subControlRect(QStyle::CC_ComboBox, comboBoxOption, QStyle::SC_ComboBoxArrow, widget); + + //ComboBox 箭头按钮 + arrowRect.adjust(1.5, 1.5, -1.5, -1.5); + QPainterPath indicatorPainterPath = RenderHelper::roundedPath(arrowRect, CornersRight, 4); + + auto background = schemeLoader->getColor(widget,option,SchemeLoader::Combo_Background); + auto border = schemeLoader->getColor(widget,option,SchemeLoader::Combo_Border); + + QPen pen = painter->pen(); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(border); + painter->setBrush(background); + painter->drawPath(indicatorPainterPath); + + //输入框 + QStyleOptionComplex tmpOpt(*option); + //让输入框长度加上一个宽度进行绘制用来去掉输入框的圆角 + tmpOpt.rect.setWidth(tmpOpt.rect.width() - arrowRect.width() + 3); + style->drawPrimitive(QStyle::PE_FrameLineEdit, &tmpOpt, painter, widget); + } + } + else + { + if(flat) + { + auto background = option->palette.color(QPalette::Base); + if(sunken) + { + background = schemeLoader->getColor(widget,option,SchemeLoader::Combo_Background); + } + RenderHelper::renderFrame(painter,option->rect,1,0,background); + } + else + { + QStyleOptionComboBox tempOption(*comboBoxOption); + auto backgroundColor = schemeLoader->getColor(widget,&tempOption,SchemeLoader::Combo_Background); + auto borderColor = schemeLoader->getColor(widget,&tempOption,SchemeLoader::Combo_Border); + RenderHelper::renderFrame(painter, option->rect, 1, 4, backgroundColor, borderColor); + } + } + } + + // arrow + if (option->subControls & QStyle::SC_ComboBoxArrow) + { + auto arrowRect(style->subControlRect(QStyle::CC_ComboBox, option, QStyle::SC_ComboBoxArrow, widget)); + auto arrowColor = schemeLoader->getColor(widget,option,SchemeLoader::Indicator_Arrow); + RenderHelper::renderArrow(painter, arrowRect, Arrow_Down, arrowColor); + } + + return true; +} + +bool Kiran::Style::drawControlComboBoxLabel(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto comboBoxOption(qstyleoption_cast(option)); + if (!comboBoxOption) return false; + if (comboBoxOption->editable) return false; + + const QStyle::State &state(option->state); + const bool enabled(state & QStyle::State_Enabled); + const bool sunken(state & (QStyle::State_On | QStyle::State_Sunken)); + const bool mouseOver(enabled && (option->state & QStyle::State_MouseOver)); + const bool hasFocus(enabled && !mouseOver && (option->state & QStyle::State_HasFocus)); + const bool flat(!comboBoxOption->frame); + + QPalette::ColorRole textRole; + if (flat) + { + textRole = QPalette::WindowText; + } + else if (hasFocus) + { + textRole = QPalette::ButtonText; + } + else + { + textRole = QPalette::ButtonText; + } + + painter->setPen(QPen(option->palette.color(textRole), 1)); + + //获取文本框位置 + auto editRect = style->subControlRect(QStyle::CC_ComboBox, comboBoxOption, QStyle::SC_ComboBoxEditField, widget); + + painter->save(); + painter->setClipRect(editRect); + //绘制图标 + if (!comboBoxOption->currentIcon.isNull() && qobject_cast(widget)) + { + QIcon::Mode mode; + + if ((comboBoxOption->state & QStyle::State_Selected) && (comboBoxOption->state & QStyle::State_Active)) + { + mode = QIcon::Selected; + } + else if (comboBoxOption->state & QStyle::State_Enabled) + { + mode = QIcon::Normal; + } + else + { + mode = QIcon::Disabled; + } + + QPixmap pixmap = comboBoxOption->currentIcon.pixmap(comboBoxOption->iconSize, mode); + QRect iconRect(editRect); + iconRect.setWidth(comboBoxOption->iconSize.width() + 4); + iconRect = QStyle::alignedRect(comboBoxOption->direction, + Qt::AlignLeft | Qt::AlignVCenter, + iconRect.size(), editRect); + + if (comboBoxOption->editable) + { + painter->fillRect(iconRect, option->palette.brush(QPalette::Base)); + } + + style->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); + + if (comboBoxOption->direction == Qt::RightToLeft) + { + editRect.translate(-4 - comboBoxOption->iconSize.width(), 0); + } + else + { + editRect.translate(comboBoxOption->iconSize.width() + 4, 0); + } + } + + //绘制文本 + if (!comboBoxOption->currentText.isEmpty() && !comboBoxOption->editable) + { + QRect itemTextRect = editRect.adjusted(Metrics::ComboBox_FrameWidth, 0, -1, 0); + int itemTextFlags = QStyle::visualAlignment(comboBoxOption->direction, Qt::AlignLeft | Qt::AlignVCenter); + bool itemTextEnable = comboBoxOption->state & QStyle::State_Enabled; + style->drawItemText(painter, itemTextRect, itemTextFlags, comboBoxOption->palette, itemTextEnable, comboBoxOption->currentText); + } + + painter->restore(); + + return true; +} diff --git a/style/src/draw-helper/draw-combo-box-helper.h b/style/src/draw-helper/draw-combo-box-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..a64eb70c9cc325df1428ddf22614cc2b301bbd44 --- /dev/null +++ b/style/src/draw-helper/draw-combo-box-helper.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_COMBO_BOX_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_COMBO_BOX_HELPER_H_ + +#include + +class QStyleOptionComplex; +class QStyleOption; +class QWidget; +class QPainter; + +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; + +QSize comboBoxSizeFromContents(const QStyle* style, const QStyleOption* option, const QSize& contentSize, const QWidget* widget); +bool comboBoxSubControlRect(const QStyle* style, const QStyleOptionComplex* opt, QStyle::SubControl sc, const QWidget* widget, QRect& controlRect); +bool drawCCComboBox(const QStyle* style, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget); +bool drawControlComboBoxLabel(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); +} +} + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_COMBO_BOX_HELPER_H_ diff --git a/style/src/draw-helper/draw-common-helper.cpp b/style/src/draw-helper/draw-common-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6d766ceaec36b7e0c6969f556100c937ac588e94 --- /dev/null +++ b/style/src/draw-helper/draw-common-helper.cpp @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-common-helper.h" +#include "render-helper.h" + +#include "kiran-palette.h" +#include "scheme-loader-fetcher.h" + +#define private public +#include +#undef private + +#include +#include +#include +#include + +using namespace Kiran::Style; + +bool Kiran::Style::drawPEFrame(const QStyle *style, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget) +{ + QColor border,background; + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + background = schemeLoader->getColor(widget,option,SchemeLoader::Frame_Background); + border = schemeLoader->getColor(widget,option,SchemeLoader::Frame_Border); + + RenderHelper::renderFrame(painter, option->rect, 1, 0, background,border ); + return true; +} + +bool Kiran::Style::drawPEFrameFocusRect(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + //TODO:聚焦虚线框 颜色? 增加配置文件聚焦虚线框可配置 + return true; +} + +bool Kiran::Style::drawPEFrameGroupBox(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto frameOption(qstyleoption_cast(option)); + if (!frameOption) return true; + + // no frame for flat groupboxes + if (frameOption->features & QStyleOptionFrame::Flat) return true; + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto background = schemeLoader->getColor(widget,option,SchemeLoader::Frame_Background); + auto border = schemeLoader->getColor(widget,option,SchemeLoader::Frame_Border); + + RenderHelper::renderFrame(painter, option->rect,1, 0, background, border); + return true; +} + +bool Kiran::Style::drawControlShapedFrame(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto *frameOpt = qstyleoption_cast(option); + if (!frameOpt) + { + return false; + } + + switch (frameOpt->frameShape) + { + case QFrame::Box: + { + if (option->state & QStyle::State_Sunken) + { + return true; + } + else + { + break; + } + + } + case QFrame::HLine: + case QFrame::VLine: + { + const QRect &rect(option->rect); + bool isVertical(frameOpt->frameShape == QFrame::VLine); + + QColor color = KiranPalette::instance()->color(widget,option,KiranPalette::Window,KiranPalette::Border); + RenderHelper::renderSeparator(painter, option->rect, isVertical, color); + return true; + } + case QFrame::StyledPanel: + { + if (RenderHelper::isQtQuickControl(option, widget)) + { + style->drawPrimitive(QStyle::PE_FrameMenu,option,painter,widget); + return true; + } + break; + } + default: + break; + } + + return false; +} + +QSize Kiran::Style::defaultSizeFromContents(const QStyleOption* option, const QSize &contentSize, const QWidget *widget) +{ + return contentSize; +} diff --git a/style/src/draw-helper/draw-common-helper.h b/style/src/draw-helper/draw-common-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..d52211e9d272f79de75a18d1f38102aa3f9ec020 --- /dev/null +++ b/style/src/draw-helper/draw-common-helper.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_COMMON_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_COMMON_HELPER_H_ + +#include "define.h" + +#include +#include +#include +#include + +class QStyle; +class QStyleOption; +class QStyleOptionToolButton; + +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; + +bool drawPEFrame(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); +bool drawPEFrameFocusRect(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); +bool drawPEFrameGroupBox(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); + +bool drawControlShapedFrame(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); + +QSize defaultSizeFromContents(const QStyleOption* option, const QSize& contentSize, const QWidget* widget); +} // namespace Style +} // namespace Kiran + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_COMMON_HELPER_H_ diff --git a/style/src/draw-helper/draw-edit-helper.cpp b/style/src/draw-helper/draw-edit-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bbc7c4efc440164b7fbb03cbeb22f5601e2cb135 --- /dev/null +++ b/style/src/draw-helper/draw-edit-helper.cpp @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-edit-helper.h" +#include "kiran-palette.h" +#include "draw-common-helper.h" +#include "render-helper.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include +#include +#include + +bool Kiran::Style::drawPEFrameLineEdit(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + const auto &rect = option->rect; + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto background = schemeLoader->getColor(widget,option,SchemeLoader::Edit_Background); + auto border = schemeLoader->getColor(widget,option,SchemeLoader::Edit_Border); + + //控件高度不足够绘制边框 + if (rect.height() < 2 + option->fontMetrics.height()) + { + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->setPen(Qt::NoPen); + painter->setBrush(background); + painter->drawRect(rect); + painter->restore(); + } + else + { + if( qobject_cast(widget) ) + RenderHelper::renderFlatFrame(painter, rect, 4, background, border); + else + RenderHelper::renderFrame(painter, rect, 1,4, background, border); + } + + return true; +} diff --git a/style/src/draw-helper/draw-edit-helper.h b/style/src/draw-helper/draw-edit-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..b5039da1d250a2c309862ed0e43450b6cc6a2ad8 --- /dev/null +++ b/style/src/draw-helper/draw-edit-helper.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_EDIT_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_EDIT_HELPER_H_ + +class QStyle; +class QStyleOption; +class QPainter; +class QWidget; +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; +bool drawPEFrameLineEdit(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); +} +} + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_EDIT_HELPER_H_ diff --git a/style/src/draw-helper/draw-group-box-helper.cpp b/style/src/draw-helper/draw-group-box-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fc7bea5da995e941402bcf69018a755e64df5c53 --- /dev/null +++ b/style/src/draw-helper/draw-group-box-helper.cpp @@ -0,0 +1,198 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-group-box-helper.h" +#include +#include +#include +#include + +//copy from QCommonStyle +QSize Kiran::Style::groupBoxSizeFromContents(const QStyle *style, const QStyleOption *option, const QSize &contentSize, const QWidget *widget) +{ + if (!widget || !widget->inherits("QGroupBox")) + { + return {}; + } + + auto groupBox = qobject_cast(widget); + QSize size = contentSize + QSize(groupBox->isFlat() ? 16 : 0, 0); + + return size; +} + +//copy from QCommonStyle +bool Kiran::Style::groupBoxSubControlRect(const QStyle *style, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget, QRect &controlRect) +{ + const auto groupBox = qstyleoption_cast(opt); + if(!groupBox) + return false; + + switch (sc) + { + case QStyle::SC_GroupBoxFrame: + case QStyle::SC_GroupBoxContents: + { + int topMargin = 0; + int topHeight = 0; + int verticalAlignment = style->styleHint(QStyle::SH_GroupBox_TextLabelVerticalAlignment, groupBox, widget); + if (groupBox->text.size() || (groupBox->subControls & QStyle::SC_GroupBoxCheckBox)) + { + topHeight = groupBox->fontMetrics.height(); + if (verticalAlignment & Qt::AlignVCenter) + topMargin = topHeight / 2; + else if (verticalAlignment & Qt::AlignTop) + topMargin = topHeight; + } + + QRect frameRect = groupBox->rect; + frameRect.setTop(topMargin); + + if (sc == QStyle::SC_GroupBoxFrame) + { + controlRect = frameRect; + break; + } + + int frameWidth = 0; + if ((groupBox->features & QStyleOptionFrame::Flat) == 0) + frameWidth = style->pixelMetric(QStyle::PM_DefaultFrameWidth, groupBox, widget); + controlRect = frameRect.adjusted(frameWidth, frameWidth + topHeight - topMargin, -frameWidth, -frameWidth); + break; + } + case QStyle::SC_GroupBoxCheckBox: + case QStyle::SC_GroupBoxLabel: + { + QFontMetrics fontMetrics = groupBox->fontMetrics; + int h = fontMetrics.height(); + int tw = fontMetrics.size(Qt::TextShowMnemonic, groupBox->text + QLatin1Char(' ')).width(); + int marg = (groupBox->features & QStyleOptionFrame::Flat) ? 0 : 8; + controlRect = groupBox->rect.adjusted(marg, 0, -marg, 0); + controlRect.setHeight(h); + + int indicatorWidth = style->pixelMetric(QStyle::PM_IndicatorWidth, opt, widget); + int indicatorSpace = style->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, opt, widget) - 1; + bool hasCheckBox = groupBox->subControls & QStyle::SC_GroupBoxCheckBox; + int checkBoxSize = hasCheckBox ? (indicatorWidth + indicatorSpace) : 0; + + // Adjusted rect for label + indicatorWidth + indicatorSpace + QRect totalRect = QStyle::alignedRect(groupBox->direction, groupBox->textAlignment, + QSize(tw + checkBoxSize, h), controlRect); + + // Adjust totalRect if checkbox is set + if (hasCheckBox) + { + bool ltr = groupBox->direction == Qt::LeftToRight; + int left = 0; + // Adjust for check box + if (sc == QStyle::SC_GroupBoxCheckBox) + { + int indicatorHeight = style->pixelMetric(QStyle::PM_IndicatorHeight, opt, widget); + left = ltr ? totalRect.left() : (totalRect.right() - indicatorWidth); + int top = totalRect.top() + qMax(0, fontMetrics.height() - indicatorHeight) / 2; + totalRect.setRect(left, top, indicatorWidth, indicatorHeight); + // Adjust for label + } + else + { + left = ltr ? (totalRect.left() + checkBoxSize - 2) : totalRect.left(); + totalRect.setRect(left, totalRect.top(), + totalRect.width() - checkBoxSize, totalRect.height()); + } + } + controlRect = totalRect; + break; + } + default: + return false; + } + + return true; +} + +//copy from QCommonStyle +bool Kiran::Style::drawCCGroupBox(const QStyle *style, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) +{ + const auto *groupBox = qstyleoption_cast(option); + if (!groupBox) + { + return false; + } + + // Draw frame + QRect textRect = style->subControlRect(QStyle::CC_GroupBox, option, QStyle::SC_GroupBoxLabel, widget); + QRect checkBoxRect = style->subControlRect(QStyle::CC_GroupBox, option, QStyle::SC_GroupBoxCheckBox, widget); + if (groupBox->subControls & QStyle::SC_GroupBoxFrame) + { + QStyleOptionFrame frame; + frame.QStyleOption::operator=(*groupBox); + frame.features = groupBox->features; + frame.lineWidth = groupBox->lineWidth; + frame.midLineWidth = groupBox->midLineWidth; + frame.rect = style->subControlRect(QStyle::CC_GroupBox, option, QStyle::SC_GroupBoxFrame, widget); + painter->save(); + QRegion region(groupBox->rect); + if (!groupBox->text.isEmpty()) + { + bool ltr = groupBox->direction == Qt::LeftToRight; + QRect finalRect; + if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) + { + finalRect = checkBoxRect.united(textRect); + finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0); + } + else + { + finalRect = textRect; + } + region -= finalRect; + } + painter->setClipRegion(region); + style->drawPrimitive(QStyle::PE_FrameGroupBox, &frame, painter, widget); + painter->restore(); + } + + // Draw title + if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) + { + QColor textColor = groupBox->textColor; + if (textColor.isValid()) + painter->setPen(textColor); + int alignment = int(groupBox->textAlignment); + if (!style->styleHint(QStyle::SH_UnderlineShortcut, option, widget)) + alignment |= Qt::TextHideMnemonic; + + style->drawItemText(painter, textRect, Qt::TextShowMnemonic | Qt::AlignHCenter | alignment, + groupBox->palette, groupBox->state & QStyle::State_Enabled, groupBox->text, + textColor.isValid() ? QPalette::NoRole : QPalette::WindowText); + + if (groupBox->state & QStyle::State_HasFocus) + { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*groupBox); + fropt.rect = textRect; + style->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, painter, widget); + } + } + + // Draw checkbox + if (groupBox->subControls & QStyle::SC_GroupBoxCheckBox) + { + QStyleOptionButton box; + box.QStyleOption::operator=(*groupBox); + box.rect = checkBoxRect; + style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &box, painter, widget); + } + + return true; +} diff --git a/style/src/draw-helper/draw-group-box-helper.h b/style/src/draw-helper/draw-group-box-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..aa7db641cb35c851841f1ad1bc702d50db6bc21d --- /dev/null +++ b/style/src/draw-helper/draw-group-box-helper.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_GROUP_BOX_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_GROUP_BOX_HELPER_H_ + +#include + +class QStyle; +class QWidget; +class QStyleOption; +class QStyleOptionComplex; + +///绘制QGroupBox + +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; +QSize groupBoxSizeFromContents(const QStyle* style,const QStyleOption* option,const QSize& contentSize,const QWidget* widget); +bool groupBoxSubControlRect(const QStyle* style, const QStyleOptionComplex* opt, QStyle::SubControl sc, const QWidget* widget, QRect& controlRect); +bool drawCCGroupBox(const QStyle* style, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget); +} +} + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_GROUP_BOX_HELPER_H_ diff --git a/style/src/draw-helper/draw-indicator-helper.cpp b/style/src/draw-helper/draw-indicator-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2931565b08a21e85d2d97c50b0d009102740f8e3 --- /dev/null +++ b/style/src/draw-helper/draw-indicator-helper.cpp @@ -0,0 +1,270 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-indicator-helper.h" +#include "draw-common-helper.h" +#include "metrics.h" +#include "render-helper.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include +#include +#include +#include + +using namespace Kiran::Style; + +bool Kiran::Style::drawPEIndicatorButtonDropDown(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto toolButtonOption( qstyleoption_cast( option ) ); + if( !toolButtonOption ) + return true; + + const QStyle::State& state( option->state ); + bool autoRaise( state & QStyle::State_AutoRaise ); + bool enabled( state & QStyle::State_Enabled ); + bool mouseOver( (state & QStyle::State_Active) && enabled && ( state & QStyle::State_MouseOver ) ); + bool sunken( enabled && ( state & QStyle::State_Sunken ) ); + + // do nothing for autoraise buttons + if( (autoRaise && !sunken && !mouseOver) || !(toolButtonOption->subControls & QStyle::SC_ToolButtonMenu) ) + return true; + + const QPalette& palette( option->palette ); + const QRect& rect( option->rect ); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto backgroundColor = schemeLoader->getColor(widget,option,SchemeLoader::Button_Background); + auto borderColor = schemeLoader->getColor(widget,option,SchemeLoader::Button_Border); + auto separatorColor = borderColor; + + QRect frameRect( rect ); + painter->setClipRect( rect ); + frameRect.adjust( -Metrics::Frame_FrameRadius - 1, 0, 0, 0 ); + frameRect = QStyle::visualRect( option->direction,option->rect, frameRect ); + + // render + RenderHelper::renderFrame(painter, frameRect, 1, 4, backgroundColor, borderColor); + + // also render separator + QRect separatorRect( rect.adjusted( 0, 2, -2, -2 ) ); + separatorRect.setWidth( 1 ); + separatorRect = QStyle::visualRect( option->direction,option->rect, separatorRect ); + RenderHelper::renderSeparator(painter, separatorRect, true, separatorColor); + + return true; +} + +bool drawPEIndicatorArrow(ArrowOrientation orientation, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto arrowColor = schemeLoader->getColor(widget,option,Kiran::Style::SchemeLoader::Indicator_Arrow); + + RenderHelper::renderArrow(painter,option->rect,orientation,arrowColor); + return true; +} + +bool Kiran::Style::drawPEIndicatorArrowUp(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + return drawPEIndicatorArrow(Arrow_Up, option, painter, widget); +} + +bool Kiran::Style::drawPEIndicatorArrowDown(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + return drawPEIndicatorArrow(Arrow_Down, option, painter, widget); +} + +bool Kiran::Style::drawPEIndicatorArrowLeft(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + return drawPEIndicatorArrow(Arrow_Left, option, painter, widget); +} + +bool Kiran::Style::drawPEIndicatorArrowRight(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + return drawPEIndicatorArrow(Arrow_Right, option, painter, widget); +} + +bool Kiran::Style::drawPEIndicatorRadioButton(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto &rect(option->rect); + + bool checked = option->state & QStyle::State_On; + QString state = "normal"; + if( !(option->state & QStyle::State_Enabled) ) + { + state = "disabled"; + } + else if( (option->state & QStyle::State_Sunken) ) + { + state = "active"; + } + else if( (option->state & QStyle::State_MouseOver) ) + { + state = "hover"; + } + QString iconUrl = QString(":/style-helper/images/radio-%1-%2.svg").arg(checked?"checked":"unchecked").arg(state); + + painter->save(); + painter->setRenderHints(QPainter::SmoothPixmapTransform|QPainter::HighQualityAntialiasing); + QSvgRenderer renderer(iconUrl); + renderer.render(painter, rect); + painter->restore(); + return true; +} + +bool Kiran::Style::drawPEIndicatorCheckBox(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto &rect(option->rect); + + bool checked = option->state & QStyle::State_On; + QString state = "normal"; + if( !(option->state & QStyle::State_Enabled) ) + { + state = "disabled"; + } + else if( (option->state & QStyle::State_Sunken) ) + { + state = "active"; + } + else if( (option->state & QStyle::State_MouseOver) ) + { + state = "hover"; + } + QString iconUrl = QString(":/style-helper/images/check-%1-%2.svg").arg(checked?"checked":"unchecked").arg(state); + + painter->save(); + painter->setRenderHints(QPainter::SmoothPixmapTransform|QPainter::HighQualityAntialiasing); + QSvgRenderer renderer(iconUrl); + renderer.render(painter, rect); + painter->restore(); + return true; +} + +bool Kiran::Style::drawPEIndicatorBranch(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const QRect &rect(option->rect); + const QPalette &palette(option->palette); + + const QStyle::State &state(option->state); + bool reverseLayout(option->direction == Qt::RightToLeft); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + + //绘制扩展的箭头 + int expanderAdjust = 0; + if (state & QStyle::State_Children) { + // state + bool expanderOpen(state & QStyle::State_Open); + bool enabled(state & QStyle::State_Enabled); + bool mouseOver((state & QStyle::State_Active) && enabled && (state & QStyle::State_MouseOver)); + + // expander rect + int expanderSize = qMin(rect.width(), rect.height()); + expanderSize = qMin(expanderSize, int(Metrics::ItemView_ArrowSize)); + expanderAdjust = expanderSize / 2 + 1; + QRect arrowRect = RenderHelper::centerRect(rect, expanderSize, expanderSize); + + // get orientation from option + ArrowOrientation orientation; + if (expanderOpen) { + orientation = Arrow_Down; + } else if (reverseLayout) { + orientation = Arrow_Left; + } else { + orientation = Arrow_Right; + } + auto arrowColor = schemeLoader->getColor(widget,option,SchemeLoader::Indicator_Arrow); + RenderHelper::renderArrow(painter,arrowRect,orientation,arrowColor,QSize(12,12)); + } + + if( !RenderHelper::drawTreeBranches() ) + { + return true; + } + + QPoint center(rect.center()); + auto lineColor = schemeLoader->getColor(widget,option,SchemeLoader::Frame_Border); + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, true); + painter->translate(0.5, 0.5); + painter->setPen(QPen(lineColor, 1)); + if (state & (QStyle::State_Item | QStyle::State_Children | QStyle::State_Sibling)) { + QLineF line(QPointF(center.x(), rect.top()), QPointF(center.x(), center.y() - expanderAdjust - 1)); + painter->drawLine(line); + } + + //如果存在Item则绘制左/右取决与方向的线 + if (state & QStyle::State_Item) { + const QLineF line = reverseLayout ? + QLineF(QPointF(rect.left(), center.y()), QPointF(center.x() - expanderAdjust, center.y())) : + QLineF(QPointF(center.x() + expanderAdjust, center.y()), QPointF(rect.right(), center.y())); + painter->drawLine(line); + } + + // 如果存在同级节点的话,则绘制上下的线 + if (state & QStyle::State_Sibling) { + QLineF line(QPointF(center.x(), center.y() + expanderAdjust), QPointF(center.x(), rect.bottom())); + painter->drawLine(line); + } + painter->restore(); + + return true; +} + +bool Kiran::Style::drawPEIndicatorToolBarSeparator(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const QStyle::State & state = option->state; + bool enable = (state & QStyle::State_Enabled); + bool separatorIsVertical( state & QStyle::State_Horizontal ); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto separatorColor = schemeLoader->getColor(widget,option,SchemeLoader::Widget_Border); + RenderHelper::renderSeparator(painter,option->rect,separatorIsVertical,separatorColor); + return true; +} + +bool Kiran::Style::drawPEIndicatorToolBarHandle(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const QStyle::State& state = option->state; + QRect rect( option->rect ); + bool separatorIsVertical( state & QStyle::State_Horizontal ); + bool enabled(state & QStyle::State_Enabled); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto separatorColor = schemeLoader->getColor(widget,option,SchemeLoader::Widget_Border); + if( separatorIsVertical ) + { + rect.setWidth( Metrics::ToolBar_HandleWidth ); + rect = RenderHelper::centerRect( option->rect, rect.size() ); + rect.setWidth( 3 ); + RenderHelper::renderSeparator( painter, rect, separatorIsVertical,separatorColor ); + + rect.translate( 2, 0 ); + RenderHelper::renderSeparator( painter, rect, separatorIsVertical,separatorColor ); + + } else { + + rect.setHeight( Metrics::ToolBar_HandleWidth ); + rect = RenderHelper::centerRect( option->rect, rect.size() ); + rect.setHeight( 3 ); + RenderHelper::renderSeparator( painter, rect, separatorIsVertical,separatorColor ); + + rect.translate( 0, 2 ); + RenderHelper::renderSeparator( painter, rect, separatorIsVertical,separatorColor ); + + } + + return true; +} diff --git a/style/src/draw-helper/draw-indicator-helper.h b/style/src/draw-helper/draw-indicator-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..fe0b0d3f184c0a21eec9eae389e04861db49c8f6 --- /dev/null +++ b/style/src/draw-helper/draw-indicator-helper.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_INDICATOR_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_INDICATOR_HELPER_H_ + +class QStyle; +class QStyleOption; +class QPainter; +class QWidget; + +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; + +bool drawPEIndicatorButtonDropDown(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); + +bool drawPEIndicatorArrowUp(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawPEIndicatorArrowDown(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawPEIndicatorArrowLeft(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawPEIndicatorArrowRight(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); + +bool drawPEIndicatorRadioButton(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawPEIndicatorCheckBox(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); + +bool drawPEIndicatorBranch(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); + +bool drawPEIndicatorToolBarSeparator(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawPEIndicatorToolBarHandle(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +} // namespace Style +} // namespace Kiran + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_INDICATOR_HELPER_H_ diff --git a/style/src/draw-helper/draw-item-view-helper.cpp b/style/src/draw-helper/draw-item-view-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8624c59e19698d812b98857e8bd6a79601560f36 --- /dev/null +++ b/style/src/draw-helper/draw-item-view-helper.cpp @@ -0,0 +1,171 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-item-view-helper.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include + +bool Kiran::Style::drawControlHeaderSection(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const QRect &rect(option->rect); + const QPalette &palette(option->palette); + const QStyle::State &state(option->state); + bool enabled(state & QStyle::State_Enabled); + bool mouseOver((state & QStyle::State_Active) && enabled && (state & QStyle::State_MouseOver)); + + const auto *headerOption(qstyleoption_cast(option)); + if (!headerOption) + { + return true; + } + + bool horizontal(headerOption->orientation == Qt::Horizontal); + bool isFirst(horizontal && (headerOption->position == QStyleOptionHeader::Beginning)); + bool isCorner(widget && widget->inherits("QTableCornerButton")); + bool reverseLayout(option->direction == Qt::RightToLeft); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + + // outline + painter->setBrush(Qt::NoBrush); + auto outlineColor = schemeLoader->getColor(widget, option, SchemeLoader::ItemView_Branch); + painter->setPen(outlineColor); + + if (isCorner) + { + if (reverseLayout) + { + painter->drawPoint(rect.bottomLeft()); + } + else + { + painter->drawPoint(rect.bottomRight()); + } + } + else if (horizontal) + { + painter->drawLine(rect.bottomLeft(), rect.bottomRight()); + } + else + { + if (reverseLayout) + { + painter->drawLine(rect.topLeft(), rect.bottomLeft()); + } + else + { + painter->drawLine(rect.topRight(), rect.bottomRight()); + } + } + + // separators + if (horizontal) + { + if (headerOption->section != 0 || isFirst) + { + if (reverseLayout) + { + painter->drawLine(rect.topLeft(), rect.bottomLeft() - QPoint(0, 1)); + } + else + { + painter->drawLine(rect.topRight(), rect.bottomRight() - QPoint(0, 1)); + } + } + } + else + { + if (reverseLayout) + { + painter->drawLine(rect.bottomLeft() + QPoint(1, 0), rect.bottomRight()); + } + else + { + painter->drawLine(rect.bottomLeft(), rect.bottomRight() - QPoint(1, 0)); + } + } + + return true; +} + +bool Kiran::Style::drawControlHeaderLabel(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + if (const QStyleOptionHeader *header = qstyleoption_cast(option)) + { + QRect rect = header->rect; + if (!header->icon.isNull()) + { + QPixmap pixmap = header->icon.pixmap(style->pixelMetric(QStyle::PM_SmallIconSize), (header->state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled); + int pixw = pixmap.width(); + + QRect aligned = QStyle::alignedRect(header->direction, QFlag(header->iconAlignment), pixmap.size(), rect); + QRect inter = aligned.intersected(rect); + painter->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), inter.width(), inter.height()); + + if (header->direction == Qt::LeftToRight) + rect.setLeft(rect.left() + pixw + 2); + else + rect.setRight(rect.right() - pixw - 2); + } + + QFont fnt = painter->font(); + painter->setFont(fnt); + QPalette palette(header->palette); + style->drawItemText(painter, rect, header->textAlignment, palette, (header->state & QStyle::State_Active), header->text, QPalette::Text); + } + return true; +} + +bool Kiran::Style::drawControlHeaderEmptyArea(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + // use the same background as in drawHeaderPrimitive + const QRect &rect(option->rect); + QPalette palette(option->palette); + + bool horizontal(option->state & QStyle::State_Horizontal); + bool reverseLayout(option->direction == Qt::RightToLeft); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + + // fill + painter->setRenderHint(QPainter::Antialiasing, false); + painter->setBrush(palette.color(QPalette::Base)); + painter->setPen(Qt::NoPen); + painter->drawRect(rect); + + // outline + auto outlineColor = schemeLoader->getColor(widget,option,SchemeLoader::ItemView_Branch); + painter->setBrush(Qt::NoBrush); + painter->setPen(outlineColor); + + if (horizontal) + { + painter->drawLine(rect.bottomLeft(), rect.bottomRight()); + } + else + { + if (reverseLayout) + { + painter->drawLine(rect.topLeft(), rect.bottomLeft()); + } + else + { + painter->drawLine(rect.topRight(), rect.bottomRight()); + } + } + + return true; +} diff --git a/style/src/draw-helper/draw-item-view-helper.h b/style/src/draw-helper/draw-item-view-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..609ee6df4fd6529abdffc53af7245ca38234f3bb --- /dev/null +++ b/style/src/draw-helper/draw-item-view-helper.h @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_ITEM_VIEW_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_ITEM_VIEW_HELPER_H_ + +class QStyle; +class QStyleOption; +class QPainter; +class QWidget; +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; +bool drawControlHeaderSection(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); +bool drawControlHeaderLabel(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); +bool drawControlHeaderEmptyArea(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); +} +} + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_ITEM_VIEW_HELPER_H_ diff --git a/style/src/draw-helper/draw-menu-helper.cpp b/style/src/draw-helper/draw-menu-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb48a87bac2849e7faafd54f7ab22d27ef05aa04 --- /dev/null +++ b/style/src/draw-helper/draw-menu-helper.cpp @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-menu-helper.h" +#include "render-helper.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include +#include + +bool Kiran::Style::drawControlMenuBarItem(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + auto menuItemOption = qstyleoption_cast(option); + const QStyle::State& state = option->state; + const bool enabled(option->state & QStyle::State_Enabled); + const bool mouseOver((option->state & QStyle::State_MouseOver) && enabled); + const bool sunken((option->state & QStyle::State_Sunken) && enabled); + + Kiran::Style::PainterSaver painterSaver(painter); + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + +#if 0 + if(mouseOver && sunken) + { + auto background = schemeLoader->getColor(widget,option,SchemeLoader::MenuBar_ItemBackground); + painter->setRenderHints(QPainter::Antialiasing); + painter->setPen(Qt::NoPen); + painter->setBrush(background); + painter->drawRoundedRect(option->rect,4,4); + } +#endif + //TODO: menubaritem 和 menubar背景存在差异 + if( menuItemOption ) + { + int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + + if( !style->styleHint(QStyle::SH_UnderlineShortcut,option,widget) ) + alignment |= Qt::TextHideMnemonic; + + int iconExtent = style->pixelMetric(QStyle::PM_SmallIconSize); + + QPixmap pix = menuItemOption->icon.pixmap(widget?widget->window()->windowHandle(): nullptr,QSize(iconExtent,iconExtent),enabled?(mouseOver?QIcon::Active:QIcon::Normal):QIcon::Disabled); + + if( !pix.isNull() ) + { + style->drawItemPixmap(painter,option->rect,alignment,pix); + } + else + { + QStyleOptionMenuItem itemOption = *menuItemOption; + auto background = schemeLoader->getColor(widget,option,SchemeLoader::MenuBar_ItemBackground); + painter->fillRect(option->rect,background); + style->drawItemText(painter,itemOption.rect,alignment,itemOption.palette,enabled,itemOption.text,QPalette::ButtonText); + } + } + return true; +} + +bool Kiran::Style::drawControlMenuBarEmptyArea(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + QColor background = schemeLoader->getColor(widget,option,SchemeLoader::MenuBar_EmptyAreaBackground); + painter->fillRect(option->rect,background); + return true; +} diff --git a/style/src/draw-helper/draw-menu-helper.h b/style/src/draw-helper/draw-menu-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..cd039ddb7a321f01a4e703ddcd7ca0c84116f3c0 --- /dev/null +++ b/style/src/draw-helper/draw-menu-helper.h @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_INTEGRATION_STYLE_SRC_DRAW_HELPER_DRAW_MENU_HELPER_H_ +#define KIRAN_QT5_INTEGRATION_STYLE_SRC_DRAW_HELPER_DRAW_MENU_HELPER_H_ + +class QStyle; +class QStyleOption; +class QPainter; +class QWidget; + +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; +bool drawControlMenuBarItem(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawControlMenuBarEmptyArea(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget); +} +} +#endif //KIRAN_QT5_INTEGRATION_STYLE_SRC_DRAW_HELPER_DRAW_MENU_HELPER_H_ diff --git a/style/src/draw-helper/draw-progress-bar-helper.cpp b/style/src/draw-helper/draw-progress-bar-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f4eefe11e0f5c0db371d5b14f670cfe1deae5c2a --- /dev/null +++ b/style/src/draw-helper/draw-progress-bar-helper.cpp @@ -0,0 +1,214 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#include "draw-progress-bar-helper.h" +#include "draw-common-helper.h" +#include "metrics.h" +#include "render-helper.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include +#include +#include +#include + +using namespace Kiran::Style; + +QSize Kiran::Style::progressBarSizeFromContents(const QStyle *style, + const QStyleOption *option, + const QSize &contentSize, + const QWidget *widget) +{ + const auto progressBarOption(qstyleoption_cast(option)); + if (!progressBarOption) return contentSize; + + bool horizontal(progressBarOption->orientation == Qt::Horizontal); + + QSize size(contentSize); + + if (horizontal) + { + bool textVisible(progressBarOption->textVisible); + + size.setWidth(qMax(size.width(), int(Metrics::ProgressBar_Thickness))); + size.setHeight(qMax(size.height(), int(Metrics::ProgressBar_Thickness))); + if (textVisible) size.setHeight(qMax(size.height(), option->fontMetrics.height())); + } + else + { + size.setHeight(qMax(size.height(), int(Metrics::ProgressBar_Thickness))); + size.setWidth(qMax(size.width(), int(Metrics::ProgressBar_Thickness))); + } + + return size; +} + +QRect Kiran::Style::progressBarElementRect(const QStyle *style, + QStyle::SubElement subElement, + const QStyleOption *option, + const QWidget *widget) +{ + const auto progressBarOption(qstyleoption_cast(option)); + if (!progressBarOption) return option->rect; + + bool textVisible(progressBarOption->textVisible); + bool horizontal(progressBarOption->orientation == Qt::Horizontal); + bool inverted(progressBarOption->invertedAppearance); + + QRect rect(option->rect); + int frameWidth(style->pixelMetric(QStyle::PM_DefaultFrameWidth, option, widget)); + + if (horizontal) + rect = RenderHelper::insideMargin(rect, frameWidth, 0); + else + rect = RenderHelper::insideMargin(rect, 0, frameWidth); + + switch (subElement) + { + case QStyle::SE_ProgressBarContents: + { + rect = style->subElementRect(QStyle::SE_ProgressBarGroove,option,widget); + + qreal progress(progressBarOption->progress - progressBarOption->minimum); + int steps(qMax(progressBarOption->maximum - progressBarOption->minimum, 1)); + qreal widthFrac = qMin(qreal(1), progress / steps); + int indicatorSize(widthFrac * (horizontal ? rect.width() : rect.height())); + + if (horizontal) + { + rect = QRect(inverted ? (rect.right() - indicatorSize + 1) : rect.left(), rect.y(), indicatorSize, rect.height()); + rect = QStyle::visualRect(option->direction, rect, rect); + } + else + { + rect = QRect(rect.x(), inverted ? rect.top() : (rect.bottom() - indicatorSize + 1), rect.width(), indicatorSize); + } + break; + } + case QStyle::SE_ProgressBarGroove: + { + if (textVisible && horizontal) + { + QRect textRect(style->subElementRect(QStyle::SE_ProgressBarLabel, option, widget)); + textRect = QStyle::visualRect(option->direction, option->rect, textRect); + rect.setRight(textRect.left() - Metrics::ProgressBar_ItemSpacing - 1); + rect = QStyle::visualRect(option->direction, option->rect, rect); + rect = RenderHelper::centerRect(rect, rect.width(), Metrics::ProgressBar_Thickness); + } + else if (horizontal) + { + rect = RenderHelper::centerRect(rect, rect.width(), Metrics::ProgressBar_Thickness); + } + else + { + rect = RenderHelper::centerRect(rect, Metrics::ProgressBar_Thickness, rect.height()); + } + break; + } + case QStyle::SE_ProgressBarLabel: + { + if (!textVisible) + return QRect(); + + int textWidth = qMax( + option->fontMetrics.size(RenderHelper::mnemonicsTextFlags(), progressBarOption->text).width(), + option->fontMetrics.size(RenderHelper::mnemonicsTextFlags(), QStringLiteral("100%")).width()); + + rect = RenderHelper::insideMargin(option->rect, Metrics::Frame_FrameWidth, 0); + rect.setLeft(rect.right() - textWidth + 1); + rect = QStyle::visualRect(option->direction, option->rect, rect); + break; + } + default: + break; + } + + return rect; +} + +bool Kiran::Style::drawControlProgressBar(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto progressBarOption(qstyleoption_cast(option)); + if (!progressBarOption) return true; + + QStyleOptionProgressBar tempOption(*progressBarOption); + + QRect grooveRect = style->subElementRect(QStyle::SE_ProgressBarGroove,option,widget); + QRect contentsRect = style->subElementRect(QStyle::SE_ProgressBarContents,option,widget); + QRect labelRect = style->subElementRect(QStyle::SE_ProgressBarLabel,option,widget); + + tempOption.rect = grooveRect; + style->drawControl(QStyle::CE_ProgressBarGroove,&tempOption,painter,widget); + + //TODO: BUSY动画 + tempOption.rect = contentsRect; + style->drawControl(QStyle::CE_ProgressBarContents,&tempOption,painter,widget); + + bool textVisible( progressBarOption->textVisible ); + bool busy( progressBarOption->minimum == 0 && progressBarOption->maximum == 0 ); + if( textVisible && !busy ) + { + tempOption.rect = labelRect; + style->drawControl(QStyle::CE_ProgressBarLabel,&tempOption,painter,widget); + } + + return true; +} + +bool Kiran::Style::drawControlProgressBarGroove(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto progressBarOption(qstyleoption_cast(option)); + if (!progressBarOption) return true; + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto grooveColor = schemeLoader->getColor(widget,option,SchemeLoader::Progress_Groove); + + RenderHelper::renderFrame(painter,option->rect,1,4,grooveColor); + return true; +} + +bool Kiran::Style::drawControlProgressBarContents(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto progressBarOption(qstyleoption_cast(option)); + if (!progressBarOption) return true; + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto contentsColor = schemeLoader->getColor(widget,option,SchemeLoader::Progress_Content); + + RenderHelper::renderFrame(painter,option->rect,1,4,contentsColor); + return true; +} + +bool Kiran::Style::drawControlProgressBarLabel(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const auto progressBarOption( qstyleoption_cast( option ) ); + if( !progressBarOption ) return true; + + bool horizontal = progressBarOption->orientation == Qt::Horizontal; + if( !horizontal ) return true; + + const QRect& rect( option->rect ); + const QPalette& palette( option->palette ); + + // store state and direction + const QStyle::State& state( option->state ); + bool enabled( state & QStyle::State_Enabled ); + + // define text rect + Qt::Alignment hAlign( ( progressBarOption->textAlignment == Qt::AlignLeft ) ? Qt::AlignHCenter : progressBarOption->textAlignment ); + style->drawItemText( painter, rect, Qt::AlignVCenter | hAlign, palette, enabled, progressBarOption->text, QPalette::WindowText ); + return true; +} \ No newline at end of file diff --git a/style/src/draw-helper/draw-progress-bar-helper.h b/style/src/draw-helper/draw-progress-bar-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..72c37a087046ef17be0ea3544be6bb502ec49287 --- /dev/null +++ b/style/src/draw-helper/draw-progress-bar-helper.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_PROGRESS_BAR_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_PROGRESS_BAR_HELPER_H_ + +#include +#include + +class QStyleOption; +class QPainter; +class QWidget; + +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; +QSize progressBarSizeFromContents(const QStyle *style, const QStyleOption *option, const QSize &contentSize, const QWidget *widget); +QRect progressBarElementRect(const QStyle* style, QStyle::SubElement subElement, const QStyleOption* option, const QWidget* widget); + +bool drawControlProgressBar(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawControlProgressBarGroove(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawControlProgressBarContents(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawControlProgressBarLabel(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +} +} + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_PROGRESS_BAR_HELPER_H_ diff --git a/style/src/draw-helper/draw-scroll-bar-helper.cpp b/style/src/draw-helper/draw-scroll-bar-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e941b7fb6f8f59faa5fb9a28cb9d1f51ff005b56 --- /dev/null +++ b/style/src/draw-helper/draw-scroll-bar-helper.cpp @@ -0,0 +1,295 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#include "draw-scroll-bar-helper.h" +#include "draw-common-helper.h" +#include "metrics.h" +#include "render-helper.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace Kiran::Style; + +enum ScrollBarButtonType +{ + NoButton, + SingleButton, + DoubleButton +} _subLineButtons = NoButton, + _addLineButtons = NoButton; + +//* returns height for scrollbar buttons depending of button types +int scrollBarButtonHeight(ScrollBarButtonType type) +{ + switch (type) + { + case NoButton: + return Metrics::ScrollBar_NoButtonHeight; + case SingleButton: + return Metrics::ScrollBar_SingleButtonHeight; + case DoubleButton: + return Metrics::ScrollBar_DoubleButtonHeight; + default: + return 0; + } +} + +QRect scrollBarInternalSubControlRect(const QStyleOptionComplex *option, + QStyle::SubControl subControl) +{ + const QRect &rect = option->rect; + const QStyle::State &state(option->state); + bool horizontal(state & QStyle::State_Horizontal); + + switch (subControl) + { + case QStyle::SC_ScrollBarSubLine: + { + int majorSize(scrollBarButtonHeight(_subLineButtons)); + if (horizontal) + { + return QStyle::visualRect(option->direction, option->rect, QRect(rect.left(), rect.top(), majorSize, rect.height())); + } + else + { + return QStyle::visualRect(option->direction, option->rect, QRect(rect.left(), rect.top(), rect.width(), majorSize)); + } + } + case QStyle::SC_ScrollBarAddLine: + { + int majorSize(scrollBarButtonHeight(_addLineButtons)); + if (horizontal) + { + return QStyle::visualRect(option->direction, option->rect, QRect(rect.right() - majorSize + 1, rect.top(), majorSize, rect.height())); + } + else + { + return QStyle::visualRect(option->direction, option->rect, QRect(rect.left(), rect.bottom() - majorSize + 1, rect.width(), majorSize)); + } + } + + default: + return QRect(); + } +} + +bool Kiran::Style::scrollBarSubControlRect(const QStyle *style, + const QStyleOptionComplex *option, + QStyle::SubControl subControl, + const QWidget *widget, + QRect &controlRect) +{ + const auto sliderOption(qstyleoption_cast(option)); + if (!sliderOption) + return false; + + const QStyle::State &state(option->state); + bool horizontal(state & QStyle::State_Horizontal); + + switch (subControl) + { + case QStyle::SC_ScrollBarAddLine: + case QStyle::SC_ScrollBarSubLine: + case QStyle::SC_ScrollBarAddPage: + case QStyle::SC_ScrollBarSubPage: + controlRect = QRect(); + break; + case QStyle::SC_ScrollBarSlider: + { + // handle RTL here to unreflect things if need be + QRect groove = QStyle::visualRect(option->direction, option->rect, style->subControlRect(QStyle::CC_ScrollBar, option, QStyle::SC_ScrollBarGroove, widget)); + groove.adjust(0, 0, 1, 1); + + if (sliderOption->minimum == sliderOption->maximum) + { + controlRect = groove; + break; + } + + // Figure out how much room there is + int space(horizontal ? groove.width() : groove.height()); + + // Calculate the portion of this space that the slider should occupy + int sliderSize = space * qreal(sliderOption->pageStep) / (sliderOption->maximum - sliderOption->minimum + sliderOption->pageStep); + sliderSize = qMax(sliderSize, static_cast(Metrics::ScrollBar_MinSliderHeight)); + sliderSize = qMin(sliderSize, space); + + space -= sliderSize; + if (space <= 0) + { + controlRect = groove; + break; + } + + int pos = qRound(qreal(sliderOption->sliderPosition - sliderOption->minimum) / (sliderOption->maximum - sliderOption->minimum) * space); + if (sliderOption->upsideDown) + { + pos = space - pos; + } + + if (horizontal) + { + controlRect = QStyle::visualRect(option->direction,option->rect,QRect(groove.left() + pos, groove.top(), sliderSize, groove.height())); + } + else + { + controlRect = QStyle::visualRect(option->direction,option->rect,QRect(groove.left(), groove.top() + pos, groove.width(), sliderSize)); + } + break; + } + case QStyle::SC_ScrollBarGroove: + { + QRect topRect = QStyle::visualRect(option->direction, option->rect, scrollBarInternalSubControlRect(option, QStyle::SC_ScrollBarSubLine)); + QRect bottomRect = QStyle::visualRect(option->direction, option->rect, scrollBarInternalSubControlRect(option, QStyle::SC_ScrollBarAddLine)); + + QPoint topLeftCorner; + QPoint botRightCorner; + + if (horizontal) + { + topLeftCorner = QPoint(topRect.right() + 1, topRect.top()); + botRightCorner = QPoint(bottomRect.left() - 1, topRect.bottom()); + } + else + { + topLeftCorner = QPoint(topRect.left(), topRect.bottom() + 1); + botRightCorner = QPoint(topRect.right(), bottomRect.top() - 1); + } + + // define rect + controlRect = QStyle::visualRect(option->direction,option->rect,QRect(topLeftCorner, botRightCorner)); + break; + } + default: + return true; + } + + return true; +} + +bool Kiran::Style::drawCCScrollBar(const QStyle *style, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) +{ + const auto scrollbarOption = qstyleoption_cast(option); + if (!scrollbarOption) + { + return true; + } + + bool enabled( option->state & QStyle::State_Enabled ); + bool mouseOver( (option->state & QStyle::State_Active) && option->state & QStyle::State_MouseOver ); + + if( option->subControls & QStyle::SC_ScrollBarGroove ) + { + auto grooveRect = style->subControlRect(QStyle::CC_ScrollBar,option,QStyle::SC_ScrollBarGroove,widget); +//不绘制滑动槽 +#if 0 + if( mouseOver ) + { + QColor grooveColor = KiranPalette::instance()->color(widget,option,KiranPalette::Window,KiranPalette::Background); + painter->save(); + painter->setPen(Qt::NoPen); + painter->setBrush(grooveColor); + painter->drawRect(grooveRect); + painter->restore(); + } +#endif + } + + if (scrollbarOption->subControls & QStyle::SC_ScrollBarSlider) + { + QStyleOptionSlider sliderOption = *scrollbarOption; + sliderOption.state = scrollbarOption->state; + sliderOption.rect = style->subControlRect(QStyle::CC_ScrollBar, &sliderOption, QStyle::SC_ScrollBarSlider, widget); + if (sliderOption.rect.isValid()) + { + style->drawControl(QStyle::CE_ScrollBarSlider, &sliderOption, painter, widget); + } + } + + return true; +} + +bool Kiran::Style::drawControlScrollBarGroove(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget, Kiran::Style::SchemeLoader *scheme) +{ + return false; +} + +bool Kiran::Style::drawControlScrollBarSlider(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget) +{ + const QStyleOptionSlider *sliderOption(qstyleoption_cast(option)); + if (!sliderOption) + { + return true; + } + + const QStyle::State &state(option->state); + bool horizontal(state & QStyle::State_Horizontal); + //const QRect &rect(horizontal ? option->rect.adjusted(-1, 4, 0, -4) : option->rect.adjusted(4, -1, -4, 0)); + const QRect &rect(option->rect); + QRect handleRect ; + + bool enabled(state & QStyle::State_Enabled); + bool mouseOver((state & QStyle::State_Active) && enabled && (state & QStyle::State_MouseOver)); + bool sunken(enabled && (state & (QStyle::State_On | QStyle::State_Sunken))); + + const QWidget *parent = nullptr; + if ((widget && widget->parentWidget())) + { + auto scrollBar = qobject_cast(widget); + + QAbstractScrollArea *scrollArea = nullptr; + if (!(scrollArea = qobject_cast(widget->parentWidget()))) + { + scrollArea = qobject_cast(widget->parentWidget()->parentWidget()); + } + + if (scrollArea && (scrollBar == scrollArea->verticalScrollBar() || scrollBar == scrollArea->horizontalScrollBar())) + { + parent = scrollArea; + } + } + + bool hasFocus(enabled && parent && parent->hasFocus()); + + handleRect = rect; + if( !mouseOver && horizontal ) + { + handleRect = rect.adjusted(0,1,0,-1); + } + else if( !mouseOver && !horizontal) + { + handleRect = rect.adjusted(1,0,-1,0); + } + + QStyleOption tempOption(*option); + if(sunken) + { + tempOption.state |= QStyle::State_Sunken; + } + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto sliderColor = schemeLoader->getColor(widget,option,SchemeLoader::Scroll_Slider); + + painter->setRenderHint(QPainter::Antialiasing,true); + QPainterPath painterPath = RenderHelper::roundedPath(handleRect,AllCorners,2); + painter->fillPath(painterPath,sliderColor); + return true; +} diff --git a/style/src/draw-helper/draw-scroll-bar-helper.h b/style/src/draw-helper/draw-scroll-bar-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..feccfd137823871dc766a9a2bd01e6810be44750 --- /dev/null +++ b/style/src/draw-helper/draw-scroll-bar-helper.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_SCROLL_BAR_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_SCROLL_BAR_HELPER_H_ + +#include +#include + +class QStyle; +class QStyleOptionComplex; +class QPainter; +class QWidget; +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; + +bool scrollBarSubControlRect(const QStyle *style, const QStyleOptionComplex *option, QStyle::SubControl subControl, const QWidget *widget, QRect &controlRect); + +bool drawCCScrollBar(const QStyle *style, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget); + +bool drawControlScrollBarGroove(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget, Kiran::Style::SchemeLoader *scheme); +bool drawControlScrollBarSlider(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +} +} + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_SCROLL_BAR_HELPER_H_ diff --git a/style/src/draw-helper/draw-slider-helper.cpp b/style/src/draw-helper/draw-slider-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d323b48d685151b701634f2ea896eb381f47a80c --- /dev/null +++ b/style/src/draw-helper/draw-slider-helper.cpp @@ -0,0 +1,316 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-slider-helper.h" +#include "draw-common-helper.h" +#include "metrics.h" +#include "render-helper.h" +#include "style.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include + +//TODO: 之后整合所有的绘制过程中的是否启用的开关 +static const bool sliderDrawTickMarks = true; +enum Side +{ + SideNone = 0x0, + SideLeft = 0x1, + SideTop = 0x2, + SideRight = 0x4, + SideBottom = 0x8, + AllSides = SideLeft | SideTop | SideRight | SideBottom +}; + +QSize Kiran::Style::sliderSizeFromContents(const QStyle *style, + const QStyleOption *option, + const QSize &contentSize, + const QWidget *widget) +{ + // cast option and check + const auto sliderOption(qstyleoption_cast(option)); + if (!sliderOption) return contentSize; + + // store tick position and orientation + const QSlider::TickPosition &tickPosition(sliderOption->tickPosition); + bool horizontal(sliderOption->orientation == Qt::Horizontal); + bool disableTicks = !sliderDrawTickMarks; + + // do nothing if no ticks are requested + if (tickPosition == QSlider::NoTicks) return contentSize; + + /* + * Qt adds its own tick length directly inside QSlider. + * Take it out and replace by ours, if needed + */ + const int tickLength(disableTicks ? 0 : (Metrics::Slider_TickLength + Metrics::Slider_TickMarginWidth + (Metrics::Slider_GrooveThickness - Metrics::Slider_ControlThickness) / 2)); + + int builtInTickLength(2); + + QSize size(contentSize); + if (horizontal) + { + if (tickPosition & QSlider::TicksAbove) size.rheight() += tickLength - builtInTickLength; + if (tickPosition & QSlider::TicksBelow) size.rheight() += tickLength - builtInTickLength; + } + else + { + if (tickPosition & QSlider::TicksAbove) size.rwidth() += tickLength - builtInTickLength; + if (tickPosition & QSlider::TicksBelow) size.rwidth() += tickLength - builtInTickLength; + } + + return size; +} + +bool Kiran::Style::drawCCSlider(const QStyle *style, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) +{ + // cast option and check + const auto sliderOption(qstyleoption_cast(option)); + if (!sliderOption) return true; + + // copy rect and palette + const QRect &rect(option->rect); + const QPalette &palette(option->palette); + + // copy state + const QStyle::State &state(option->state); + bool enabled(state & QStyle::State_Enabled); + bool windowActive(state & QStyle::State_Active); + bool mouseOver((state & QStyle::State_Active) && enabled && (state & QStyle::State_MouseOver)); + bool hasFocus(enabled && (state & QStyle::State_HasFocus)); + bool horizontal(sliderOption->orientation == Qt::Horizontal); + Side tickSide{SideNone}; + if (horizontal && sliderOption->tickPosition == QSlider::TicksAbove) tickSide = (Side)((int)tickSide | (int)SideTop); + if (horizontal && sliderOption->tickPosition == QSlider::TicksBelow) tickSide = (Side)((int)tickSide | (int)SideBottom); + if (!horizontal && sliderOption->tickPosition == QSlider::TicksLeft) tickSide = (Side)((int)tickSide | (int)SideLeft); + if (!horizontal && sliderOption->tickPosition == QSlider::TicksRight) tickSide = (Side)((int)tickSide | (int)SideRight); + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto grooveColor = schemeLoader->getColor(widget,option,SchemeLoader::Slider_Groove); + auto contentColor = schemeLoader->getColor(widget,option,SchemeLoader::Slider_Content); + // tickmarks + if ( sliderDrawTickMarks && (sliderOption->subControls & QStyle::SC_SliderTickmarks) ) + { + bool upsideDown(sliderOption->upsideDown); + int tickPosition(sliderOption->tickPosition); + int available(style->pixelMetric(QStyle::PM_SliderSpaceAvailable, option, widget)); + int interval = sliderOption->tickInterval; + if (interval < 1) interval = sliderOption->pageStep; + if (interval >= 1) + { + int fudge(style->pixelMetric(QStyle::PM_SliderLength, option, widget) / 2); + int current(sliderOption->minimum); + + // store tick lines + QRect grooveRect(style->subControlRect(QStyle::CC_Slider, sliderOption, QStyle::SC_SliderGroove, widget)); + QList tickLines; + if (horizontal) + { + if (tickPosition & QSlider::TicksAbove) tickLines.append(QLine(rect.left(), grooveRect.top() - Metrics::Slider_TickMarginWidth, rect.left(), grooveRect.top() - Metrics::Slider_TickMarginWidth - Metrics::Slider_TickLength)); + if (tickPosition & QSlider::TicksBelow) tickLines.append(QLine(rect.left(), grooveRect.bottom() + Metrics::Slider_TickMarginWidth, rect.left(), grooveRect.bottom() + Metrics::Slider_TickMarginWidth + Metrics::Slider_TickLength)); + } + else + { + if (tickPosition & QSlider::TicksAbove) tickLines.append(QLine(grooveRect.left() - Metrics::Slider_TickMarginWidth, rect.top(), grooveRect.left() - Metrics::Slider_TickMarginWidth - Metrics::Slider_TickLength, rect.top())); + if (tickPosition & QSlider::TicksBelow) tickLines.append(QLine(grooveRect.right() + Metrics::Slider_TickMarginWidth, rect.top(), grooveRect.right() + Metrics::Slider_TickMarginWidth + Metrics::Slider_TickLength, rect.top())); + } + + while (current <= sliderOption->maximum) + { + // adjust color + QColor color((enabled && current <= sliderOption->sliderPosition) ? contentColor : grooveColor); + painter->setPen(color); + + // calculate positions and draw lines + int position(QStyle::sliderPositionFromValue(sliderOption->minimum, sliderOption->maximum, current, available) + fudge); + foreach (const QLine &tickLine, tickLines) + { + if (horizontal) + painter->drawLine(tickLine.translated(upsideDown ? (rect.width() - position) : position, 0)); + else + painter->drawLine(tickLine.translated(0, upsideDown ? (rect.height() - position) : position)); + } + + // go to next position + current += interval; + } + } + } + + // groove + if (sliderOption->subControls & QStyle::SC_SliderGroove) + { + if (hasFocus) + { + QRect focusRect = style->subElementRect(QStyle::SE_SliderFocusRect, option, widget); + + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*option); + fropt.rect = focusRect; + style->drawPrimitive(QStyle::PE_FrameFocusRect, &fropt, painter, widget); + } + + // retrieve groove rect + QRect grooveRect(style->subControlRect(QStyle::CC_Slider, sliderOption, QStyle::SC_SliderGroove, widget)); + + if (!enabled) + { + RenderHelper::renderFrame(painter,grooveRect,1,0,grooveColor); + } + else + { + bool upsideDown(sliderOption->upsideDown); + + // handle rect + QRect handleRect(style->subControlRect(QStyle::CC_Slider, sliderOption, QStyle::SC_SliderHandle, widget)); + + if (sliderOption->orientation == Qt::Horizontal) + { + QRect leftRect(grooveRect); + QRect rightRect(grooveRect); + leftRect.setRight(handleRect.right() - Metrics::Slider_ControlThickness / 2); + rightRect.setLeft(handleRect.left() + Metrics::Slider_ControlThickness / 2); + + if (upsideDown) + { + RenderHelper::renderFrame(painter,leftRect,1,0,grooveColor); + RenderHelper::renderFrame(painter,rightRect,1,0,contentColor); + } + else + { + RenderHelper::renderFrame(painter,leftRect,1,0,contentColor); + RenderHelper::renderFrame(painter,rightRect,1,0,grooveColor); + } + } + else + { + QRect topRect(grooveRect); + topRect.setBottom(handleRect.bottom() - Metrics::Slider_ControlThickness / 2); + QRect bottomRect(grooveRect); + bottomRect.setTop(handleRect.top() + Metrics::Slider_ControlThickness / 2); + + if (upsideDown) + { + RenderHelper::renderFrame(painter,topRect,1,0,grooveColor); + RenderHelper::renderFrame(painter,bottomRect,1,0,contentColor); + } + else + { + RenderHelper::renderFrame(painter,topRect,1,0,contentColor); + RenderHelper::renderFrame(painter,bottomRect,1,0,grooveColor); + } + } + } + } + + // handle + if (sliderOption->subControls & QStyle::SC_SliderHandle) + { + // get rect and center + QRectF handleRect(style->subControlRect(QStyle::CC_Slider, sliderOption, QStyle::SC_SliderHandle, widget)); + + // handle state + bool handleActive(sliderOption->activeSubControls & QStyle::SC_SliderHandle); + bool sunken(state & (QStyle::State_On | QStyle::State_Sunken)); + + QColor handleBorder = schemeLoader->getColor(widget,option,SchemeLoader::Slider_HandleBorder); + QColor handleBackground = schemeLoader->getColor(widget,option,SchemeLoader::Slider_HandleBackground); + + //draw handle + painter->setRenderHint(QPainter::Antialiasing); + handleRect.adjust(1, 1, -1, -1); + + if (handleBorder.isValid()) + { + QPen pen; + pen.setCapStyle(Qt::RoundCap); + pen.setColor(handleBorder); + pen.setWidth(2); + painter->setPen(pen); + handleRect = RenderHelper::strokedRect(handleRect, 2); + } + else + { + painter->setPen(Qt::NoPen); + } + painter->setBrush(handleBackground); + painter->drawRoundedRect(handleRect, handleRect.width()/2, handleRect.width()); + } + + return true; +} + +QRect Kiran::Style::sliderElementRect(const QStyle *style, QStyle::SubElement subElement, const QStyleOption *option, const QWidget *widget) +{ + QRect rect(option->rect); + + switch (subElement) + { + case QStyle::SE_SliderFocusRect: + { + const auto sliderOption(qstyleoption_cast(option)); + QRect r(option->rect); + if (sliderOption->orientation == Qt::Vertical) + { + int thickness = Metrics::Slider_GrooveThickness + 8; + return QRect(r.center().x() - thickness / 2, r.top() + 1, thickness + 1, r.height() - 1); + } + else + { + int thickness = Metrics::Slider_GrooveThickness + 6; + return QRect(r.left() + 1, r.center().y() - thickness / 2, r.width() - 1, thickness + 1); + } + } + default: + break; + } + + return rect; +} + +bool Kiran::Style::sliderSubControlRect(const QStyle *style, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget, QRect &controlRect) +{ + // cast option and check + const auto sliderOption(qstyleoption_cast(opt)); + if (!sliderOption) + return false; + + switch (sc) + { + case QStyle::SC_SliderGroove: + { + // direction + bool horizontal(sliderOption->orientation == Qt::Horizontal); + + // get base class rect + //NOTE:该处直接调用父类的方法 + QRect grooveRect = qobject_cast(style)->ParentStyle::subControlRect(QStyle::CC_Slider,opt,sc,widget); + grooveRect = RenderHelper::insideMargin(grooveRect, style->pixelMetric(QStyle::PM_DefaultFrameWidth, opt, widget)); + + // centering + if (horizontal) + grooveRect = RenderHelper::centerRect(grooveRect, grooveRect.width(), Metrics::Slider_GrooveThickness); + else + grooveRect = RenderHelper::centerRect(grooveRect, Metrics::Slider_GrooveThickness, grooveRect.height()); + + controlRect = grooveRect; + break; + } + default: + return false; + } + + return true; +} diff --git a/style/src/draw-helper/draw-slider-helper.h b/style/src/draw-helper/draw-slider-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..ff8b18cdfd8c5e3dfabed91fd6cfc46b4b7c8630 --- /dev/null +++ b/style/src/draw-helper/draw-slider-helper.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_SLIDER_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_SLIDER_HELPER_H_ + +#include +#include +#include + +class QStyleOption; +class QWidget; +class QStyleOptionComplex; +class QWidget; +class QPainter; +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; +QSize sliderSizeFromContents(const QStyle *style,const QStyleOption *option,const QSize &contentSize,const QWidget *widget); +bool drawCCSlider(const QStyle *style, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget); +QRect sliderElementRect(const QStyle *style,QStyle::SubElement subElement,const QStyleOption *option,const QWidget *widget); +bool sliderSubControlRect(const QStyle *style, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget, QRect &controlRect); +} +} + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_SLIDER_HELPER_H_ diff --git a/style/src/draw-helper/draw-spin-box-helper.cpp b/style/src/draw-helper/draw-spin-box-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f98e99040f3cbe346af7bf1e4f60f75e6e28247f --- /dev/null +++ b/style/src/draw-helper/draw-spin-box-helper.cpp @@ -0,0 +1,232 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#include "draw-spin-box-helper.h" +#include "define.h" +#include "draw-common-helper.h" +#include "metrics.h" +#include "render-helper.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace Kiran::Style; + +QSize Kiran::Style::spinBoxSizeFromContents(const QStyle *style, + const QStyleOption *option, + const QSize &contentSize, + const QWidget *widget) +{ + const auto spinBoxOption(qstyleoption_cast(option)); + if (!spinBoxOption) return contentSize; + + bool flat(!spinBoxOption->frame); + + // copy size + QSize size(contentSize); + + // add editor margins + int frameWidth(style->pixelMetric(QStyle::PM_SpinBoxFrameWidth, option, widget)); + if (!flat) size = RenderHelper::expandSize(size, frameWidth); + + // add button width and spacing + size.rwidth() += 2 * size.height() - 1; + + return size; +} + +bool Kiran::Style::spinBoxSubControlRect(const QStyle *style, + const QStyleOptionComplex *option, + QStyle::SubControl subControl, + const QWidget *widget, + QRect &controlRect) +{ + const auto spinBoxOption(qstyleoption_cast(option)); + if (!spinBoxOption) return false; + + bool flat(!spinBoxOption->frame); + + QRect rect(option->rect); + switch (subControl) + { + case QStyle::SC_SpinBoxFrame: + controlRect = flat ? QRect() : rect; + break; + case QStyle::SC_SpinBoxUp: + if (rect.width() > 2 * rect.height() + 24) + { + controlRect = QRect(rect.right() - rect.height(), rect.top(), rect.height(), rect.height() - 1); + } + else + { + controlRect = QRect(rect.right() - 0.6 * rect.height() + 1, rect.top(), rect.height() * 0.6, rect.height() / 2 + 3); + } + break; + case QStyle::SC_SpinBoxDown: + if (rect.width() > 2 * rect.height() + 24) + { + controlRect = QRect(rect.right() - 2 * rect.height() + 1,rect.top(),rect.height(),rect.height() - 1); + } + else + { + controlRect = QRect(rect.right() - 0.6 * rect.height() + 1,rect.top() + rect.height() / 2 - 2,rect.height() * 0.6,rect.height() / 2 + 2); + } + break; + case QStyle::SC_SpinBoxEditField: + { + int frameWidth(style->pixelMetric(QStyle::PM_SpinBoxFrameWidth, option, widget)); + if (rect.width() > 2 * rect.height() + 24) + controlRect = QRect( + rect.left(), rect.top(), + rect.width() - 2 * rect.height() - frameWidth, + rect.height()); + else + controlRect = QRect( + rect.left(), rect.top(), + rect.width() - 0.6 * rect.height() - frameWidth, + rect.height()); + + // remove right side line editor margins + if (!flat && controlRect.height() >= option->fontMetrics.height() + 2 * frameWidth) + { + controlRect.adjust(frameWidth, frameWidth, 0, -frameWidth); + } + + controlRect = QStyle::visualRect(option->direction, option->rect, controlRect); + break; + } + default: + return false; + } + + return true; +} + +void renderSpinBoxArrow(const QStyle *style, const QStyle::SubControl &subControl, const QStyleOptionSpinBox *option, QPainter *painter, const QWidget *widget) +{ + const QStyle::State &state(option->state); + + QRectF arrowRect(style->subControlRect(QStyle::CC_SpinBox, option, subControl, widget)); + + bool enabled(state & QStyle::State_Enabled); + const bool atLimit = (subControl == QStyle::SC_SpinBoxUp && !(option->stepEnabled & QAbstractSpinBox::StepUpEnabled)) || + (subControl == QStyle::SC_SpinBoxDown && !(option->stepEnabled & QAbstractSpinBox::StepDownEnabled)); + enabled &= !atLimit; + + const bool mouseOver(enabled && (state & QStyle::State_MouseOver)); + bool sunken(state & QStyle::State_Sunken && option->activeSubControls & subControl); + bool hasFocus(state & QStyle::State_HasFocus); + + const bool subControlHover(enabled && mouseOver && (option->activeSubControls & subControl)); + const bool subControlSunken(enabled && (sunken) && (option->activeSubControls & subControl)); + + const QMap subControlFlagMap = { + {QStyle::State_MouseOver, subControlHover}, + {QStyle::State_Sunken, subControlSunken}, + {QStyle::State_Enabled, enabled}}; + + //复制一个子控件StyleOption,更新其中的状态 + QStyleOption tempOption(*option); + for (auto iter = subControlFlagMap.begin(); iter != subControlFlagMap.end(); iter++) + { + if (iter.value()) + { + tempOption.state |= iter.key(); + } + else + { + tempOption.state &= ~iter.key(); + } + } + + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto borderColor = schemeLoader->getColor(widget,option,SchemeLoader::SpinBox_Border); + auto backgroundColor = schemeLoader->getColor(widget,option,SchemeLoader::SpinBox_Background); + auto signColor = schemeLoader->getColor(widget,option,SchemeLoader::SpinBox_SignColor); + + //绘制按钮与输入区域之间的分割线 + if (subControl == QStyle::SC_SpinBoxDown) + { + painter->setBrush(Qt::NoBrush); + painter->setPen(borderColor); + int highlight = hasFocus ? 1 : 0; + painter->drawLine(arrowRect.left(), arrowRect.top() + 2 + highlight, arrowRect.left(), arrowRect.bottom() - 1 - highlight); + } + + if (subControl == QStyle::SC_SpinBoxUp) + { + painter->setBrush(Qt::NoBrush); + painter->setPen(borderColor); + int highlight = hasFocus ? 1 : 0; + painter->drawLine(arrowRect.left(), arrowRect.top() + 2 + highlight, arrowRect.left(), arrowRect.bottom() - 1 - highlight); + } + + //绘制按钮背景色 + painter->setPen(Qt::NoPen); + QColor background = backgroundColor; + painter->setBrush(background); + painter->drawRect(arrowRect.adjusted(0.5,0.5,-0.5,-0.5)); + + //绘制按钮之上的符号 “+”,“-” + QPen pen(signColor, 2); + pen.setCapStyle(Qt::FlatCap); + QRect r = arrowRect.adjusted(1, 2, 0, 0).toRect(); + painter->setPen(pen); + painter->drawLine(r.center() - QPointF(5, 0), r.center() + QPointF(5, 0)); + if (subControl == QStyle::SC_SpinBoxUp) + painter->drawLine(r.center() - QPointF(0, 5), r.center() + QPointF(0, 5)); +} + +bool Kiran::Style::drawCCSpinBox(const QStyle *style, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) +{ + const auto spinBoxOption(qstyleoption_cast(option)); + if (!spinBoxOption) return true; + + const QPalette &palette(option->palette); + const QRect &rect(option->rect); + + if (option->subControls & QStyle::SC_SpinBoxFrame) + { + // detect flat spinboxes + bool flat(!spinBoxOption->frame); + flat |= (rect.height() < 2 * Metrics::Frame_FrameWidth + Metrics::SpinBox_ArrowButtonWidth); + if (flat) + { + const auto &background = palette.color(QPalette::Base); + + painter->setBrush(background); + painter->setPen(Qt::NoPen); + painter->drawRect(rect); + } + else + { + style->drawPrimitive(QStyle::PE_FrameLineEdit, option, painter, widget); + } + } + + //裁剪掉按钮露出圆角边框的区域 + QPainterPath catPath = RenderHelper::roundedPath(RenderHelper::insideMargin(option->rect, 1), AllCorners, 4); + PainterSaver painterSaver(painter); + painter->setClipPath(catPath); + if (option->subControls & QStyle::SC_SpinBoxUp) renderSpinBoxArrow(style, QStyle::SC_SpinBoxUp, spinBoxOption, painter, widget); + if (option->subControls & QStyle::SC_SpinBoxDown) renderSpinBoxArrow(style, QStyle::SC_SpinBoxDown, spinBoxOption, painter, widget); + + return true; +} diff --git a/style/src/draw-helper/draw-spin-box-helper.h b/style/src/draw-helper/draw-spin-box-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..aabb39cc33a3ccdd93ecfbf2739dabba5e461352 --- /dev/null +++ b/style/src/draw-helper/draw-spin-box-helper.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_SPIN_BOX_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_SPIN_BOX_HELPER_H_ + +#include + +class QStyle; +class QStyleOptionComplex; +class QStyleOption; +class QWidget; +class QPainter; +class QSize; +class QRect; +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; +QSize spinBoxSizeFromContents(const QStyle *style, const QStyleOption *option, const QSize &contentSize, const QWidget *widget); +bool spinBoxSubControlRect(const QStyle* style, const QStyleOptionComplex* option, QStyle::SubControl subControl, const QWidget* widget, QRect& controlRect); +bool drawCCSpinBox(const QStyle* style, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget); + +} +} +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_SPIN_BOX_HELPER_H_ diff --git a/style/src/draw-helper/draw-tab-widget-helper.cpp b/style/src/draw-helper/draw-tab-widget-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..114bb5bdad671d25d4e28d2f93141ba900e10559 --- /dev/null +++ b/style/src/draw-helper/draw-tab-widget-helper.cpp @@ -0,0 +1,350 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "draw-tab-widget-helper.h" +#include "kiran-palette.h" +#include "draw-common-helper.h" +#include "metrics.h" +#include "render-helper.h" +#include "scheme-loader-fetcher.h" + +#include +#include +#include +#include +#include + +//FIXME:需要和sizeFromContents获取的大小对应 + +using namespace Kiran::Style; + +void tabLayout(const QStyle *style, const QStyleOptionTab *opt, const QWidget *widget, QRect *textRect, QRect *iconRect) +{ + QRect tr = opt->rect; + bool verticalTabs = RenderHelper::isVerticalTab(opt->shape); + + if (verticalTabs) + tr.setRect(0, 0, tr.height(), tr.width()); // 0, 0 as we will have a translate transform + + /// 垂直和水平偏移量,以突出当前选中的Tab标签 + int verticalShift = style->pixelMetric(QStyle::PM_TabBarTabShiftVertical, opt, widget); + int horizontalShift = style->pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, opt, widget); + + int hpadding = style->pixelMetric(QStyle::PM_TabBarTabHSpace, opt, widget) / 2; + int vpadding = style->pixelMetric(QStyle::PM_TabBarTabVSpace, opt, widget) / 2; + + if (opt->shape == QTabBar::RoundedSouth || opt->shape == QTabBar::TriangularSouth) + verticalShift = -verticalShift; + + tr.adjust(hpadding, verticalShift - vpadding, horizontalShift - hpadding, vpadding); + bool selected = opt->state & QStyle::State_Selected; + if (selected) { + tr.setTop(tr.top() - verticalShift); + tr.setRight(tr.right() - horizontalShift); + } + + // left widget + if (!opt->leftButtonSize.isEmpty()) { + tr.setLeft(tr.left() + 4 + + (verticalTabs ? opt->leftButtonSize.height() : opt->leftButtonSize.width())); + } + // right widget + if (!opt->rightButtonSize.isEmpty()) { + tr.setRight(tr.right() - 4 - + (verticalTabs ? opt->rightButtonSize.height() : opt->rightButtonSize.width())); + } + + // icon + if (!opt->icon.isNull()) { + QSize iconSize = opt->iconSize; + if (!iconSize.isValid()) { + int iconExtent = style->pixelMetric(QStyle::PM_SmallIconSize); + iconSize = QSize(iconExtent, iconExtent); + } + QSize tabIconSize = opt->icon.actualSize(iconSize, + (opt->state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled, + (opt->state & QStyle::State_Selected) ? QIcon::On : QIcon::Off); + // High-dpi icons do not need adjustment; make sure tabIconSize is not larger than iconSize + tabIconSize = QSize(qMin(tabIconSize.width(), iconSize.width()), qMin(tabIconSize.height(), iconSize.height())); + + *iconRect = QRect(tr.left(), tr.center().y() - tabIconSize.height() / 2, + tabIconSize.width(), tabIconSize.height()); + if (!verticalTabs) + *iconRect = style->visualRect(opt->direction, opt->rect, *iconRect); + tr.setLeft(tr.left() + tabIconSize.width() + 4); + } + + if (!verticalTabs) + tr = style->visualRect(opt->direction, opt->rect, tr); + + *textRect = tr; +} + +QSize Kiran::Style::tabBarTabSizeFromContents(const QStyle* style, const QStyleOption* option, const QSize& contentSize, const QWidget* widget) +{ + const auto tabOption(qstyleoption_cast(option)); + const bool hasText(tabOption && !tabOption->text.isEmpty()); + const bool hasIcon(tabOption && !tabOption->icon.isNull()); + const bool hasLeftButton(tabOption && !tabOption->leftButtonSize.isEmpty()); + const bool hasRightButton(tabOption && !tabOption->leftButtonSize.isEmpty()); + + // calculate width increment for horizontal tabs + int widthIncrement = 0; + if (hasIcon && !(hasText || hasLeftButton || hasRightButton)) widthIncrement -= 4; + if (hasText && hasIcon) widthIncrement += Metrics::TabBar_TabItemSpacing; + if (hasLeftButton && (hasText || hasIcon)) widthIncrement += Metrics::TabBar_TabItemSpacing; + if (hasRightButton && (hasText || hasIcon || hasLeftButton)) widthIncrement += Metrics::TabBar_TabItemSpacing; + + // add margins + QSize size(contentSize); + + // compare to minimum size + const bool verticalTabs(tabOption && RenderHelper::isVerticalTab(tabOption->shape)); + if (verticalTabs) + { + size.rheight() += widthIncrement; + if (hasIcon && !hasText) + size = size.expandedTo(QSize(Metrics::TabBar_TabMinHeight, 0)); + else + size = size.expandedTo(QSize(Metrics::TabBar_TabMinHeight, Metrics::TabBar_TabMinWidth)); + } + else + { + size.rwidth() += widthIncrement; + if (hasIcon && !hasText) + size = size.expandedTo(QSize(0, Metrics::TabBar_TabMinHeight)); + else + size = size.expandedTo(QSize(Metrics::TabBar_TabMinWidth, Metrics::TabBar_TabMinHeight)); + } + + return size; +} + +bool Kiran::Style::drawPEFrameTabWidget(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + auto schemeLoader = SchemeLoaderFetcher::getSchemeLoader(); + auto background = schemeLoader->getColor(widget,option,SchemeLoader::Frame_Background); + auto border = schemeLoader->getColor(widget,option,SchemeLoader::Frame_Border); + RenderHelper::renderFrame(painter, option->rect,1, 0, background, border); + return true; +} + +bool Kiran::Style::drawPEFrameTabBarBase(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + return true; +} + +bool Kiran::Style::drawControlTabBarTabLabel(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + return false; +} + +bool Kiran::Style::drawControlTabBarTabShape(const QStyle* style, const QStyleOption* option, QPainter* painter, const QWidget* widget) +{ + const auto tabOption(qstyleoption_cast(option)); + if (!tabOption) return true; + + const QPalette &palette(option->palette); + const QStyle::State &state(option->state); + bool enabled(state & QStyle::State_Enabled); + bool selected(state & QStyle::State_Selected); + bool mouseOver((state & QStyle::State_Active) && !selected && (state & QStyle::State_MouseOver) && enabled); + + // check if tab is being dragged + bool isDragged(widget && selected && painter->device() != widget); + //bool isLocked(widget && _tabBarData->isLocked(widget)); + + const QStyleOptionTab::TabPosition &position = tabOption->position; + const bool isSingle(position == QStyleOptionTab::OnlyOneTab); + const bool isQtQuickControl(RenderHelper::isQtQuickControl(option, widget)); + bool isFirst(isSingle || position == QStyleOptionTab::Beginning); + bool isLast(isSingle || position == QStyleOptionTab::End); + bool isLeftOfSelected(/*!isLocked &&*/ tabOption->selectedPosition == QStyleOptionTab::NextIsSelected); + bool isRightOfSelected(/*!isLocked &&*/ tabOption->selectedPosition == QStyleOptionTab::PreviousIsSelected); + + // true if widget is aligned to the frame + // need to check for 'isRightOfSelected' because for some reason the isFirst flag is set when active tab is being moved + isFirst &= !isRightOfSelected; + isLast &= !isLeftOfSelected; + + // 若是反向布局交换状态,改变与方向相关属性 + const bool reverseLayout(option->direction == Qt::RightToLeft); + const bool verticalTabs(RenderHelper::isVerticalTab(tabOption->shape)); + if (reverseLayout && !verticalTabs) + { + qSwap(isFirst, isLast); + qSwap(isLeftOfSelected, isRightOfSelected); + } + + //根据TabBar方向调整选项卡圆角的位置 + Corners corners; + switch (tabOption->shape) + { + case QTabBar::RoundedNorth: + case QTabBar::TriangularNorth: + corners = CornersTop; + break; + case QTabBar::RoundedSouth: + case QTabBar::TriangularSouth: + corners = CornersBottom; + break; + case QTabBar::RoundedWest: + case QTabBar::TriangularWest: + corners = CornersLeft; + break; + case QTabBar::RoundedEast: + case QTabBar::TriangularEast: + corners = CornersRight; + break; + default: + break; + } + +// quint64 specialPseudoClass = PseudoClass_Unspecified; +// if (selected) +// { +// specialPseudoClass = PseudoClass_Pressed; +// } +// QColor backgroundColor = scheme->getColor(widget, option, SchemeLoader::Button_BackgroundColor, specialPseudoClass); + QStyleOption tempOption(*option); + if( selected ) + { + tempOption.state |= QStyle::State_Sunken; + } + QColor backgroundColor = KiranPalette::instance()->color(widget,&tempOption,KiranPalette::Widget,KiranPalette::Background); + RenderHelper::renderTabBarTab(painter, option->rect, corners,4, backgroundColor ); + + return true; +} + +QRect Kiran::Style::tabBarSubElementRect(const QStyle* style, QStyle::SubElement subElement, const QStyleOption* option, const QWidget* widget) +{ + const auto *tabOption(qstyleoption_cast(option)); + + if( !widget || !widget->inherits("QTabBar") ) + { + return QRect(); + } + + const auto tabBar = qobject_cast(widget); + const QRect& rect = option->rect; + + switch (subElement) + { + case QStyle::SE_TabBarTearIndicator: + case QStyle::SE_TabBarTearIndicatorRight: + return QRect(); + + //QTabBar::setTabButton所设置位于单个Tab左右的控件 + case QStyle::SE_TabBarTabLeftButton: + case QStyle::SE_TabBarTabRightButton: + { + if( !tabOption ) + { + return QRect(); + } + + bool isLeft = (subElement == QStyle::SE_TabBarTabLeftButton); + + QSize size( isLeft?tabOption->leftButtonSize:tabOption->rightButtonSize ); + if( size.isEmpty() ) + { + return QRect(); + } + + QRect buttonRect( QPoint(0,0), size ); + + switch( tabOption->shape ) + { + case QTabBar::RoundedNorth: + case QTabBar::TriangularNorth: + + case QTabBar::RoundedSouth: + case QTabBar::TriangularSouth: + if( isLeft) + { + buttonRect.moveLeft( rect.left() + Metrics::TabBar_TabMarginWidth ); + } + else + { + buttonRect.moveRight( rect.right() - Metrics::TabBar_TabMarginWidth ); + } + buttonRect.moveTop( ( rect.height() - buttonRect.height() )/2 ); + buttonRect = QStyle::visualRect( option->direction,option->rect, buttonRect ); + break; + + case QTabBar::RoundedWest: + case QTabBar::TriangularWest: + if(isLeft) + { + buttonRect.moveBottom( rect.bottom() - Metrics::TabBar_TabMarginWidth ); + } + else + { + buttonRect.moveTop( rect.top() + Metrics::TabBar_TabMarginWidth ); + } + buttonRect.moveLeft( ( rect.width() - buttonRect.width() )/2 ); + break; + + case QTabBar::RoundedEast: + case QTabBar::TriangularEast: + if(isLeft) + { + buttonRect.moveTop( rect.top() + Metrics::TabBar_TabMarginWidth ); + } + else + { + buttonRect.moveBottom( rect.bottom() - Metrics::TabBar_TabMarginWidth ); + } + buttonRect.moveLeft( ( rect.width() - buttonRect.width() )/2 ); + break; + + default: break; + } + + return buttonRect; + } + + case QStyle::SE_TabBarTabText: + { + QRect textRect; + QRect iconRect; + tabLayout(style, tabOption, widget, &textRect, &iconRect); + return textRect; + } + + //当QTabBar过多时,QTabBar左右滑动的按钮 + case QStyle::SE_TabBarScrollLeftButton: + case QStyle::SE_TabBarScrollRightButton: + { + bool isLeft = (subElement == QStyle::SE_TabBarScrollLeftButton); + const int buttonWidth = style->pixelMetric(QStyle::PM_TabBarScrollButtonWidth, nullptr,widget); + QRect scrollButtonRect ; + + if(RenderHelper::isVerticalTab(tabBar->shape()) ) + { + scrollButtonRect = QRect(0,isLeft?0:(rect.height()-buttonWidth),rect.width(),buttonWidth); + } + else + { + scrollButtonRect = QRect(isLeft?0:(rect.width()-buttonWidth),0,buttonWidth,rect.height()); + QStyle::visualRect(option->direction,option->rect,scrollButtonRect); + } + return scrollButtonRect; + } + default: + break; + } + + return QRect(); +} diff --git a/style/src/draw-helper/draw-tab-widget-helper.h b/style/src/draw-helper/draw-tab-widget-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..3e88a8042d02e07af0c268408b3110d5bf114240 --- /dev/null +++ b/style/src/draw-helper/draw-tab-widget-helper.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_TAB_WIDGET_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_TAB_WIDGET_HELPER_H_ + +#include +#include + +class QStyle; +class QStyleOption; +class QStyleOptionComplex; +class QPainter; +class QWidget; +class QRect; +namespace Kiran +{ +namespace Style +{ +class SchemeLoader; + +QSize tabBarTabSizeFromContents(const QStyle *style, const QStyleOption *option, const QSize &contentSize, const QWidget *widget); +QRect tabBarSubElementRect(const QStyle *style, QStyle::SubElement subElement, const QStyleOption *option, const QWidget *widget); + +bool drawPEFrameTabWidget(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawPEFrameTabBarBase(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); + +bool drawControlTabBarTabLabel(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +bool drawControlTabBarTabShape(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); +} // namespace Style +} // namespace Kiran + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_DRAW_HELPER_DRAW_TAB_WIDGET_HELPER_H_ diff --git a/style/src/kiran-style-plugin.cpp b/style/src/kiran-style-plugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..597d9475051af153454d1c25ad7b1cd32b20d961 --- /dev/null +++ b/style/src/kiran-style-plugin.cpp @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "kiran-style-plugin.h" +#include "style.h" + +#include +#include + +KiranStylePlugin::KiranStylePlugin(QObject *parent) : QStylePlugin(parent) +{ + +} + +QStringList KiranStylePlugin::keys() const +{ + return QStringList() << "Kiran" + << "Kiran-dark"; +} + +QStyle *KiranStylePlugin::create(const QString & key) +{ + //TODO:根据情况,后期加入黑名单,屏蔽掉一些不需要加载KiranStyle的Qt程序 + qInfo("create style:%s",key.toStdString().c_str()); + + if( key.compare("Kiran",Qt::CaseInsensitive) == 0 || + key.compare("Kiran-dark",Qt::CaseInsensitive) == 0 ) + { + return new Kiran::Style::Style(); + } + + return nullptr; +} diff --git a/style/src/kiran-style-plugin.h b/style/src/kiran-style-plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..c0da13d57781f033b67b8dcdafa06fac4dc3050f --- /dev/null +++ b/style/src/kiran-style-plugin.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_KIRAN_STYLE_PLUGIN_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_KIRAN_STYLE_PLUGIN_H_ + +#include + +class KiranStylePlugin : public QStylePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "../kiran-style-plugin.json") +public: + explicit KiranStylePlugin(QObject* parent = nullptr); + QStringList keys() const; + QStyle* create(const QString&) override; +}; + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_KIRAN_STYLE_PLUGIN_H_ diff --git a/style/src/metrics.h b/style/src/metrics.h new file mode 100644 index 0000000000000000000000000000000000000000..c4b073b918b3ba159b36fd6efa7ad34e12e92eb3 --- /dev/null +++ b/style/src/metrics.h @@ -0,0 +1,147 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_METRICS_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_METRICS_H_ + +namespace Kiran +{ +namespace Style +{ +//* metrics +struct Metrics +{ + // frames + static constexpr int Frame_FrameWidth = 4; + static constexpr int Frame_FrameRadius = 4; + + // layout + static constexpr int Layout_TopLevelMarginWidth = 10; + static constexpr int Layout_ChildMarginWidth = 6; + static constexpr int Layout_DefaultSpacing = 6; + + // line editors + static constexpr int LineEdit_FrameWidth = 7; + + // menu items + static constexpr int Menu_FrameWidth = 0; + static constexpr int MenuItem_MarginWidth = 5; + static constexpr int MenuItem_ItemSpacing = 4; + static constexpr int MenuItem_AcceleratorSpace = 16; + static constexpr int MenuButton_IndicatorWidth = 24; + + // combobox + static constexpr int ComboBox_FrameWidth = 4; + static constexpr int ComboBox_MarginHeight = 2; + static constexpr int ComboBox_MarginWidth = 2; + static constexpr int ComboBox_MinHeight = 34; + static constexpr int ComboBox_MinWidth = 80; + + // spinbox + static constexpr int SpinBox_FrameWidth = LineEdit_FrameWidth; + static constexpr int SpinBox_ArrowButtonWidth = 20; + + // groupbox title margin + static constexpr int GroupBox_TitleMarginWidth = 4; + + // buttons + static constexpr int Button_MinHeight = 34; + static constexpr int Button_MinWidth = 60; + static constexpr int Button_MarginHeight = 2; + static constexpr int Button_MarginWidth = 4; + static constexpr int Button_ItemSpacing = 4; + + // tool buttons + static constexpr int ToolButton_MarginWidth = 6; + static constexpr int ToolButton_ItemSpacing = 4; + static constexpr int ToolButton_InlineIndicatorWidth = 12; + + // checkboxes and radio buttons + static constexpr int CheckBox_Size = 15; + static constexpr int CheckBox_FocusMarginWidth = 2; + static constexpr int CheckBox_ItemSpacing = 4; + static constexpr int CheckBox_Radius = Frame_FrameRadius - 1; + + // menubar items + static constexpr int MenuBarItem_MarginWidth = 10; + static constexpr int MenuBarItem_MarginHeight = 6; + + // scrollbars + static constexpr int ScrollBar_Extend = 12; + static constexpr int ScrollBar_SliderWidth = 8; + static constexpr int ScrollBar_MinSliderHeight = 20; + static constexpr int ScrollBar_NoButtonHeight = (ScrollBar_Extend-ScrollBar_SliderWidth)/2; + static constexpr int ScrollBar_SingleButtonHeight = ScrollBar_Extend; + static constexpr int ScrollBar_DoubleButtonHeight = 2*ScrollBar_Extend; + + // toolbars + static constexpr int ToolBar_FrameWidth = 6; + static constexpr int ToolBar_HandleExtent = 10; + static constexpr int ToolBar_HandleWidth = 6; + static constexpr int ToolBar_SeparatorWidth = 8; + static constexpr int ToolBar_ExtensionWidth = 20; + static constexpr int ToolBar_ItemSpacing = 0; + + // progressbars + static constexpr int ProgressBar_BusyIndicatorSize = 14; + static constexpr int ProgressBar_Thickness = 8; + static constexpr int ProgressBar_ItemSpacing = 4; + + // mdi title bar + static constexpr int TitleBar_MarginWidth = 4; + + // sliders + static constexpr int Slider_TickLength = 8; + static constexpr int Slider_TickMarginWidth = 2; + static constexpr int Slider_GrooveThickness = 6; + static constexpr int Slider_ControlThickness = 16; + + // tabbar + static constexpr int TabBar_TabMarginHeight = 4; + static constexpr int TabBar_TabMarginWidth = 8; + static constexpr int TabBar_TabMinWidth = 80; + static constexpr int TabBar_TabMinHeight = 30; + static constexpr int TabBar_TabItemSpacing = 8; + static constexpr int TabBar_TabOverlap = 1; + static constexpr int TabBar_BaseOverlap = 2; + + // tab widget + static constexpr int TabWidget_MarginWidth = 4; + + // toolbox + static constexpr int ToolBox_TabMinWidth = 80; + static constexpr int ToolBox_TabItemSpacing = 4; + static constexpr int ToolBox_TabMarginWidth = 8; + + // tooltips + static constexpr int ToolTip_FrameWidth = 3; + + // list headers + static constexpr int Header_MarginWidth = 6; + static constexpr int Header_ItemSpacing = 4; + static constexpr int Header_ArrowSize = 10; + + // tree view + static constexpr int ItemView_ArrowSize = 12; + static constexpr int ItemView_ItemMarginWidth = 3; + static constexpr int SidePanel_ItemMarginWidth = 4; + + // splitter + static constexpr int Splitter_SplitterWidth = 1; + + // shadow dimensions + static constexpr int Shadow_Overlap = 2; +}; +} +} +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_METRICS_H_ diff --git a/style/src/render-helper.cpp b/style/src/render-helper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f78d2042c07d3331489f9b716a7606d944530f0 --- /dev/null +++ b/style/src/render-helper.cpp @@ -0,0 +1,371 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "render-helper.h" +#include "metrics.h" + +#include +#include +#include +#include + +#define private public +#include +#undef private + +using namespace Kiran::Style; + +PainterSaver::PainterSaver(QPainter *painter) :_painter(painter) +{ + _painter->save(); +} + +PainterSaver::~PainterSaver() +{ + _painter->restore(); +} + +int RenderHelper::mnemonicsTextFlags() +{ + //Qt::TextHideMnemonic Qt::TextShowMnemonic + return Qt::TextHideMnemonic; +} + +bool RenderHelper::showIconsOnPushButtons() +{ + return true; +} + +bool RenderHelper::drawTreeBranches() +{ + return false; +} + +bool RenderHelper::isQtQuickControl(const QStyleOption *option, const QWidget *widget) +{ + return (widget == nullptr) && option && option->styleObject && option->styleObject->inherits("QQuickItem"); +} + +bool RenderHelper::isVerticalTab(const QTabBar::Shape &shape) +{ + return shape == QTabBar::RoundedEast || shape == QTabBar::RoundedWest || shape == QTabBar::TriangularEast || shape == QTabBar::TriangularWest; +} + +#define property_menu_title "_kiran_property_menu_title_" +bool RenderHelper::isMenuTitle(const QWidget *widget) +{ + if (!widget) return false; + + //FIXME:若控件后续移动,该属性不会自动更新,将会导致问题 + QVariant property(widget->property(property_menu_title)); + if (property.isValid()) return property.toBool(); + + QWidget *parent = widget->parentWidget(); + if (qobject_cast(parent)) + { + foreach (auto child, parent->findChildren()) + { + if (child->defaultWidget() != widget) continue; + const_cast(widget)->setProperty(property_menu_title, true); + return true; + } + } + + const_cast(widget)->setProperty(property_menu_title, false); + return false; +} + +QRectF RenderHelper::strokedRect(const QRectF &rect, const qreal penWidth) +{ + qreal adjustment = 0.5 * penWidth; + return rect.adjusted(adjustment, adjustment, -adjustment, -adjustment); +} + +QRect RenderHelper::insideMargin(const QRect &r, int margin) +{ + return insideMargin(r,margin,margin); +} + +QRect RenderHelper::insideMargin(const QRect &r, int marginWidth, int marginHeight) +{ + return r.adjusted(marginWidth, marginHeight, -marginWidth, -marginHeight); +} + +QRect RenderHelper::centerRect(const QRect &rect, int width, int height) +{ + return {rect.left() + (rect.width() - width) / 2, rect.top() + (rect.height() - height) / 2, width, height}; +} + +QRect RenderHelper::centerRect(const QRect &rect, const QSize &size) +{ + return centerRect(rect,size.width(),size.height()); +} + +QSize RenderHelper::expandSize(const QSize &size, int margin) +{ + return expandSize(size,margin,margin); +} + +QSize RenderHelper::expandSize(const QSize &size, int marginWidth, int marginHeight) +{ + return size + 2 * QSize(marginWidth, marginHeight); +} + +QPainterPath RenderHelper::roundedPath(const QRectF &rect, Corners corners, int radius) +{ + QPainterPath path; + + if (corners == 0) + { + path.addRect(rect); + return path; + } + + if (corners == AllCorners) + { + path.addRoundedRect(rect, radius, radius); + return path; + } + + const QSizeF cornerSize(2 * radius, 2 * radius); + + // rotate counterclockwise + // top left corner + if (corners & CornerTopLeft) + { + path.moveTo(rect.topLeft() + QPointF(radius, 0)); + path.arcTo(QRectF(rect.topLeft(), cornerSize), 90, 90); + } + else + path.moveTo(rect.topLeft()); + + // bottom left corner + if (corners & CornerBottomLeft) + { + path.lineTo(rect.bottomLeft() - QPointF(0, radius)); + path.arcTo(QRectF(rect.bottomLeft() - QPointF(0, 2 * radius), cornerSize), 180, 90); + } + else + path.lineTo(rect.bottomLeft()); + + // bottom right corner + if (corners & CornerBottomRight) + { + path.lineTo(rect.bottomRight() - QPointF(radius, 0)); + path.arcTo(QRectF(rect.bottomRight() - QPointF(2 * radius, 2 * radius), cornerSize), 270, 90); + } + else + path.lineTo(rect.bottomRight()); + + // top right corner + if (corners & CornerTopRight) + { + path.lineTo(rect.topRight() + QPointF(0, radius)); + path.arcTo(QRectF(rect.topRight() - QPointF(2 * radius, 0), cornerSize), 0, 90); + } + else + path.lineTo(rect.topRight()); + + path.closeSubpath(); + return path; +} + +void RenderHelper::renderFrame(QPainter *painter, const QRect &rect, int penWidth, int radius, const QColor &color, const QColor &outline) +{ + PainterSaver painterSave(painter); + + QRectF frameRect(rect); + + // set pen + if (outline.isValid()) + { + painter->setPen(outline); + frameRect = strokedRect(frameRect, penWidth/2.0); + } + else + { + painter->setPen(Qt::NoPen); + } + + // set brush + if (color.isValid()) + painter->setBrush(color); + else + painter->setBrush(Qt::NoBrush); + + // render + painter->drawRoundedRect(frameRect, radius, radius); +} + +void RenderHelper::renderFlatFrame(QPainter *painter, const QRect &rect, int radius, const QColor &color, const QColor &outline) +{ + painter->setRenderHint(QPainter::Antialiasing); + + QRectF frameRect(rect.adjusted(1, 1, -1, -1)); + + // set pen + if (outline.isValid()) + { + painter->setPen(outline); + frameRect = strokedRect(frameRect, 1); + } + else + { + painter->setPen(Qt::NoPen); + } + + // set brush + if (color.isValid()) + painter->setBrush(color); + else + painter->setBrush(Qt::NoBrush); + + QPainterPath path; + path.setFillRule( Qt::WindingFill ); + path.addRect( frameRect.adjusted(2 * radius, 0, 0, 0) ); + path.addRoundedRect( frameRect.adjusted(0, 0, - 2 *radius, 0), radius, radius); + + painter->drawPath( path.simplified() ); +} + +void RenderHelper::renderSeparator(QPainter *painter, const QRect &rect, bool vertical, const QColor &color) +{ + PainterSaver painterSave(painter); + + painter->setRenderHint(QPainter::Antialiasing, false); + painter->setBrush(Qt::NoBrush); + painter->setPen(color); + + if (vertical) + { + painter->translate(rect.width() / 2, 0); + painter->drawLine(rect.topLeft(), rect.bottomLeft()); + } + else + { + painter->translate(0, rect.height() / 2); + painter->drawLine(rect.topLeft(), rect.topRight()); + } +} + +void RenderHelper::renderMenuTitle(const QStyle *style, + QPainter *painter, + const QStyleOptionToolButton *option, + const QWidget *widget, + const QColor &separatorColor) +{ + // 底部渲染分割线 + const QPalette& palette( option->palette ); + auto separatorRect = QRect( option->rect.bottomLeft()-QPoint( 0, Metrics::MenuItem_MarginWidth), QSize( option->rect.width(), 1 ) ); + renderSeparator(painter,separatorRect,false,separatorColor); + + // 渲染文本居中,丢弃图标 + painter->setFont( option->font ); + QRect contentsRect = insideMargin( option->rect, Metrics::MenuItem_MarginWidth ); + style->drawItemText( painter, contentsRect, Qt::AlignCenter, palette, true, option->text, QPalette::WindowText ); +} + +QPixmap RenderHelper::changeSVGFillColor(const QString &svgFile, const QColor &fillColor, const QSize &size) +{ + //调用Qt私有接口,修改所有命名节点其所有的fill属性为指定颜色 + auto tinyDoc = QSvgTinyDocument::load(svgFile); + + auto namedNodes = tinyDoc->m_namedNodes; + for (auto iter = namedNodes.begin(); iter != namedNodes.end(); iter++) + { + auto styleProperty = iter.value()->styleProperty(QSvgStyleProperty::FILL); + if (styleProperty != nullptr) + { + auto fillProperty = dynamic_cast(styleProperty); + fillProperty->m_fill.setColor(fillColor); + } + } + + //渲染到Pixmap之中 + QPixmap tempPixmap(size); + tempPixmap.fill(Qt::transparent); + + QPainter tempPainter; + tempPainter.begin(&tempPixmap); + tempPainter.setRenderHint(QPainter::Antialiasing); + tempPainter.setRenderHint(QPainter::SmoothPixmapTransform); + tinyDoc->draw(&tempPainter); + tempPainter.end(); + + return tempPixmap; +} + +void RenderHelper::renderArrow(QPainter *painter, const QRect &rect, ArrowOrientation orientation, const QColor &color,const QSize& arrowSize) +{ + QString svgFile = QString(":/style-helper/images/arrow.svg"); + PainterSaver painterSave(painter); + painter->setRenderHint(QPainter::Antialiasing); + + //修改箭头默认颜色 + QPixmap tempPixmap = changeSVGFillColor(svgFile,color,arrowSize.isEmpty()?QSize(16,16):arrowSize); + + //旋转图片 + int rotateAngle = 0; + switch (orientation) + { + case Arrow_Up: + rotateAngle = 180; + break; + case Arrow_Down: + break; + case Arrow_Left: + rotateAngle = 90; + break; + case Arrow_Right: + rotateAngle = 270; + break; + default: + break; + } + QMatrix matrix; + matrix.rotate(rotateAngle); + QPixmap arrowPixmap = tempPixmap.transformed(matrix,Qt::SmoothTransformation); + + //绘制图片 + QRect arrowRect = centerRect(rect, arrowPixmap.size()); + painter->drawPixmap(arrowRect,arrowPixmap); +} + +void RenderHelper::renderTabBarTab(QPainter *painter, + const QRect &rect, + Corners corners, + int radius, + const QColor &background, + const QColor &border) +{ + PainterSaver painterSave(painter); + + QRectF frameRect(rect); + + // set pen + if (border.isValid()) + { + painter->setPen(border); + frameRect = strokedRect(frameRect, 1); + } + else + { + painter->setPen(Qt::NoPen); + } + + painter->setBrush(background); + QPainterPath painterPath = roundedPath(frameRect, corners, radius); + + painter->setRenderHint(QPainter::Antialiasing); + painter->drawPath(painterPath); +} diff --git a/style/src/render-helper.h b/style/src/render-helper.h new file mode 100644 index 0000000000000000000000000000000000000000..ae7b668a2b26e66eb4f8db424624f54ff7d73b5e --- /dev/null +++ b/style/src/render-helper.h @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_RENDER_HELPER_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_RENDER_HELPER_H_ + +#include +#include "define.h" + +class QStyleOption; +class QStyleOptionToolButton; + +class QWidget; +class QPainter; +namespace Kiran +{ +namespace Style +{ +class PainterSaver +{ +public: + explicit PainterSaver(QPainter* painter); + ~PainterSaver(); +private: + QPainter* _painter; +}; +namespace RenderHelper +{ +//获取助记符的文本标志位 +int mnemonicsTextFlags(); + +bool showIconsOnPushButtons(); + +//是否绘制树分支 +bool drawTreeBranches(); + +//判断是否绘制对象为QtQuick控件 +bool isQtQuickControl(const QStyleOption *option, const QWidget *widget); + +//根据TabBar形状获取是否显示为垂直 +bool isVerticalTab(const QTabBar::Shape &shape); + +//判断控件是否位于菜单 +bool isMenuTitle(const QWidget* widget); + +//当QPainter中QPen宽度为1时,矩形每个笔画像素侧面向内移动半个像素 +//这样可以使笔划是像素完美的,而不是像素之间的模糊,并防止带有笔划的矩形变得大于矩形的原始大小 +QRectF strokedRect(const QRectF &rect, const qreal penWidth); + +//矩形收缩内边距 +QRect insideMargin(const QRect& r,int margin); +QRect insideMargin(const QRect& r, int marginWidth, int marginHeight); + +//获取矩形中居中一块大小的矩形 +QRect centerRect(const QRect& rect, int width, int height); +QRect centerRect(const QRect& rect, const QSize& size); + +//矩形往外扩张边距大小 +QSize expandSize(const QSize& size, int margin); +QSize expandSize(const QSize& size, int marginWidth, int marginHeight); + +//获取圆角绘制路径 +QPainterPath roundedPath(const QRectF& rect, Corners corners, int radius); + +//绘制边框 +void renderFrame(QPainter* painter, const QRect& rect, int penWidth, int radius, const QColor& color, const QColor& outline = QColor()); +void renderFlatFrame(QPainter *painter, const QRect &rect, int radius, const QColor &color, const QColor &outline); + +//绘制分割线 +void renderSeparator(QPainter* painter, const QRect& rect, bool vertical, const QColor& color); + +//获知菜单标题按钮 +void renderMenuTitle(const QStyle* style,QPainter* painter,const QStyleOptionToolButton* option,const QWidget* widget,const QColor& separatorColor); + +//修改SVG图片之中的fill属性颜色,生成制定大小的Pixmap +QPixmap changeSVGFillColor(const QString& svgFile,const QColor& fillColor,const QSize& size); + +//绘制箭头 +void renderArrow(QPainter* painter, const QRect& rect, ArrowOrientation orientation, const QColor& color,const QSize& arrowSize=QSize()); + +//绘制TabBar Tab +void renderTabBarTab(QPainter* painter, const QRect& rect, Corners corners, int radius, const QColor& background, const QColor& border = QColor()); +}; +} +} + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_SRC_RENDER_HELPER_H_ diff --git a/style/src/scheme-loader-fetcher.cpp b/style/src/scheme-loader-fetcher.cpp new file mode 100644 index 0000000000000000000000000000000000000000..894c1e4ce98c558b0ec91530277ca3068396df1d --- /dev/null +++ b/style/src/scheme-loader-fetcher.cpp @@ -0,0 +1,9 @@ +#include "scheme-loader-fetcher.h" + +#include "kiran-palette.h" +#include "scheme-loader.h" + +Kiran::Style::SchemeLoader* SchemeLoaderFetcher::getSchemeLoader() +{ + return KiranPalette::instance()->getSchemeLoader(); +} diff --git a/style/src/scheme-loader-fetcher.h b/style/src/scheme-loader-fetcher.h new file mode 100644 index 0000000000000000000000000000000000000000..5d3fbcf769e4305e723c77f0e8e99699ec92d1d7 --- /dev/null +++ b/style/src/scheme-loader-fetcher.h @@ -0,0 +1,15 @@ +// +// Created by liuxinhao on 2022/5/11. +// + +#ifndef KIRAN_QT5_INTEGRATION_SCHEMELOADERFETCHER_H +#define KIRAN_QT5_INTEGRATION_SCHEMELOADERFETCHER_H + +#include "scheme-loader.h" + +class SchemeLoaderFetcher +{ +public: + static Kiran::Style::SchemeLoader* getSchemeLoader(); +}; +#endif // KIRAN_QT5_INTEGRATION_SCHEMELOADERFETCHER_H diff --git a/style/src/style.cpp b/style/src/style.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8a2379a962afd69ef539c3d2c8c6e95f94c7391d --- /dev/null +++ b/style/src/style.cpp @@ -0,0 +1,678 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include "style.h" +#include "scheme-loader.h" +#include "render-helper.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//TODO: 后期考虑再度整合到几个大类的源文件之中 +#include "draw-helper/draw-button-helper.h" +#include "draw-helper/draw-combo-box-helper.h" +#include "draw-helper/draw-edit-helper.h" +#include "draw-helper/draw-group-box-helper.h" +#include "draw-helper/draw-indicator-helper.h" +#include "draw-helper/draw-item-view-helper.h" +#include "draw-helper/draw-progress-bar-helper.h" +#include "draw-helper/draw-scroll-bar-helper.h" +#include "draw-helper/draw-slider-helper.h" +#include "draw-helper/draw-spin-box-helper.h" +#include "draw-helper/draw-tab-widget-helper.h" +#include "draw-helper/draw-menu-helper.h" + +using namespace Kiran::Style; + +#if 0 +//调试用 +QDebug operator<<(QDebug dbg, const QColor &color) +{ + QString format = QString("[rgb(%1,%2,%3) #%4%5%6]") + .arg(color.red()).arg(color.green()).arg(color.blue()) + .arg(QString::number(color.red(),16)).arg(QString::number(color.green(),16)).arg(QString::number(color.blue(),16)); + dbg << format; + return dbg; +} +#endif + +Style::Style() + : ParentStyle() +{ +} + +Style::~Style() +{ +} + +int Style::styleHint(QStyle::StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const +{ + switch (hint) + { + //下栏框的下栏列表鼠标追踪 + case SH_ComboBox_ListMouseTracking: + return true; + //菜单栏鼠标追踪 + case SH_MenuBar_MouseTracking: + return true; + //菜单鼠标追踪 + case SH_Menu_MouseTracking: + return true; + //菜单之中子菜单弹出延时时间 + case SH_Menu_SubMenuPopupDelay: + return 150; + //弹出菜单是否支持用户在跨越菜单的其他项时将鼠标光标移动到子菜单 + case SH_Menu_SloppySubMenus: + return true; + //该值目前亦废弃,改用SH_Widget_Animation_Duration + case SH_Widget_Animate: + return true; + //动画持续时间ms,0代表禁用 + case SH_Widget_Animation_Duration: + return 100; + //确定样式是在菜单中显示节,还是将其视为普通分隔符。节是带有文本和图标提示的分隔符 + case SH_Menu_SupportsSections: + return true; + //指示QDialogButtonBox中的标准按钮是否应具有图标 + case SH_DialogButtonBox_ButtonsHaveIcons: + return false; + //GroupBox文本标签垂直对齐选项 + case SH_GroupBox_TextLabelVerticalAlignment: + return Qt::AlignVCenter; + //TabBar的对齐方式 + case SH_TabBar_Alignment: + return Qt::AlignLeft | Qt::AlignVCenter; + //ToolBox中所选页面标题是否显示粗体 + case SH_ToolBox_SelectedPageTitleBold: + return false; + //滚动条上单击鼠标中键滑块是否跳转到该绝对位置 + case SH_ScrollBar_MiddleClickAbsolutePosition: + return true; + //ScrollView是仅围绕内容(如Motif)还是围绕内容、滚动条和角部件(如窗口)绘制框架。 + case SH_ScrollView_FrameOnlyAroundContents: + return false; + //FormLayout对齐其内容的默认方式 + case SH_FormLayoutFormAlignment: + return Qt::AlignLeft | Qt::AlignVCenter; + //FormatLayout对齐标签的默认方式 + case SH_FormLayoutLabelAlignment: + return Qt::AlignRight; + //QFormLayout中字段增长方式的默认值。返回QFormLayout::FieldGrowthPolicy枚举 + case SH_FormLayoutFieldGrowthPolicy: + return QFormLayout::ExpandingFieldsGrow; + //QFormLayout中如何换行的默认方式。返回一个QFormLayout::RowWrapPolicy enum。 + case SH_FormLayoutWrapPolicy: + return QFormLayout::DontWrapRows; + //消息框中的文本是否允许用户交互(如选择) + case SH_MessageBox_TextInteractionFlags: + return Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse; + //进度对话框按钮取消按钮是否居中对齐,否则右对齐 + case SH_ProgressDialog_CenterCancelButton: + return false; + //消息框是否按钮居中 + case SH_MessageBox_CenterButtons: + return false; + //输入控件请求输入面板的时间点,返回类型为QStyle::RequestSoftwareInputPanel。 + case SH_RequestSoftwareInputPanel: + return RSIP_OnMouseClick; + //标题栏无边框 + case SH_TitleBar_NoBorder: + return true; + //DockWidget的按钮是否有Frame + case SH_DockWidget_ButtonsHaveFrame: + return false; + //ToolTip透明文本标签的透明度 + case SH_ToolTipLabel_Opacity: + return 204; + //Table里网格线的颜色 + case SH_Table_GridLineColor: + return KiranPalette::instance()->color(KiranPalette::Normal,KiranPalette::Widget,KiranPalette::Border).rgb(); + default: + return ParentStyle::styleHint(hint, option, widget, returnData); + } +} + +int Style::pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const +{ + switch (metric) + { + case PM_DefaultFrameWidth: + return 6; + case PM_SpinBoxFrameWidth: + return 2; + case PM_ToolBarFrameWidth: + return 6; + case PM_ToolTipLabelFrameWidth: + return 3; + case PM_ComboBoxFrameWidth: + return 3; + case PM_FocusFrameVMargin: + case PM_FocusFrameHMargin: + return 2; + + //布局默认边距 + case PM_LayoutLeftMargin: + case PM_LayoutTopMargin: + case PM_LayoutRightMargin: + case PM_LayoutBottomMargin: + { + //使用子控件边距或顶层窗口边距,取决于部件类型 + if ((option && (option->state & QStyle::State_Window)) || (widget && widget->isWindow())) + { + return 10; + } + else + { + return 6; + } + } + + //布局默认间距 + case PM_LayoutHorizontalSpacing: + case PM_LayoutVerticalSpacing: + return 6; + + //按钮边距 + case PM_ButtonMargin: + return 12; + + //按钮默认指示器边框宽度 + case PM_ButtonDefaultIndicator: + return 0; + //当按钮按下时,按钮的内容水平移动偏移量。 + case PM_ButtonShiftHorizontal: + return 0; + //当按钮按下时,按钮的内容垂直移动偏移量。 + case PM_ButtonShiftVertical: + return 0; + + //菜单栏默认边框宽度 + case PM_MenuBarPanelWidth: + return 0; + //菜单栏水平边距 + case PM_MenuBarHMargin: + return 0; + //菜单栏垂直边距 + case PM_MenuBarVMargin: + return 3; + //菜单项间距 + case PM_MenuBarItemSpacing: + return 10; + //桌面菜单边框宽度 + case PM_MenuDesktopFrameWidth: + return 0; + + //菜单按钮指示器宽度 + case PM_MenuButtonIndicator: + return 24; + + // toolbars + case PM_ToolBarHandleExtent: + return 10; + case PM_ToolBarSeparatorExtent: + return 8; + case PM_ToolBarExtensionExtent: + return pixelMetric(PM_SmallIconSize, option, widget) + 2 * 6; + + case PM_ToolBarItemMargin: + return 0; + case PM_ToolBarItemSpacing: + return 0; + + // tabbars + case PM_TabBarTabShiftVertical: + return 0; + case PM_TabBarTabShiftHorizontal: + return 0; + case PM_TabBarTabOverlap: + return 1; + case PM_TabBarBaseOverlap: + return 2; + case PM_TabBarTabHSpace: + return 2 * 8; + case PM_TabBarTabVSpace: + return 2 * 4; + case PM_TabCloseIndicatorWidth: + case PM_TabCloseIndicatorHeight: + return pixelMetric(PM_SmallIconSize, option, widget); + + // scrollbars + case PM_ScrollBarExtent: + return 5; + case PM_ScrollBarSliderMin: + return 20; + + // scrollview + case PM_ScrollView_ScrollBarOverlap: + return 1; + + // title bar + case PM_TitleBarHeight: + return 2 * 4 + pixelMetric(PM_SmallIconSize, option, widget); + + // sliders + case PM_SliderThickness: + return 16; + case PM_SliderControlThickness: + return 16; + case PM_SliderLength: + return 16; + + // checkboxes and radio buttons + case PM_IndicatorWidth: + case PM_IndicatorHeight: + case PM_ExclusiveIndicatorWidth: + case PM_ExclusiveIndicatorHeight: + return 15; + + // list headers + case PM_HeaderMarkSize: + return 10; + case PM_HeaderMargin: + return 6; + + // dock widget + // return 0 here, since frame is handled directly in polish + case PM_DockWidgetFrameWidth: + return 0; + case PM_DockWidgetTitleMargin: + return 4; + case PM_DockWidgetTitleBarButtonMargin: + return 6; + + case PM_SplitterWidth: + return 1; + case PM_DockWidgetSeparatorExtent: + return 1; + + default: //fallback + break; + } + return ParentStyle::pixelMetric(metric, option, widget); +} + +void Style::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const +{ + bool (*func)(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); + func = nullptr; + + // clang-format off + switch (element) + { + case PE_Frame: func = &drawPEFrame; break; + case PE_FrameFocusRect: func = &drawPEFrameFocusRect; break; + case PE_FrameGroupBox: func = &drawPEFrameGroupBox; break; + + case PE_PanelButtonCommand: func = &drawPEPanelButtonCommand; break; + case PE_PanelButtonTool: func = &drawPEPanelButtonTool; break; + + case PE_FrameLineEdit: func = &drawPEFrameLineEdit; break; + case PE_PanelLineEdit: + { + if( widget && widget->parentWidget() ) + { + /*作为QComoBox和QAbstractSpinBox的子控件时不进行绘制*/ + if( qobject_cast(widget->parentWidget()) || + qobject_cast(widget->parentWidget()) ) + { + return; + } + } + break; + } + case PE_IndicatorButtonDropDown: func = &drawPEIndicatorButtonDropDown; break; + case PE_IndicatorArrowUp: func = &drawPEIndicatorArrowUp; break; + case PE_IndicatorArrowDown: func = &drawPEIndicatorArrowDown; break; + case PE_IndicatorArrowLeft: func = &drawPEIndicatorArrowLeft; break; + case PE_IndicatorArrowRight: func = &drawPEIndicatorArrowRight; break; + case PE_IndicatorRadioButton: func = &drawPEIndicatorRadioButton; break; + case PE_IndicatorCheckBox: func = &drawPEIndicatorCheckBox; break; + case PE_IndicatorBranch: func = &drawPEIndicatorBranch; break; + case PE_FrameTabWidget: func = &drawPEFrameTabWidget; break; + case PE_FrameTabBarBase: func = &drawPEFrameTabBarBase; break; + + case PE_IndicatorToolBarSeparator: func = &drawPEIndicatorToolBarSeparator; break; + case PE_IndicatorToolBarHandle: func = &drawPEIndicatorToolBarHandle; break; + default: break; + } + // clang-format on + + PainterSaver painterSaver(painter); + if (!(func && (*func)(this, option, painter, widget))) + { + ParentStyle::drawPrimitive(element, option, painter, widget); + } +} + +void Style::drawComplexControl(QStyle::ComplexControl control, + const QStyleOptionComplex *option, + QPainter *painter, + const QWidget *widget) const +{ + bool (*func)(const QStyle *style, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget); + func = nullptr; + + // clang-format off + switch (control) + { + case QStyle::CC_ToolButton: func = &drawCCToolButton; break; + case QStyle::CC_ComboBox: func = &drawCCComboBox; break; + case QStyle::CC_ScrollBar: func = &drawCCScrollBar; break; + case QStyle::CC_SpinBox: func = &drawCCSpinBox; break; + case QStyle::CC_GroupBox: func = &drawCCGroupBox; break; + case QStyle::CC_Slider: func = &drawCCSlider; break; + default: break; + } + // clang-format on + + painter->save(); + if (!(func && (*func)(this, option, painter, widget))) + { + ParentStyle::drawComplexControl(control, option, painter, widget); + } + painter->restore(); +} + +QRect Style::subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const +{ + QRect rect; + + switch (element) + { + case QStyle::SE_TabBarTearIndicator: + case QStyle::SE_TabBarTabLeftButton: + case QStyle::SE_TabBarTabRightButton: + case QStyle::SE_TabBarTabText: + case QStyle::SE_TabBarScrollLeftButton: + case QStyle::SE_TabBarScrollRightButton: + case QStyle::SE_TabBarTearIndicatorRight: + rect = tabBarSubElementRect(this, element, option, widget); + return rect; + + case SE_ProgressBarGroove: + case SE_ProgressBarContents: + case SE_ProgressBarLabel: + rect = progressBarElementRect(this, element, option, widget); + return rect; + + case QStyle::SE_SliderFocusRect: + rect = sliderElementRect(this, element, option, widget); + return rect; + + default: + break; + } + + return ParentStyle::subElementRect(element, option, widget); +} + +QRect Style::subControlRect(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget) const +{ + typedef bool (*SubControlRectFunc)(const QStyle *, const QStyleOptionComplex *, QStyle::SubControl, const QWidget *, QRect &); + static const QMap SubControlRectMap = { + {QStyle::CC_ToolButton, &toolButtonSubControlRect}, + {QStyle::CC_ComboBox, &comboBoxSubControlRect}, + {QStyle::CC_ScrollBar, &scrollBarSubControlRect}, + {QStyle::CC_SpinBox, &spinBoxSubControlRect}, + {QStyle::CC_GroupBox, &groupBoxSubControlRect}, + {QStyle::CC_Slider, &sliderSubControlRect}}; + + QRect controlRect; + if ((SubControlRectMap.find(cc) != SubControlRectMap.end()) && + (*SubControlRectMap.find(cc))(this, opt, sc, widget, controlRect)) + { + return controlRect; + } + else + { + return ParentStyle::subControlRect(cc, opt, sc, widget); + } +} + +QSize Style::sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &contentSize, const QWidget *widget) const +{ + switch (type) + { + case CT_PushButton: + return pushButtonSizeFromContents(this, option, contentSize, widget); + case CT_ToolButton: + return toolButtonSizeFromContents(this, option, contentSize, widget); + case CT_TabBarTab: + return tabBarTabSizeFromContents(this, option, contentSize, widget); + case CT_ComboBox: + return comboBoxSizeFromContents(this, option, contentSize, widget); + case CT_SpinBox: + return spinBoxSizeFromContents(this, option, contentSize, widget); + case CT_GroupBox: + return groupBoxSizeFromContents(this, option, contentSize, widget); + case CT_ProgressBar: + return progressBarSizeFromContents(this, option, contentSize, widget); + case CT_Slider: + return sliderSizeFromContents(this, option, contentSize, widget); + case CT_CheckBox: + break; + case CT_RadioButton: + break; + case CT_Splitter: + break; + case CT_MenuItem: + break; + case CT_MenuBarItem: + break; + case CT_MenuBar: + break; + case CT_Menu: + break; + case CT_LineEdit: + { + int buttuonMargin = pixelMetric(QStyle::PM_ButtonMargin,option,widget); + QSize size = contentSize + QSize(buttuonMargin,buttuonMargin); + return size; + } + case CT_SizeGrip: + break; + case CT_TabWidget: + break; + case CT_DialogButtons: + break; + case CT_HeaderSection: + break; + case CT_MdiControls: + break; + case CT_ItemViewItem: + break; + default: + break; + } + return ParentStyle::sizeFromContents(type, option, contentSize, widget); +} + +void Style::drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const +{ + bool (*func)(const QStyle *style, const QStyleOption *option, QPainter *painter, const QWidget *widget); + func = nullptr; + + //不绘制的控件元素集合 + static QSet emptyControlSet = {CE_ToolBar}; + + // clang-format off + switch (element) + { + case QStyle::CE_ShapedFrame: func = &drawControlShapedFrame; break; + + case QStyle::CE_TabBarTabLabel: func = &drawControlTabBarTabLabel; break; + case QStyle::CE_TabBarTabShape: func = &drawControlTabBarTabShape; break; + + case QStyle::CE_ToolBoxTabLabel: func = &drawControlToolButtonLabel; break; + case QStyle::CE_ComboBoxLabel: func = &drawControlComboBoxLabel; break; + + case QStyle::CE_HeaderLabel: func = &drawControlHeaderLabel; break; + case QStyle::CE_HeaderSection: func = &drawControlHeaderSection; break; + case QStyle::CE_HeaderEmptyArea: func = &drawControlHeaderEmptyArea; break; + + case QStyle::CE_ScrollBarSlider: func = &drawControlScrollBarSlider; break; + + case QStyle::CE_ProgressBar: func = &drawControlProgressBar; break; + case CE_ProgressBarGroove: func = &drawControlProgressBarGroove; break; + case CE_ProgressBarContents: func = &drawControlProgressBarContents; break; + case CE_ProgressBarLabel: func = &drawControlProgressBarLabel; break; + + case QStyle::CE_MenuBarItem: func = &drawControlMenuBarItem; break; + case QStyle::CE_MenuBarEmptyArea: func = &drawControlMenuBarEmptyArea; break; + default: break; + } + // clang-format on + + painter->save(); + if (!(func && (*func)(this, option, painter, widget))) + { + if( emptyControlSet.find(element) == emptyControlSet.end() ) + { + ParentStyle::drawControl(element, option, painter, widget); + } + } + painter->restore(); +} + +void Style::polish(QWidget *widget) +{ + if (!widget) + return; + + // enable mouse over effects for all necessary widgets + if (qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget) || + qobject_cast(widget)) + { + widget->setAttribute(Qt::WA_Hover); + } + + if( qobject_cast(widget) ) + { + auto scrollArea = qobject_cast(widget); + if( scrollArea->frameShadow() == QFrame::Sunken && scrollArea->focusPolicy()&Qt::StrongFocus ) + { + scrollArea->setAttribute( Qt::WA_Hover ); + } +// scrollArea->viewport()->setAutoFillBackground(false); +// qInfo() << "viewport:" << scrollArea->viewport()->geometry(); +// qInfo() << "scroll area:" << scrollArea->geometry(); + } + + if( QAbstractItemView *itemView = qobject_cast( widget ) ) { + // enable mouse over effects in itemviews' viewport + itemView->viewport()->setAttribute(Qt::WA_Hover); + } + + ParentStyle::polish(widget); +} + +void Style::polish(QApplication *app) +{ + ParentStyle::polish(app); + + QPalette palette; + KiranPalette::instance()->polishPalette(&palette); + QApplication::setPalette(palette); +} + +void Style::polish(QPalette &palette) +{ +// ParentStyle::polish(palette); + KiranPalette::instance()->polishPalette(&palette); +} + +QPixmap Style::standardPixmap(QStyle::StandardPixmap standardPixmap, const QStyleOption *opt, const QWidget *widget) const +{ + switch (standardPixmap) + { + case SP_ArrowRight: + { + QPalette palette = widget?widget->palette():qApp->palette(); + QPalette::ColorRole colorRole = widget?widget->foregroundRole():QPalette::ButtonText; + QPixmap arrowPixmap(16,16); + arrowPixmap.fill(Qt::transparent); + QPainter painter(&arrowPixmap); + RenderHelper::renderArrow(&painter,arrowPixmap.rect(),Kiran::Style::Arrow_Right,palette.color(colorRole),arrowPixmap.size()); + return arrowPixmap; + } + default: + break; + } + return ParentStyle::standardPixmap(standardPixmap, opt, widget); +} + +QIcon Style::standardIcon(QStyle::StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const +{ + static const QMap titleBarIconMap = { + {SP_TitleBarMinButton,":/style-helper/images/window-minimum-symbolic.svg"}, + {SP_TitleBarMaxButton,":/style-helper/images/window-maximum-symbolic.svg"}, + {SP_TitleBarCloseButton,":/style-helper/images/window-close-symbolic.svg"}, + {SP_TitleBarNormalButton,":/style-helper/images/window-unmaximum-symbolic.svg"}, + + }; + switch (standardIcon) + { + case SP_TitleBarMinButton: + case SP_TitleBarMaxButton: + case SP_TitleBarCloseButton: + case SP_TitleBarNormalButton: + { + QSize defaultTitleBarIconSize(16,16); + + QIcon res; + auto iter = titleBarIconMap.find(standardIcon); + QString iconPath = iter.value(); + QPalette palette = qApp->palette(); + + QPixmap normal = RenderHelper::changeSVGFillColor(iconPath,palette.color(QPalette::Normal,QPalette::Foreground),defaultTitleBarIconSize); + res.addPixmap(normal,QIcon::Normal); + + QPixmap disable = RenderHelper::changeSVGFillColor(iconPath,palette.color(QPalette::Disabled,QPalette::Foreground),defaultTitleBarIconSize); + res.addPixmap(normal,QIcon::Disabled); + return res; + } + default: + break; + } + return ParentStyle::standardIcon(standardIcon, option, widget); +} diff --git a/style/src/style.h b/style/src/style.h new file mode 100644 index 0000000000000000000000000000000000000000..08e192a5b8afc0657a84500e3dcf323b80c281c4 --- /dev/null +++ b/style/src/style.h @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#ifndef KIRAN_QT5_PLATFORMTHEME_STYLE_KIRAN_STYLE_H_ +#define KIRAN_QT5_PLATFORMTHEME_STYLE_KIRAN_STYLE_H_ + +#include + +#include "draw-helper/draw-common-helper.h" + +#if 1 +#include +#define ParentStyle QFusionStyle +#else +#include +#define ParentStyle QCommonStyle +#endif + +namespace Kiran +{ +namespace Style +{ +class Style : public ParentStyle +{ +public: + explicit Style(); + ~Style(); + + int styleHint(StyleHint hint, const QStyleOption* option, const QWidget* widget, QStyleHintReturn* returnData) const override; + int pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const override; + + void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const override; + void drawComplexControl(ComplexControl control,const QStyleOptionComplex* option,QPainter* painter,const QWidget* widget) const override; + void drawControl(ControlElement element,const QStyleOption* option,QPainter* painter,const QWidget* widget) const override; + + QRect subElementRect(SubElement element, const QStyleOption* option, const QWidget* widget) const override; + QRect subControlRect(ComplexControl cc,const QStyleOptionComplex* opt,SubControl sc,const QWidget* widget) const override; + QSize sizeFromContents(ContentsType type,const QStyleOption* option,const QSize& contentSize,const QWidget* widget) const override; + + QPixmap standardPixmap(StandardPixmap standardPixmap, const QStyleOption* opt, const QWidget* widget) const override; + QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption* option, const QWidget* widget) const override; + + void polish(QWidget* widget) override; + void polish(QApplication* app) override; + void polish(QPalette& pal) override; +}; +} // namespace Style +} // namespace Kiran + +#endif //KIRAN_QT5_PLATFORMTHEME_STYLE_KIRAN_STYLE_H_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..bc286f3c886502bdae1be994b2599c4595de90d0 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.5) + +project(test) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +find_package(PkgConfig REQUIRED) +find_package(Qt5 COMPONENTS Widgets Svg) +pkg_search_module(KLOG_QT5 REQUIRED klog-qt5) +#pkg_search_module(KIRAN_STYLE_HELPER REQUIRED kiran-style-helper) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +file(GLOB_RECURSE SRC "src/*.cpp" "src/*.h" "src/*.ui") + +add_executable(${PROJECT_NAME} ${SRC}) + +message("current source:${SRC}") + +target_link_libraries(${PROJECT_NAME} PRIVATE + Qt5::Widgets + Qt5::Gui + Qt5::Svg + kiran-style-helper + ${KLOG_QT5_LIBRARIES}) + +target_include_directories(${PROJECT_NAME} PRIVATE + ${KLOG_QT5_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} + ${Qt5Gui_PRIVATE_INCLUDE_DIRS} + ${Qt5Widgets_PRIVATE_INCLUDE_DIRS} + ${Qt5Svg_PRIVATE_INCLUDE_DIRS}) diff --git a/test/src/main.cpp b/test/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1e9b071d3d5c13052d8e92ed2fe9917ab707b4a --- /dev/null +++ b/test/src/main.cpp @@ -0,0 +1,79 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#include +#include +#include +#include +#include +#include +#include +#include "qt-widget-factor.h" + +bool test_load_plugin() +{ + QPluginLoader loader; + loader.setFileName("/usr/lib64/qt5/plugins/platformthemes/libqkiran.so"); + if(loader.load()) + { + auto plugin = qobject_cast(loader.instance()); + if(!plugin) + { + qInfo() << "can't cast to QPlatformThemePlugin"; + return false; + } + else + { + qInfo() << "cast to QPlatformThemePlugin success"; + } + } + else + { + qInfo() << "load failed" << loader.errorString(); + return false; + } + + loader.setFileName("/usr/lib64/qt5/plugins/styles/libkiranstyle.so"); + if(loader.load()) + { + auto plugin = qobject_cast(loader.instance()); + if(!plugin) + { + qInfo() << "can't cast to QStylePlugin"; + return false; + } + else + { + qInfo() << "cast to QStylePlugin success"; + } + } + else + { + qInfo() << "load failed" << loader.errorString(); + return false; + } + + return true; +} + +int main(int argc,char* argv[]) +{ + QApplication app(argc,argv); + QtWidgetFactor factor; + factor.show(); + + QPalette palette = app.palette(); + palette.setColor(QPalette::Active,QPalette::Window,"red"); + app.setPalette(palette,""); + return app.exec(); +} \ No newline at end of file diff --git a/test/src/qt-widget-factor.cpp b/test/src/qt-widget-factor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4a452a625a52e2299058165edb8693816c207763 --- /dev/null +++ b/test/src/qt-widget-factor.cpp @@ -0,0 +1,172 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ + +#include "qt-widget-factor.h" + +#include +#include +#include +#include +#include +#include +#include "kiran-style-property.h" +#include "ui_qt-widget-factor.h" + +QtWidgetFactor::QtWidgetFactor(QWidget* parent) : QWidget(parent), ui(new Ui::QtWidgetFactor) +{ + ui->setupUi(this); + + initStyleSwitch(); + initDisableSwitch(); + initMenu(); + initToolBar(); + initTabBar(); + + Kiran::Style::PropertyHelper::setButtonType(ui->pushbutton_normal, Kiran::Style::BUTTON_Default); +} + +QtWidgetFactor::~QtWidgetFactor() +{ + delete ui; +} + +void QtWidgetFactor::initStyleSwitch() +{ + auto keys = QStyleFactory::keys(); + for (auto key : keys) + { + ui->comboBox_switchStyle->addItem(key); + } + + connect(ui->comboBox_switchStyle, &QComboBox::currentTextChanged, [this](const QString& text) { + qInfo() << "set style:" << text; + qApp->setStyle(QStyleFactory::create(text)); + }); + + int curIdx = ui->comboBox_switchStyle->findText("Kiran"); + ui->comboBox_switchStyle->setCurrentIndex(curIdx); +} + +void QtWidgetFactor::initDisableSwitch() +{ + connect(ui->checkBox_disable, &QCheckBox::stateChanged, [this](int state) { + if (state == 2) + { + ui->tabWidget->setDisabled(true); + } + else + { + ui->tabWidget->setEnabled(true); + } + }); +} + +void QtWidgetFactor::initMenu() +{ + QMenu* menu = new QMenu; + menu->addAction("Action1"); + menu->addAction("Action2"); + ui->toolButton_menu->setMenu(menu); + ui->toolButton_menu_2->setMenu(menu); +} +void QtWidgetFactor::initToolBar() +{ + //toolbar + auto toolbar = new QToolBar(); + ui->layout_toolbar->addWidget(toolbar); + + QStringList apps = {"firefox", "eom", "gnote", "gpick", "mate-desktop", "fcitx"}; + QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << "/usr/share/icons/We10X/apps/scalable/"); + qInfo() << QIcon::themeSearchPaths(); + for (const QString& app : apps) + { + auto toolButton = new QToolButton; + toolButton->setIcon(QIcon::fromTheme(app)); + toolbar->addWidget(toolButton); + } +} + +void QtWidgetFactor::initTabBar() +{ + //tabbar + ui->tabWidget->tabBar()->setTabButton(0, QTabBar::LeftSide, new QCheckBox); + + //tab shape + static const QMap shapeMap = { + {QTabWidget::Rounded, ui->radio_tabshape_rounded}, + {QTabWidget::Triangular, ui->radio_tabshape_trianguler}}; + auto tabShape = ui->tabWidget->tabShape(); + auto iter = shapeMap.find(tabShape); + if (iter != shapeMap.end()) + { + iter.value()->setChecked(true); + } + + connect(ui->buttonGroup_2, QOverload::of(&QButtonGroup::buttonToggled), [&](QAbstractButton* button, bool toggled) { + if (toggled) + { + auto radio = qobject_cast(button); + if (!radio) + return; + + for (auto iter = shapeMap.begin(); iter != shapeMap.end(); iter++) + { + if (iter.value() == radio) + ui->tabWidget->setTabShape(iter.key()); + } + } + }); + + //tab position + static const QMap positionMap = { + {QTabWidget::North, ui->radio_tabposition_north}, + {QTabWidget::South, ui->radio_tabposition_south}, + {QTabWidget::West, ui->radio_tabposition_west}, + {QTabWidget::East, ui->radio_tabposition_east}}; + auto tabPosition = ui->tabWidget->tabPosition(); + auto positionIter = positionMap.find(tabPosition); + if (positionIter != positionMap.end()) + { + positionIter.value()->setChecked(true); + } + + connect(ui->buttonGroup, QOverload::of(&QButtonGroup::buttonToggled), [&](QAbstractButton* button, bool toggled) { + if (toggled) + { + auto radio = qobject_cast(button); + if (!radio) + return; + + for (auto iter = positionMap.begin(); iter != positionMap.end(); iter++) + { + if (iter.value() == radio) + ui->tabWidget->setTabPosition(iter.key()); + } + } + }); + + //docment mode + bool documentMode = ui->tabWidget->documentMode(); + ui->check_docmode->setChecked(documentMode); + connect(ui->check_docmode, &QCheckBox::toggled, [&](bool checked) { + ui->tabWidget->setDocumentMode(checked); + }); + + //auto hide + bool autoHide = ui->tabWidget->tabBarAutoHide(); + ui->check_tab_autohide->setChecked(autoHide); + connect(ui->check_tab_autohide, &QCheckBox::toggled, [&](bool checked) { + ui->tabWidget->setTabBarAutoHide(checked); + }); +} diff --git a/test/src/qt-widget-factor.h b/test/src/qt-widget-factor.h new file mode 100644 index 0000000000000000000000000000000000000000..3863c99a3abc8f76fc46e468f81c0636bceb48fd --- /dev/null +++ b/test/src/qt-widget-factor.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2020 ~ 2022 KylinSec Co., Ltd. + * kiran-qt5-integration is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + * + * Author: liuxinhao + */ +#ifndef KIRAN_QT5_PLATFORMTHEME_TEST_QT_WIDGET_FACTOR_H_ +#define KIRAN_QT5_PLATFORMTHEME_TEST_QT_WIDGET_FACTOR_H_ + +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui +{ +class QtWidgetFactor; +} +QT_END_NAMESPACE + +class QtWidgetFactor : public QWidget +{ + Q_OBJECT + +public: + explicit QtWidgetFactor(QWidget *parent = nullptr); + ~QtWidgetFactor() override; + + void initStyleSwitch(); + void initDisableSwitch(); + void initMenu(); + void initToolBar(); + void initTabBar(); + +private: + Ui::QtWidgetFactor *ui; +}; + +#endif //KIRAN_QT5_PLATFORMTHEME_TEST_QT_WIDGET_FACTOR_H_ diff --git a/test/src/qt-widget-factor.ui b/test/src/qt-widget-factor.ui new file mode 100644 index 0000000000000000000000000000000000000000..e340d84c74a06258368b959ec430786cd5ea2ab7 --- /dev/null +++ b/test/src/qt-widget-factor.ui @@ -0,0 +1,2122 @@ + + + QtWidgetFactor + + + + 0 + 0 + 1140 + 963 + + + + QtWidgetFactor + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + + 0 + 0 + + + + 切换主题: + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + disable + + + + + + + + + QTabWidget::Rounded + + + 5 + + + true + + + false + + + true + + + true + + + true + + + + Buttons + + + + + + PushButtons + + + true + + + + 10 + + + + + 0 + + + + + normal + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + + + + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + + + checkable + + + true + + + true + + + + + + + flat + + + true + + + + + + + flat and checkable + + + true + + + true + + + true + + + + + + + + + + + + ToolButtons + + + true + + + + + + 0 + + + + + + 0 + 35 + + + + normal + + + + + + + icon + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + + + text beside icon + + + + ../../../../.designer/backup../../../../.designer/backup + + + Qt::ToolButtonTextBesideIcon + + + + + + + text under icon + + + + ../../../../.designer/backup../../../../.designer/backup + + + Qt::ToolButtonTextUnderIcon + + + + + + + ... + + + Qt::UpArrow + + + + + + + ... + + + Qt::DownArrow + + + + + + + ... + + + Qt::LeftArrow + + + + + + + ... + + + Qt::RightArrow + + + + + + + + 0 + 35 + + + + + 16777215 + 35 + + + + checkable + + + true + + + Qt::NoArrow + + + + + + + + 0 + 35 + + + + + 16777215 + 35 + + + + ToolButton Menu Button + + + QToolButton::MenuButtonPopup + + + Qt::ToolButtonTextOnly + + + Qt::NoArrow + + + + + + + + 0 + 35 + + + + + 16777215 + 35 + + + + ToolButton Menu Instant Popup + + + QToolButton::InstantPopup + + + + + + + + + + + + RadioButtons + + + true + + + + + + + + normal + + + + + + + checked + + + true + + + + + + + not checkable + + + false + + + + + + + + + + + + CheckBoxs + + + + + + + + normal + + + false + + + + + + + checked + + + true + + + + + + + notcheckable + + + false + + + + + + + tristate + + + true + + + + + + + + + + + + Dialog Button Boxs + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + true + + + + + + + + + + + + CommandLinkButtons + + + + + + + + Normal + + + false + + + 测试文本 + + + + + + + Checked + + + true + + + true + + + + + + + Default + + + true + + + + + + + + + + + + ToolBar + + + + + + 0 + + + 0 + + + + + + + + + + TabBarSettings + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + North + + + buttonGroup + + + + + + + Trianguler + + + buttonGroup_2 + + + + + + + + + + + + + + TabBar AutoHide + + + + + + + Tab position: + + + + + + + South + + + buttonGroup + + + + + + + + + + + + + + Tab shape: + + + + + + + Document mode: + + + + + + + East + + + buttonGroup + + + + + + + West + + + buttonGroup + + + + + + + Rounded + + + buttonGroup_2 + + + + + + + + + + + + + Item Widgets + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + 8 + + + + + 9 + + + + + 10 + + + + + 11 + + + + + 12 + + + + + 13 + + + + + 14 + + + + + 15 + + + + + 16 + + + + + 17 + + + + + 18 + + + + + 19 + + + + + + + + true + + + Qt::SolidLine + + + true + + + false + + + true + + + true + + + + 新建行 + + + + + 新建行 + + + + + 新建行 + + + + + 新建行 + + + + + 新建行 + + + + + 新建行 + + + + + 新建行 + + + + + 新建行 + + + + + 新建列 + + + + + 新建列 + + + + + 新建列 + + + + + 新建列 + + + + + 新建列 + + + + + 新建列 + + + + + 新建列 + + + + + + + + false + + + false + + + true + + + + + + + + + 1 + + + + 1-1 + + + + 1-1-1 + + + + 1-1-1-1 + + + + 1-1-1-1-1 + + + + 1-1-1-1-1-1 + + + + 1-1-1-1-1-1-1 + + + + 1-1-1-1-1-1-1-1 + + + + 1-1-1-1-1-1-1-1 + + + + + + + + + + + + 1-2 + + + + + 1-3 + + + + + 1-4 + + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + 8 + + + + + 9 + + + + + 10 + + + + + 11 + + + + + 12 + + + + + 13 + + + + + 14 + + + + + 15 + + + + + 16 + + + + + + + + + Containers + + + + + + 0 + + + + + 0 + 0 + 1118 + 704 + + + + ScrollArea + + + + + + true + + + + + 0 + 0 + 1098 + 684 + + + + + + + 东皇太一 +吉日兮辰良,穆将愉兮上皇; +抚长剑兮玉珥,璆锵鸣兮琳琅; +瑶席兮玉瑱,盍将把兮琼芳; +蕙肴蒸兮兰藉,奠桂酒兮椒浆; +扬枹兮拊鼓,疏缓节兮安歌; +陈竽瑟兮浩倡; +灵偃蹇兮姣服,芳菲菲兮满堂; +五音纷兮繁会,君欣欣兮乐康。 + + + + + + + + + + + + + 0 + 0 + 98 + 28 + + + + Frame + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 0 + 0 + 98 + 28 + + + + Widget + + + + + + + + + + + 0 + 0 + 214 + 86 + + + + MDI Area + + + + + + + + + + + 0 + 0 + 98 + 57 + + + + Dock Widget + + + + + + + + + + + + + + + + Input Widgets + + + + 6 + + + 6 + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Noto Sans CJK SC'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">湘夫人</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">帝子降兮北渚,目眇眇兮愁予;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">袅袅兮秋风,洞庭波兮木叶下;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">登白薠兮骋望,与佳期兮夕张;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">鸟何萃兮苹中,罾何为兮木上?</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">沅有茝兮醴有兰,思公子兮未敢言;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">荒忽兮远望,观流水兮潺湲;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">麋何食兮庭中,蛟何为兮水裔;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">朝驰余马兮江皋,夕济兮西澨;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">闻佳人兮召余,将腾驾兮偕逝;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">筑室兮水中,葺之兮荷盖;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">荪壁兮紫坛,播芳椒兮成堂;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">桂栋兮兰橑,辛夷楣兮药房;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">罔薜荔兮为帷,擗蕙櫋兮既张;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">白玉兮为镇,疏石兰兮为芳;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">芷葺兮荷屋,缭之兮杜衡;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">合百草兮实庭,建芳馨兮庑门;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">九嶷缤兮并迎,灵之来兮如云;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">捐余袂兮江中,遗余褋兮醴浦;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">搴汀洲兮杜若,将以遗兮远者;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">时不可兮骤得,聊逍遥兮容与!</span></p></body></html> + + + + + + + QTextEdit + + + + + + + Qt::Horizontal + + + + + + + QKeySequenceEdit + + + + + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + QPlainTextEdit + + + + + + + 湘君 +君不行兮夷犹,蹇谁留兮中洲; +美要眇兮宜修,沛吾乘兮桂舟; +令沅湘兮无波,使江水兮安流; +望夫君兮未来,吹参差兮谁思; +驾飞龙兮北征,邅吾道兮洞庭; +薜荔柏兮蕙绸,荪桡兮兰旌; +望涔阳兮极浦,横大江兮扬灵; +扬灵兮未极,女婵媛兮为余太息; +横流涕兮潺湲,隐思君兮陫侧; +桂棹兮兰枻,斵冰兮积雪; +采薜荔兮水中,搴芙蓉兮木末; +心不同兮媒劳,恩不甚兮轻绝; +石濑兮浅浅,飞龙兮翩翩; +交不忠兮怨长,期不信兮告余以不闲; +朝骋骛兮江皋,夕弭节兮北渚; +鸟次兮屋上,水周兮堂下; +捐余玦兮江中,遗余佩兮醴浦; +采芳洲兮杜若,将以遗兮下女; +时不可兮再得,聊逍遥兮容与。 + + + + + + + + + + QLinEdit + + + + + + + + + + QSpinBox + + + true + + + + + + QSpinBox + + + + + + + QSpinBox + + + + + + + QDateTimeEdit + + + + + + + QDateEdit + + + + + + + QDoubleSpinBox + + + + + + + QTimeEdit + + + + + + + + 0 + 0 + + + + + 240 + 0 + + + + + 240 + 16777215 + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + QAbstractSpinBox::UpDownArrows + + + + + + + + + + QComboBox + + + true + + + + 0 + + + 0 + + + 0 + + + 36 + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + firefox + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + eom + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + gpick + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + kiran-cpanel-power + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + kiran-cpanel-account + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + kiran-cpanel-display + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + kiran-cpanel-timedate + + + + ../../../../.designer/backup../../../../.designer/backup + + + + + + + + + 0 + 0 + + + + true + + + false + + + + + + + + 0 + 0 + + + + + + + + empty: + + + + + + + normal: + + + + + + + editable: + + + + + + + flat: + + + + + + + fontcombobox: + + + + + + + flat and ediable: + + + + + + + + 0 + 0 + + + + true + + + true + + + + 新建项目 + + + + + 新建项目 + + + + + 新建项目 + + + + + 新建项目 + + + + + + + + + 0 + 0 + + + + false + + + + 新建项目 + + + + + 新建项目 + + + + + 新建项目 + + + + + 新建项目 + + + + + + + + + + + + 120 + 120 + + + + + + + + + Display Widgets + + + + + + + + + + + + GraphicsView + + + + + + + ProgressBar + + + + + + 24 + + + + + + + 24 + + + Qt::Vertical + + + + + + + 24 + + + true + + + + + + + 0 + + + -1 + + + + + + + + + + LCD Number + + + + + + QFrame::Box + + + QFrame::Sunken + + + 8 + + + QLCDNumber::Filled + + + 987654321.123000025749207 + + + + + + + QFrame::Box + + + QFrame::Sunken + + + QLCDNumber::Outline + + + + + + + QFrame::Box + + + QFrame::Sunken + + + QLCDNumber::Flat + + + + + + + + + + Line + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + + + + + + + + Calendar Widget + + + + + + + Text Browser + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Noto Sans CJK SC'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">大司命</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">广开兮天门,纷吾乘兮玄云;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">令飘风兮先驱,使涷雨兮洒尘;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">君回翔兮以下,逾空桑兮从女;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">纷总总兮九州,何寿夭兮在予;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">高飞兮安翔,乘清气兮御阴阳;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">吾与君兮齐速,导帝之兮九坑;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">灵衣兮被被,玉佩兮陆离;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">一阴兮一阳,众莫知兮余所为;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">折疏麻兮瑶华,将以遗兮离居;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">老冉冉兮既极,不寖近兮愈疏;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">乘龙兮辚辚,高驰兮冲天;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">结桂枝兮延伫,羌愈思兮愁人;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">愁人兮奈何,愿若今兮无亏;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">固人命兮有当,孰离合兮可为?</span></p></body></html> + + + + + + + + Display Widgets + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + QOpenGLWidget + + + + + + + + TextBrowser + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><title>Qt Network 5.9</title><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Noto Sans CJK SC'; font-size:10pt; font-weight:400; font-style:normal;"> +<table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"> +<tr> +<td> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="qtdocheader"></a><a href="../qtdoc/supported-platforms-and-configurations.html#qt-5-9"><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">Q</span></a><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">t 5.9</span></p></td> +<td> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">Qt Network</span></p></td></tr></table> +<table border="0" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;" cellspacing="2" cellpadding="0"> +<tr> +<td width="100%"> +<p align="right" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="buildversion"></a><span style=" font-family:'Cantarell'; font-size:11pt;">Q</span><span style=" font-family:'Cantarell'; font-size:11pt;">t 5.9.0 Reference Documentation</span></p></td></tr></table> +<p style=" margin-top:14px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="toc"></a><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">C</span><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">ontents</span></p> +<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Cantarell'; font-size:11pt;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="#getting-started"><span style=" text-decoration: underline; color:#2a76c6;">Getting Started</span></a></li> +<li style=" font-family:'Cantarell'; font-size:11pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="#articles-and-guides"><span style=" text-decoration: underline; color:#2a76c6;">Articles and Guides</span></a></li> +<li style=" font-family:'Cantarell'; font-size:11pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="#api-reference"><span style=" text-decoration: underline; color:#2a76c6;">API Reference</span></a></li> +<li style=" font-family:'Cantarell'; font-size:11pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="#licenses-and-attributions"><span style=" text-decoration: underline; color:#2a76c6;">Licenses and Attributions</span></a></li></ul> +<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="sidebar-content"></a><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">Q</span><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">t Network</span><span style=" font-family:'Cantarell'; font-size:11pt;"> </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="details"></a><span style=" font-family:'Cantarell'; font-size:11pt;">Q</span><span style=" font-family:'Cantarell'; font-size:11pt;">t Network provides a set of APIs for programming applications that use TCP/IP. Operations such as requests, cookies, and sending data over HTTP are handled by various C++ classes.</span><a name="getting-started"></a><span style=" font-family:'Cantarell'; font-size:11pt;"> </span></p> +<p style=" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="getting-started"></a><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">G</span><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">etting Started</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">To use Qt Network classes,add this directive into the C++ files:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Monospace'; font-size:11pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> #include &lt;QtNetwork&gt;</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Monospace'; font-size:11pt;"><br /></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">To link against the Qt Network module, add this line to the project file:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Monospace'; font-size:11pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> QT += network</span></p> +<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="articles-and-guides"></a><span style=" font-family:'Monospace'; font-size:11pt;"> </span></p> +<p style=" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="articles-and-guides"></a><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">A</span><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">rticles and Guides</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">These articles contain information about Qt Network setup and about applications with networking capabilities.</span></p> +<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Cantarell'; font-size:11pt;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="qtnetwork-programming.html"><span style=" text-decoration: underline; color:#2a76c6;">Network Programming with Qt</span></a> - Programming applications with networking capabilities</li> +<li style=" font-family:'Cantarell'; font-size:11pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="bearer-management.html"><span style=" text-decoration: underline; color:#2a76c6;">Bearer Management</span></a> - An API to control the system's connectivity state</li> +<li style=" font-family:'Cantarell'; font-size:11pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="ssl.html"><span style=" text-decoration: underline; color:#2a76c6;">Secure Sockets Layer (SSL) Classes</span></a> - Classes for secure communication over network sockets<a name="api-reference"></a> </li></ul> +<p style=" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="api-reference"></a><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">A</span><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">PI Reference</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">These are links to the API reference materials.</span></p> +<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Cantarell'; font-size:11pt;" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a href="qtnetwork-module.html"><span style=" text-decoration: underline; color:#2a76c6;">C++ Classes</span></a><a name="licenses-and-attributions"></a> </li></ul> +<p style=" margin-top:16px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><a name="licenses-and-attributions"></a><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">L</span><span style=" font-family:'Cantarell'; font-size:11pt; font-weight:600;">icenses and Attributions</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">Qt Network is available under commercial licenses from </span><a href="http://www.qt.io/about-us/"><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">The Qt Company</span></a><span style=" font-family:'Cantarell'; font-size:11pt;">. In addition, it is available under the </span><a href="http://www.gnu.org/licenses/lgpl-3.0.html"><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">GNU Lesser General Public License, version 3</span></a><span style=" font-family:'Cantarell'; font-size:11pt;">, or the </span><a href="http://www.gnu.org/licenses/gpl-2.0.html"><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">GNU General Public License, version 2</span></a><span style=" font-family:'Cantarell'; font-size:11pt;">. See </span><a href="../qtdoc/licensing.html"><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">Qt Licensing</span></a><span style=" font-family:'Cantarell'; font-size:11pt;"> for further details.</span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">Qt Network can use the </span><a href="https://www.openssl.org/"><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">OpenSSL Toolkit</span></a><span style=" font-family:'Cantarell'; font-size:11pt;"> as a backend. The library is then linked against OpenSSL in a way that requires compliance with the </span><a href="https://www.openssl.org/source/license.html"><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">OpenSSL License</span></a><span style=" font-family:'Cantarell'; font-size:11pt;">. To allow linking OpenSSL with Qt Network under the GPL, following exceptions to the GPL do apply:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Monospace'; font-size:11pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> In addition, as a special exception, the copyright holders listed above give</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> permission to link the code of its release of Qt with the OpenSSL project's</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> &quot;OpenSSL&quot; library (or modified versions of the &quot;OpenSSL&quot; library that use the</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> same license as the original version), and distribute the linked executables.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Monospace'; font-size:11pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> You must comply with the GNU General Public License version 2 in all</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> respects for all of the code used other than the &quot;OpenSSL&quot; code. If you</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> modify this file, you may extend this exception to your version of the file,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> but you are not obligated to do so. If you do not wish to do so, delete</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Monospace'; font-size:11pt;"> this exception statement from your version of this file.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Monospace'; font-size:11pt;"><br /></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">Also note shipping OpenSSL might cause </span><a href="ssl.html#import-and-export-restrictions"><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">Import and Export Restrictions</span></a><span style=" font-family:'Cantarell'; font-size:11pt;"> to apply. </span></p> +<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;">© 2017 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners.<br />The documentation provided herein is licensed under the terms of the </span><a href="http://www.gnu.org/licenses/fdl.html"><span style=" font-family:'Cantarell'; font-size:11pt; text-decoration: underline; color:#2a76c6;">GNU Free Documentation License version 1.3</span></a><span style=" font-family:'Cantarell'; font-size:11pt;"> as published by the Free Software Foundation.<br />Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners. </span></p></body></html> + + + + + + + + Just For Test + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + false + + + + 新建项目 + + + + + 新建项目 + + + + + 新建项目 + + + + + 新建项目 + + + + + 新建项目 + + + + + 新建项目 + + + + + 新建项目 + + + + + + + + Qt::Vertical + + + + + + + false + + + + + + + PushButton + + + false + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Just For Test + + + + + Just For Test + + + + + Just For Test + + + + + Just For Test + + + + + + + + + + + + + diff --git a/test/src/test.ui b/test/src/test.ui new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391