diff --git a/.gitignore b/.gitignore index ab6c5476d1a6018f3e4e74e932e0749f5bf9c10c..a14c4af3451a16d496bb3680567381e92d7c4537 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ +# vs /.vs /.vscode /out /build -Version.aps # AMCAX Kernel /AMCAX/ @@ -10,3 +10,5 @@ Version.aps # Visual Studio CMake CMakeUserPresets.json +# Projects +Version.aps \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 9784102a4e7bfc374db3d993cfc9cb81c8733edd..cab64eca912fd43db115877d8129d507e0eab3eb 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,9 @@ set(CMAKE_CXX_STANDARD 17) configure_file(./Resources/Version.rc.in ./Resources/Version.rc) -if (MSVC) +if(MSVC) add_compile_options(/utf-8) - add_compile_options(/bigobj) + add_compile_options(/bigobj) endif() if(DEFINED CMAKE_PREFIX_PATH) @@ -19,16 +19,19 @@ else() message("CMAKE_PREFIX_PATH not defined, set to default: ${CMAKE_PREFIX_PATH}") endif() -#Turn on automoc/autouic, must have -set(CMAKE_AUTOMOC ON) +# Turn on automoc/autouic, must have +set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) - -#find depended qt libraries -find_package(Qt5 COMPONENTS Widgets Core Gui OpenGL REQUIRED) +set(SARIBBON_USE_FRAMELESS_LIB ON) -if(DEFINED CMAKE_BUILD_TYPE) +# find depended qt libraries +find_package(Qt5 COMPONENTS Widgets Core Gui OpenGL sql REQUIRED) + +include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) + +if(DEFINED CMAKE_BUILD_TYPE) message("CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") elseif(DEFINED ENV{CMAKE_BUILD_TYPE}) set(CMAKE_BUILD_TYPE $ENV{CMAKE_BUILD_TYPE}) @@ -38,22 +41,37 @@ else() message("CMAKE_BUILD_TYPE set to default: ${CMAKE_BUILD_TYPE}") endif() -if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") +if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/AMCAX/lib/cmake") list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/third_party/FreeGLUT/x64-Debug/lib/cmake") set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}) - set(AMCAX_DLL_PATH "AMCAX/bin/Debug") - set(FREEGLUT_DLL_PATH "third_party/FreeGLUT/x64-Debug/bin") + + set(AMCAX_DLL_PATH "AMCAX/bin/Debug/") + set(FREEGLUT_DLL_PATH "third_party/FreeGLUT/x64-Debug/bin/") + + # SARibbon + SET(SARIBBONBAR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SARibbon/lib/SARibbonBard.lib") + SET(QWKCORE_DLL "third_party/SARibbon/bin/QWKCored.dll") + SET(QWKWidgets_DLL "third_party/SARibbon/bin/QWKWidgetsd.dll") + SET(SARIBBONBAR_DLL "third_party/SARibbon/bin/SARibbonBard.dll") else() list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/AMCAX/lib/cmake") list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/third_party/FreeGLUT/x64-Release/lib/cmake") set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}) - set(AMCAX_DLL_PATH "AMCAX/bin/Release") - set(FREEGLUT_DLL_PATH "third_party/FreeGLUT/x64-Release/bin") + + set(AMCAX_DLL_PATH "AMCAX/bin/Release/") + set(FREEGLUT_DLL_PATH "third_party/FreeGLUT/x64-Release/bin/") + + # SARibbon + SET(SARIBBONBAR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/SARibbon/lib/SARibbonBar.lib") + SET(QWKCORE_DLL "third_party/SARibbon/bin/QWKCore.dll") + SET(QWKWidgets_DLL "third_party/SARibbon/bin/QWKWidgets.dll") + SET(SARIBBONBAR_DLL "third_party/SARibbon/bin/SARibbonBar.dll") endif() -message("CMAKE_PREFIX_PATH is now : ${CMAKE_PREFIX_PATH}") -#find depended libraries +include_directories("third_party/SARibbon/include/SARibbonBar") + +# find depended libraries find_package(AMCAXCommon REQUIRED) find_package(AMCAXPart REQUIRED) find_package(AMCAXStep REQUIRED) @@ -62,66 +80,25 @@ find_package(AMCAXTMSpline REQUIRED) find_package(AMCAXOCCTIO REQUIRED) find_package(FreeGLUT REQUIRED) - - +# find_package(SARibbonBar REQUIRED) qt5_add_resources(QRC_FILES Resources/Resource.qrc) - -FILE(GLOB Command_File "Src/Command/*.cpp" "Src/Command/*.h") - -FILE(GLOB Core_FILE "Src/Core/*.cpp" "Src/Core/*.h") - -FILE(GLOB Object_FILE "Src/Object/*.cpp" "Src/Object/*.h") - -FILE(GLOB ObjectDraw_FILE "Src/ObjectDraw/*.cpp" "Src/ObjectDraw/*.h") - -FILE(GLOB Operate_FILE "Src/Operation/*.cpp" "Src/Operation/*.h") - -FILE(GLOB Windows_FILE "Src/Windows/*.cpp" "Src/Windows/*.h") - -FILE(GLOB QGLViewer_FILE "Src/Windows/QGLViewer/*.cpp" "Src/Windows/QGLViewer/*.h") - -FILE(GLOB Record_FILE "Src/Record/*.h" "Src/Record/*.cpp") - -FILE(GLOB Utils_FILE "Src/Utils/*.h" "Src/Utils/*.cpp") - +FILE(GLOB_RECURSE SOURCE_FILE "Src/*.cpp" "Src/*.h") FILE(GLOB WIN32_RESOURCE_FILE "Resources/Version.rc") add_compile_definitions(NOMINMAX) add_compile_definitions(_USE_MATH_DEFINES) +add_executable(${PROJECT_NAME}) + +target_sources(${PROJECT_NAME} PRIVATE ${QRC_FILES} ${SOURCE_FILE} ${WIN32_RESOURCE_FILE}) -add_executable(${PROJECT_NAME} - ${QRC_FILES} - ${Command_FILE} - ${Core_FILE} - ${Object_FILE} - ${ObjectDraw_FILE} - ${Operate_FILE} - ${Windows_FILE} - ${QGLViewer_FILE} - ${Record_FILE} - ${Utils_FILE} - "Src/Main.cpp") - -source_group(Command FILES ${Command_FILE}) -source_group(Core FILES ${Core_FILE}) -source_group(Object FILES ${Object_FILE}) -source_group(ObjectDraw FILES ${ObjectDraw_FILE}) -source_group(Operate FILES ${Operate_FILE}) -source_group(Widgets FILES ${Windows_FILE}) -source_group(QGLViewer FILES ${QGLViewer_FILE}) -source_group(Record FILES ${Record_FILE}) -source_group(Utils FILES ${Utils_FILE}) - -target_sources(${PROJECT_NAME} PRIVATE ${WIN32_RESOURCE_FILE}) - - -target_link_libraries(${PROJECT_NAME} - Qt5::Widgets - Qt5::Core - Qt5::Gui +target_link_libraries(${PROJECT_NAME} + Qt5::Widgets + Qt5::Core + Qt5::Gui Qt5::OpenGL + Qt5::Sql opengl32 glu32 AMCAXCommon @@ -130,12 +107,14 @@ target_link_libraries(${PROJECT_NAME} AMCAXSubD AMCAXTMSpline FreeGLUT::freeglut + ${SARIBBONBAR} ) message("CMAKE_CURRENT_BINARY_DIR = ${CMAKE_CURRENT_BINARY_DIR}") -execute_process( COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}) +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}) -file(COPY ${AMCAX_DLL_PATH}/ ${FREEGLUT_DLL_PATH}/ - DESTINATION ${CMAKE_CURRENT_BINARY_DIR} +file(COPY ${AMCAX_DLL_PATH} ${FREEGLUT_DLL_PATH} ${QWKCORE_DLL} + ${QWKWidgets_DLL} ${SARIBBONBAR_DLL} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR} ) \ No newline at end of file diff --git a/README.md b/README.md index 9c4cb061c9111c9b85ee0527b012e3f5367d66aa..c506dfabde48f5627ab8722d00e850125e6e4611 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # AMCAX-Daemon Introduction -[![en-us](https://img.shields.io/badge/en-us-yellow.svg)](https://github.com/amcax-kernel/amcax-daemon/blob/main/README.md) -[![中文-简体](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-%E7%AE%80%E4%BD%93-red.svg)](https://github.com/amcax-kernel/amcax-daemon/blob/main/README.zh_cn.md) -**Version 1.0.2** +[![en-us](https://img.shields.io/badge/en-us-yellow.svg)](./README.md) + +**Version 1.0.3** AMCAX-Daemon showcases some basic functions that AMCAX geometry kernel supports. There are more functions that can be supported by the kernel but currently not used by Daemon. @@ -33,19 +33,7 @@ Please be informed that this project **does not** come with an AMCAX geometry ke Refer to figures below to locate the operation areas of AMCAX-Daemon -

- - base_tab - -

Figure 1 Base Tab

-

- -

- - brep_tab - -

Figure 2 BRep Tab

-

+![Refer to figures below to locate the operation areas of AMCAX-Daemon](./doc/pic/screenshot/screenshot.png) ### Create / Delete / Move Geometry Objects @@ -67,10 +55,12 @@ Refer to figures below to locate the operation areas of AMCAX-Daemon ### Persistent Geometry Objects -You may either select some objects and save them as file using `Export Selected` function, or save all the objects in your working space using `Save` function. -You can choose to either save the objects `*.abr` file, which is an AMCAX proprietary format; or save the objects as standard STEP file. -You can later load the objects from saved file into your working space. -All the related functions are availabe under the `File` menu. +![File Menu](./doc/pic/screenshot/screenshot-file.png) + +- **Save**: Save the object in `*.amcax` format, which is AMCAX's proprietary format and supports TMSpline, SubD, BRep. +- **Save As**: Save the object in the format`*.amcax`, `*.stp` `*step`. +- **Save Selected**: You can choose to save the object in the format `*.amcax`, `*.stp` `*step`. +- All of the above related features are located under the `File` menu. ### Auxillary Functions @@ -82,8 +72,6 @@ All the related functions are availabe under the `File` menu. - Undo/Redo - Currently only support undo/redo create/delete operation. New features are coming soon. - ## How to Build @@ -95,10 +83,9 @@ All the related functions are availabe under the `File` menu. - Extension: Qt VS Tool (Qt Versions Configured) - QT 5.15.2 - - Add qmake path (eg. D:\Qt\5.15.2\msvc2019_64\bin) in to PATH environment variable. - -- [AMCAX Kernal](https://amcax.net/) 3.6.0 + - Add qmake path (eg. D:\Qt\5.15.2\msvc2019_64\bin) in to `PATH` environment variable. +- [AMCAX Kernal](https://amcax.net/) 3.6.1 ### Steps @@ -149,7 +136,6 @@ The code structure for AMCAX daemon looks like below.

Figure 3 Code Structure

- It's built upon AMCAX Kernel SDK and OpenGL. From bottom to top: - [`Operation`](./Src/Operation) folder includes helper classes that can call kernel SDK to create the underlying geometry objects, [`ObjectDraw`](./Src/ObjectDraw) folder includes classes that are used to draw a target object using OpenGL; diff --git a/README.zh_cn.md b/README.zh_cn.md index 7d6363446d61bc22ac27479bedb519fc56f1ecc1..cf6710cd212c456abbcd133b727888863d786796 100644 --- a/README.zh_cn.md +++ b/README.zh_cn.md @@ -1,14 +1,13 @@ # 九韶精灵 简介 -[![en-us](https://img.shields.io/badge/en-us-yellow.svg)](https://github.com/amcax-kernel/amcax-daemon/blob/main/README.md) -[![中文-简体](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-%E7%AE%80%E4%BD%93-red.svg)](https://github.com/amcax-kernel/amcax-daemon/blob/main/README.zh_cn.md) +[![中文-简体](https://img.shields.io/badge/%E4%B8%AD%E6%96%87-%E7%AE%80%E4%BD%93-red.svg)](./README.zh_cn.md) -**版本 1.0.2** - -九韶精灵展示了九韶几何内核支持的一些基本功能。 内核支持更多更丰富的功能,但目前九韶精灵中还暂未使用。 请注意,此项目并不包含AMCAX几何内核的使用许可证,您需要到[九韶官网](https://amcax.net/)自行申请许可证。 +**版本 1.0.3** +九韶精灵展示了九韶几何内核支持的一些基本功能。 内核支持更多更丰富的功能,但目前九韶精灵中还暂未使用。 请注意,此项目并不包含 AMCAX 几何内核的使用许可证,您需要到[九韶官网](https://amcax.net/)自行申请许可证。 ## 目录 + - [如何使用](#如何使用) - [用户界面](#用户界面) - [创建 / 删除 / 移动几何对象](#创建-删除-移动几何对象) @@ -31,37 +30,22 @@ ### 用户界面 -请参考以下图片定位九韶精灵的各项操作区域 +- 九韶精灵的各项操作区域 -

- - base_tab - -

图一:Base 选项卡

-

- -

- - brep_tab - -

图二:BRep 选项卡

-

- - +![九韶精灵的各项操作区域](./doc/pic/screenshot/screenshot.png) ### 创建 / 删除 / 移动几何对象 - 要创建一个新的几何对象 - 激活 BRep 选项卡并选择对象类型,然后: - - 对于面类型:按顺序点击渲染区域,指定多个点,完成后按 `回车` + 激活 **BRep** 选项卡并选择对象类型,然后: + + - 对于面类型:按顺序点击渲染区域,指定多个点,完成后按 `回车` - - 对于其他类型:将会出现一个对话框,在对话框中指定几何对象的参数并点击 `确认` + - 对于其他类型:将会出现一个对话框,在对话框中指定几何对象的参数并点击 `确认` - 要删除对象,选择对象并按 `删除` 键。 - - 要移动对象,选择对象,按住鼠标左键,移动到目标点,然后释放按钮 - ### 修改几何对象 @@ -69,9 +53,13 @@ ### 持久化几何对象 -- 您可以选择一些对象,使用 `导出选中...`功能将它们保存为文件;也可使用 `保存` 功能来保存工作空间中的所有对象。 +![九韶精灵文件菜单](./doc/pic/screenshot/screenshot-file.png) + +- **保存**:将对象以 `*.amcax` 格式保存,这是 AMCAX 的专有格式,支持 TMSpline, SubD, BRep。 -- 可以选择将对象以 `*.abr` 格式保存,这是 AMCAX 的专有格式;也可以选择将对象以标准的STEP文件格式保存。您可以稍后将对象从文件加载到工作空间中。 +- **另存为**:将对象以 `*.amcax`,`*.stp` `*step` 格式保存。 + +- **保存选中**:可以选择将对象以 `*.amcax`,`*.stp` `*step` 格式保存。 - 上述所有相关功能都位于`文件`菜单下。 @@ -79,43 +67,37 @@ - 改变视角 - - 旋转:按住右键并移动以调整 + - 旋转:按住右键并移动以调整 - - 平移:按下左/右/上/下键调整 + - 平移:按下左/右/上/下键调整 - 撤销/重做 - - 目前仅支持撤销/重做创建/删除操作。新功能即将推出。 - + ## 如何构建 ### 前提条件 - CMake 3.27 + - - Visual Studio 2019 - - 扩展: Qt VS Tool (请配置Qt Versions) - + - 扩展: Qt VS Tool (请配置 Qt Versions) - QT 5.15.2 - - 添加 qmake 路径(例如:D:\Qt\5.15.2\msvc2019_64\bin)到 PATH 环境变量。 - -- [九韶几何内核](https://amcax.net/) 3.6.0 - + - 添加 qmake 路径(例如:D:\Qt\5.15.2\msvc2019_64\bin)到 `PATH` 环境变量。 +- [九韶几何内核](https://amcax.net/) 3.6.1 ### 步骤 - 设置环境变量 - **CMAKE\_PREFIX\_PATH=%YOUR\_QT\_INSTALL\_DIR%\\msvc2019%YOUR\_CPU\_ARCH%\\lib\\cmake** + **CMAKE_PREFIX_PATH=%YOUR_QT_INSTALL_DIR%\\msvc2019%YOUR_CPU_ARCH%\\lib\\cmake** - 例如: + 例如: - `d:\\qt\\5.15.2\\msvc2019\_64\\lib\\cmake\\` + `d:\\qt\\5.15.2\\msvc2019\_64\\lib\\cmake\\` - 添加九韶几何内核库 - 解压库并将其放在此项目根文件夹的相应位置。文件夹结构应如下所示: + 解压库并将其放在此项目根文件夹的相应位置。文件夹结构应如下所示: ```text Amcax-Daemon @@ -131,13 +113,9 @@ ``` - 打开 "Visual Studio 2019" 并选择 `continue without code`,导航到 `File` -> `Open` -> `CMake...` 然后打开此项目的 `CMakeLists.txt` 文件。 - - 构建 - - 将 AMCAX/bin 中的文件夹(`Debug` & `Release`)复制到项目的输出文件夹中。 - -- 运行 AmcaxDaemon.exe - +- 运行 AmcaxDaemon.exe ## 教程 @@ -152,21 +130,19 @@ AMCAX daemon 的代码结构如下。

图三: 代码结构

- - 它基于 AMCAX Kernel SDK 和 OpenGL。从底层到上层: -- [`Operation`](./Src/Operation/)文件夹中是一些辅助类,它们直接调用内核SDK的API来创建几何对象,[`ObjectDraw`](./Src/ObjectDraw/)目录下是利用OpenGL绘制目标对象的类; +- [`Operation`](./Src/Operation/)文件夹中是一些辅助类,它们直接调用内核 SDK 的 API 来创建几何对象,[`ObjectDraw`](./Src/ObjectDraw/)目录下是利用 OpenGL 绘制目标对象的类; - [`Object`](./Src/Object/)目录中提供了内核集合对象的包装类,这些包装类使用`Operation`和`ObjectDraw`目录中相应的类来支持对几何对象的操作和图形展示。 -- [`Core`](./Src/Core/)目录中包含了一个`DataManager`,用于管理所有创建好的几何对象,以及`UnRedoHandler`用于支持“撤销/重做”,而`ACAMFile`中则实现了几何对象的持久化。 +- [`Core`](./Src/Core/)目录中包含了一个`DataManager`,用于管理所有创建好的几何对象,以及`UndoManager`用于支持“撤销/重做”,而`ACAMFile`中则实现了几何对象的持久化。 - [`Windows`](./Src/Windows/)目录包含与用户交互相关的类,目前利用 libqglviewer 进行相机/投影相关的坐标轴变换。 - + ### 创建几何对象 -使用AMCAX内核创建对象非常简单: +使用 AMCAX 内核创建对象非常简单: ```cpp std::vector point_list; @@ -186,7 +162,7 @@ AMCAX daemon 的代码结构如下。 ### 修改几何对象 -可以在MultOperate_BoolFuse类中找到如何融合多个几何对象。代码非常简单: +可以在 MultOperate_BoolFuse 类中找到如何融合多个几何对象。代码非常简单: ```cpp std::vector shapeVector; @@ -202,7 +178,7 @@ AMCAX daemon 的代码结构如下。 ### Persistent Geometry Objects -基本思路是使用AMCAD::TopoCompound和AMCAD::TopoBuilder将您想要持久化的对象组合起来,然后将它们保存到abr文件中。代码片段如下: +基本思路是使用 AMCAD::TopoCompound 和 AMCAD::TopoBuilder 将您想要持久化的对象组合起来,然后将它们保存到 abr 文件中。代码片段如下: ```cpp std::string fileName = "foo.abr"; @@ -227,20 +203,18 @@ AMCAX daemon 的代码结构如下。 ## 已知问题 - 目前仅仅支持撤销/重做对几何对象的创建和删除操作,其他操作暂不支持。因此,如果您创建了一个对象,移动之,然后删除该对象。当您试图撤销删除操作时,您会发现被删除的几何对象虽然会重新出现,但不会在移动后的位置。 -- 不能混用Debug/Release的内核库。因此,如果您获取到的是Release版本的内核库,目前暂时只能编译运行Release版本的九韶精灵,但您可以使用最新的CMakeSettings配置,以达到调试九韶精灵本身的代码的目的。 -- 入STEP文件时,需要手动缩放视口大小使得STEP文件表示的对象合理显示。 +- 不能混用 Debug/Release 的内核库。因此,如果您获取到的是 Release 版本的内核库,目前暂时只能编译运行 Release 版本的九韶精灵,但您可以使用最新的 CMakeSettings 配置,以达到调试九韶精灵本身的代码的目的。 +- 入 STEP 文件时,需要手动缩放视口大小使得 STEP 文件表示的对象合理显示。 ## 许可证 -AMCAX-Daemon采用双重许可制度:开源软件开发按照GNU Affero通用公共许可证v3.0(AGPLv3.0)自由使用,而商业应用可以申请商业许可。 +AMCAX-Daemon 采用双重许可制度:开源软件开发按照 GNU Affero 通用公共许可证 v3.0(AGPLv3.0)自由使用,而商业应用可以申请商业许可。 ## 联系我们 欢迎提供反馈和贡献! -您可以在这里创建Git问题,或者加入我们的[中文社区](https://bbs.amcax.net/)以进一步参与这个项目。 - +您可以在这里创建 Git 问题,或者加入我们的[中文社区](https://bbs.amcax.net/)以进一步参与这个项目。 --- -**Copyright © 2023 Hefei Jiushao Intelligent Technology Co., Ltd. All rights reserved.** - +**Copyright © 2024 Hefei Jiushao Intelligent Technology Co., Ltd. All rights reserved.** diff --git a/Resources/Resource.qrc b/Resources/Resource.qrc index 84906ddef3d8934bc73cd8ad55d4d03bb74688b5..a25f391555cbfd8188ff704006676309f652eefe 100644 --- a/Resources/Resource.qrc +++ b/Resources/Resource.qrc @@ -1,55 +1,80 @@ - images/SelectEdge.png - images/SelectFace.png - images/SelectObject.png - images/SelectVert.png - images/Cube.png - images/Cylinder.png - images/copy.png - images/Plane.png - images/UCSphere.png - images/ExtrudeFace.png - images/SingalMeshFace.png - images/Cone.png - images/Combine.png - images/SetCenter.png - images/WorkPlane.png - images/Torus.png - images/Chamfer.png - images/fillet.png - images/Circular.png - images/BoolCommon.png - images/BoolCut.png - images/BoolFuse.png - images/Prism.png - images/Polygon.png images/logo.ico - images/SubdiveCC.png - images/SubdiveLoop.png - images/MeshTriangulation.png - images/DeleteObject.png - images/Mirror.png - images/EmbedFace.png - images/DeleteFace.png - images/Separate.png - images/Weld.png - images/SplitEdgeOnly.png - images/SplitLoop.png - images/SplitFace.png - images/CollapseEdge.png - images/SwapEdge.png - images/BridgeEdge.png - images/ExtrudeEdgeHorizontal.png - images/ExtrudeEdgeVertical.png - images/Thicken.png - images/FillHole.png - images/ReverseNormal.png - images/QuadSphere.png - images/Crease.png - images/RCrease.png - images/Repair.png - images/Uniform.png - images/SplitFace1.png + + + icon/redo.svg + icon/save.svg + icon/undo.svg + icon/exit.svg + icon/open.svg + icon/export-selected.svg + icon/copy-object.svg + icon/create_component.svg + icon/delete.svg + icon/face-select.svg + icon/fit-screen.svg + icon/line-select.svg + icon/mirror.svg + icon/move.svg + icon/object-select.svg + icon/render.svg + icon/rotate.svg + icon/scale.svg + icon/smooth.svg + icon/vertex-select.svg + icon/wireframe.svg + icon/workplane.svg + icon/about.svg + icon/save-small.svg + icon/new.svg + icon/save-as.svg + icon/open-recent.svg + + + TMSpline/add-crease-edge.svg + TMSpline/brige-edge.svg + TMSpline/chamfer.svg + TMSpline/circular-arc.svg + TMSpline/combine-vertex.svg + TMSpline/cone.svg + TMSpline/cube.svg + TMSpline/cut-face.svg + TMSpline/cylinder.svg + TMSpline/delete-face.svg + TMSpline/embed-face.svg + TMSpline/extrude-edge-h.svg + TMSpline/extrude-edge-v.svg + TMSpline/extrude-face.svg + TMSpline/fill-hole.svg + TMSpline/make-uniform.svg + TMSpline/plane.svg + TMSpline/quad-sphere.svg + TMSpline/remove-crease-edge.svg + TMSpline/repair.svg + TMSpline/reverse-normal.svg + TMSpline/separate.svg + TMSpline/single-face.svg + TMSpline/sphere.svg + TMSpline/split-face.svg + TMSpline/split-face-cross.svg + TMSpline/split-loop.svg + TMSpline/swap-edge.svg + TMSpline/thicken.svg + TMSpline/torus.svg + TMSpline/weld.svg + TMSpline/subd-catmull-clark.svg + TMSpline/subd-loop.svg + TMSpline/triangulated-mesh.svg + TMSpline/polygon.svg + TMSpline/prism.svg + TMSpline/fillet.svg + TMSpline/union.svg + TMSpline/union-1.svg + TMSpline/union-2.svg + TMSpline/curvature.svg + TMSpline/curvature-comb.svg + TMSpline/zebra.svg + TMSpline/copy-face.svg diff --git a/Resources/TMSpline/add-crease-edge.svg b/Resources/TMSpline/add-crease-edge.svg new file mode 100644 index 0000000000000000000000000000000000000000..14cd5562104d3228c8e9f11e1b84a892c12d5ca8 --- /dev/null +++ b/Resources/TMSpline/add-crease-edge.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/TMSpline/brige-edge.svg b/Resources/TMSpline/brige-edge.svg new file mode 100644 index 0000000000000000000000000000000000000000..acdab5d0d1bc3f8063481f0cfc74884c2f76f243 --- /dev/null +++ b/Resources/TMSpline/brige-edge.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/chamfer.svg b/Resources/TMSpline/chamfer.svg new file mode 100644 index 0000000000000000000000000000000000000000..9588750d07c733f0e55b93018305d145698f89ef --- /dev/null +++ b/Resources/TMSpline/chamfer.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/circular-arc.svg b/Resources/TMSpline/circular-arc.svg new file mode 100644 index 0000000000000000000000000000000000000000..a857a7a1bd24b22a88ab60e5ea1c1f08b53dccb7 --- /dev/null +++ b/Resources/TMSpline/circular-arc.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Resources/TMSpline/combine-vertex.svg b/Resources/TMSpline/combine-vertex.svg new file mode 100644 index 0000000000000000000000000000000000000000..65b5a2866290849cc5ca8ff5e17ffb7eb1c9c2fc --- /dev/null +++ b/Resources/TMSpline/combine-vertex.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Resources/TMSpline/cone.svg b/Resources/TMSpline/cone.svg new file mode 100644 index 0000000000000000000000000000000000000000..175677ea684811fe22e66495dd758f06d48e6542 --- /dev/null +++ b/Resources/TMSpline/cone.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Resources/TMSpline/copy-face.svg b/Resources/TMSpline/copy-face.svg new file mode 100644 index 0000000000000000000000000000000000000000..880615f6aaec12bef2ba31ca83b9b3044d937cfc --- /dev/null +++ b/Resources/TMSpline/copy-face.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/cube.svg b/Resources/TMSpline/cube.svg new file mode 100644 index 0000000000000000000000000000000000000000..1c66d863ccf31779c6b0d740b32bf9bd37bf0c81 --- /dev/null +++ b/Resources/TMSpline/cube.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/TMSpline/curvature-comb.svg b/Resources/TMSpline/curvature-comb.svg new file mode 100644 index 0000000000000000000000000000000000000000..85c692243b48f82a77227ef5868b8d6aaffaeeb7 --- /dev/null +++ b/Resources/TMSpline/curvature-comb.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Resources/TMSpline/curvature.svg b/Resources/TMSpline/curvature.svg new file mode 100644 index 0000000000000000000000000000000000000000..93f3ad48640bd2a43f916e00726b81c3b0e9268a --- /dev/null +++ b/Resources/TMSpline/curvature.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Resources/TMSpline/cut-face.svg b/Resources/TMSpline/cut-face.svg new file mode 100644 index 0000000000000000000000000000000000000000..548dab5bb3eff5c2e84babe9dc19c67ae60e0c18 --- /dev/null +++ b/Resources/TMSpline/cut-face.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Resources/TMSpline/cylinder.svg b/Resources/TMSpline/cylinder.svg new file mode 100644 index 0000000000000000000000000000000000000000..6e8c170f2f75c0af9f3dec64d976916678682d86 --- /dev/null +++ b/Resources/TMSpline/cylinder.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Resources/TMSpline/delete-face.svg b/Resources/TMSpline/delete-face.svg new file mode 100644 index 0000000000000000000000000000000000000000..35c6d17cb91b37fe8d861ae397315204dd539d11 --- /dev/null +++ b/Resources/TMSpline/delete-face.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Resources/TMSpline/embed-face.svg b/Resources/TMSpline/embed-face.svg new file mode 100644 index 0000000000000000000000000000000000000000..3cb765d8de156f88add9ac66c255ef32eb0c7c67 --- /dev/null +++ b/Resources/TMSpline/embed-face.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Resources/TMSpline/extrude-edge-h.svg b/Resources/TMSpline/extrude-edge-h.svg new file mode 100644 index 0000000000000000000000000000000000000000..13be2c0ce7c25a55d00e7f08b2d281211b28cde4 --- /dev/null +++ b/Resources/TMSpline/extrude-edge-h.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Resources/TMSpline/extrude-edge-v.svg b/Resources/TMSpline/extrude-edge-v.svg new file mode 100644 index 0000000000000000000000000000000000000000..e5a0275d5e0f6e35232d22ade31402e1ba9bb4a9 --- /dev/null +++ b/Resources/TMSpline/extrude-edge-v.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Resources/TMSpline/extrude-face.svg b/Resources/TMSpline/extrude-face.svg new file mode 100644 index 0000000000000000000000000000000000000000..e7f848c5802ad889341055a990c46bfe375244a9 --- /dev/null +++ b/Resources/TMSpline/extrude-face.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/fill-hole.svg b/Resources/TMSpline/fill-hole.svg new file mode 100644 index 0000000000000000000000000000000000000000..9af2e4b30a05b02659dae4d715bf58f16e69e580 --- /dev/null +++ b/Resources/TMSpline/fill-hole.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/fillet.svg b/Resources/TMSpline/fillet.svg new file mode 100644 index 0000000000000000000000000000000000000000..d614087eab729bd7ab1da6f95baa030580cff76a --- /dev/null +++ b/Resources/TMSpline/fillet.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/make-uniform.svg b/Resources/TMSpline/make-uniform.svg new file mode 100644 index 0000000000000000000000000000000000000000..3b3acd4b0c5e8e4b9a4949c1f5330d3f0caf3ea3 --- /dev/null +++ b/Resources/TMSpline/make-uniform.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Resources/TMSpline/plane.svg b/Resources/TMSpline/plane.svg new file mode 100644 index 0000000000000000000000000000000000000000..3fd26c6bbff3ac1e5c6c6e2b67f1bf47dd716e51 --- /dev/null +++ b/Resources/TMSpline/plane.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Resources/TMSpline/polygon.svg b/Resources/TMSpline/polygon.svg new file mode 100644 index 0000000000000000000000000000000000000000..8bb21e721e775f385021a9adac6df04a0173b76d --- /dev/null +++ b/Resources/TMSpline/polygon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Resources/TMSpline/prism.svg b/Resources/TMSpline/prism.svg new file mode 100644 index 0000000000000000000000000000000000000000..58b6e8bfb54a439a345f1ff87fb56479931f6634 --- /dev/null +++ b/Resources/TMSpline/prism.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Resources/TMSpline/quad-sphere.svg b/Resources/TMSpline/quad-sphere.svg new file mode 100644 index 0000000000000000000000000000000000000000..754ef999382a3a079a5d416d75982d29f673b57c --- /dev/null +++ b/Resources/TMSpline/quad-sphere.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/TMSpline/remove-crease-edge.svg b/Resources/TMSpline/remove-crease-edge.svg new file mode 100644 index 0000000000000000000000000000000000000000..8e089c248fbf235a0f09f06ee41bf23db087d50e --- /dev/null +++ b/Resources/TMSpline/remove-crease-edge.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/TMSpline/repair.svg b/Resources/TMSpline/repair.svg new file mode 100644 index 0000000000000000000000000000000000000000..e854a9c5cdd351633502393fff816770f0a316eb --- /dev/null +++ b/Resources/TMSpline/repair.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Resources/TMSpline/reverse-normal.svg b/Resources/TMSpline/reverse-normal.svg new file mode 100644 index 0000000000000000000000000000000000000000..e12c6e8ca198a6500441e0689ae6e41e7559dd7d --- /dev/null +++ b/Resources/TMSpline/reverse-normal.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/TMSpline/separate.svg b/Resources/TMSpline/separate.svg new file mode 100644 index 0000000000000000000000000000000000000000..1040647fbc0f26d6fb9c05e30b64b46987472e93 --- /dev/null +++ b/Resources/TMSpline/separate.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/single-face.svg b/Resources/TMSpline/single-face.svg new file mode 100644 index 0000000000000000000000000000000000000000..19c8f8b329c186ada1380f1789525fa60ce60d0c --- /dev/null +++ b/Resources/TMSpline/single-face.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Resources/TMSpline/sphere.svg b/Resources/TMSpline/sphere.svg new file mode 100644 index 0000000000000000000000000000000000000000..14290b77803198b4b95155bff70531921c285f99 --- /dev/null +++ b/Resources/TMSpline/sphere.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Resources/TMSpline/split-face-cross.svg b/Resources/TMSpline/split-face-cross.svg new file mode 100644 index 0000000000000000000000000000000000000000..55708f798b22c8c8c2b3aa398ffeb8d636afd750 --- /dev/null +++ b/Resources/TMSpline/split-face-cross.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/TMSpline/split-face.svg b/Resources/TMSpline/split-face.svg new file mode 100644 index 0000000000000000000000000000000000000000..c2087c8b05cf7dfab8b36eaf0044f6895672647c --- /dev/null +++ b/Resources/TMSpline/split-face.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Resources/TMSpline/split-loop.svg b/Resources/TMSpline/split-loop.svg new file mode 100644 index 0000000000000000000000000000000000000000..d37972378f71026c03b17c4fc69169bee22f53da --- /dev/null +++ b/Resources/TMSpline/split-loop.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/subd-catmull-clark.svg b/Resources/TMSpline/subd-catmull-clark.svg new file mode 100644 index 0000000000000000000000000000000000000000..ca518bf41132b7a471ea2515fabbd831ba839983 --- /dev/null +++ b/Resources/TMSpline/subd-catmull-clark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Resources/TMSpline/subd-loop.svg b/Resources/TMSpline/subd-loop.svg new file mode 100644 index 0000000000000000000000000000000000000000..883cac664cd3ed35237d59bb4eb0427e2be9a16c --- /dev/null +++ b/Resources/TMSpline/subd-loop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Resources/TMSpline/swap-edge.svg b/Resources/TMSpline/swap-edge.svg new file mode 100644 index 0000000000000000000000000000000000000000..63bdec4d67cb250172d3ee9523f7adcf3c7a4b79 --- /dev/null +++ b/Resources/TMSpline/swap-edge.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/TMSpline/thicken.svg b/Resources/TMSpline/thicken.svg new file mode 100644 index 0000000000000000000000000000000000000000..2aa3efd3894f78cc455316cddba5722f760f9a12 --- /dev/null +++ b/Resources/TMSpline/thicken.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/torus.svg b/Resources/TMSpline/torus.svg new file mode 100644 index 0000000000000000000000000000000000000000..8a5302ae7787bdf687ee406e6aa121782500dacc --- /dev/null +++ b/Resources/TMSpline/torus.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/TMSpline/triangulated-mesh.svg b/Resources/TMSpline/triangulated-mesh.svg new file mode 100644 index 0000000000000000000000000000000000000000..ae8dee8d6450f82d842817857bed88696d5f49f4 --- /dev/null +++ b/Resources/TMSpline/triangulated-mesh.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/TMSpline/union-1.svg b/Resources/TMSpline/union-1.svg new file mode 100644 index 0000000000000000000000000000000000000000..dfead6a350b2952b29ff00413cace2fd120ce75d --- /dev/null +++ b/Resources/TMSpline/union-1.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Resources/TMSpline/union-2.svg b/Resources/TMSpline/union-2.svg new file mode 100644 index 0000000000000000000000000000000000000000..77f520d7497c91686791e5d74c3e6d3c58c87886 --- /dev/null +++ b/Resources/TMSpline/union-2.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Resources/TMSpline/union.svg b/Resources/TMSpline/union.svg new file mode 100644 index 0000000000000000000000000000000000000000..8eb1d5015cc2f6cc9ec7feecb086248e352b1a62 --- /dev/null +++ b/Resources/TMSpline/union.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Resources/TMSpline/weld.svg b/Resources/TMSpline/weld.svg new file mode 100644 index 0000000000000000000000000000000000000000..f7960ff1e1522a5449e4c6e4ce8724deb6aee6ca --- /dev/null +++ b/Resources/TMSpline/weld.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/TMSpline/zebra.svg b/Resources/TMSpline/zebra.svg new file mode 100644 index 0000000000000000000000000000000000000000..aa7bec809fd7962ad47e299a0d1a8a9e3561c145 --- /dev/null +++ b/Resources/TMSpline/zebra.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Resources/Version.rc b/Resources/Version.rc new file mode 100644 index 0000000000000000000000000000000000000000..8f92c4574c7a4da2a2bdf1d29d3e116e6c9fd569 --- /dev/null +++ b/Resources/Version.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "images/logo.ico" \ No newline at end of file diff --git a/Resources/icon/about.svg b/Resources/icon/about.svg new file mode 100644 index 0000000000000000000000000000000000000000..17e96b316da3455f12b3bed3cbc854332e69d87d --- /dev/null +++ b/Resources/icon/about.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Resources/icon/copy-object.svg b/Resources/icon/copy-object.svg new file mode 100644 index 0000000000000000000000000000000000000000..823b34b04aa2e21838712bc1fe96362ee44c5689 --- /dev/null +++ b/Resources/icon/copy-object.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/icon/create_component.svg b/Resources/icon/create_component.svg new file mode 100644 index 0000000000000000000000000000000000000000..93eb588ae9620e0b8d882d6c7c13a9659447e347 --- /dev/null +++ b/Resources/icon/create_component.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/icon/delete.svg b/Resources/icon/delete.svg new file mode 100644 index 0000000000000000000000000000000000000000..25b8ef5e2b970071b6c36cfc74bd7d04fbed1aeb --- /dev/null +++ b/Resources/icon/delete.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/icon/exit.svg b/Resources/icon/exit.svg new file mode 100644 index 0000000000000000000000000000000000000000..5c311cdbd53de0d9e6a15b10421588ee3355ca54 --- /dev/null +++ b/Resources/icon/exit.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/icon/export-selected.svg b/Resources/icon/export-selected.svg new file mode 100644 index 0000000000000000000000000000000000000000..9485fc2ff5a25124339d72610f44b80026f66e39 --- /dev/null +++ b/Resources/icon/export-selected.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Resources/icon/face-select.svg b/Resources/icon/face-select.svg new file mode 100644 index 0000000000000000000000000000000000000000..02b931c081a5ba5d4e1dafc12ed9d2dbd2ffa10a --- /dev/null +++ b/Resources/icon/face-select.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Resources/icon/fit-screen.svg b/Resources/icon/fit-screen.svg new file mode 100644 index 0000000000000000000000000000000000000000..095bf4cfa08acf16b8ad8612b0b43cfda7113822 --- /dev/null +++ b/Resources/icon/fit-screen.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Resources/icon/line-select.svg b/Resources/icon/line-select.svg new file mode 100644 index 0000000000000000000000000000000000000000..dc57e557f54af52a840fd2356fcbfd90a3d4b01a --- /dev/null +++ b/Resources/icon/line-select.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Resources/icon/mirror.svg b/Resources/icon/mirror.svg new file mode 100644 index 0000000000000000000000000000000000000000..3191b52684cff2e51cd6c064bda8eaf00a86d338 --- /dev/null +++ b/Resources/icon/mirror.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/icon/move.svg b/Resources/icon/move.svg new file mode 100644 index 0000000000000000000000000000000000000000..7c0a7f9d5be154465a11b3915934b8ea25ea4d6c --- /dev/null +++ b/Resources/icon/move.svg @@ -0,0 +1,3 @@ + + + diff --git a/Resources/icon/new.svg b/Resources/icon/new.svg new file mode 100644 index 0000000000000000000000000000000000000000..8f2ea1566a49423961e1cb8b6330d74ffc42dda7 --- /dev/null +++ b/Resources/icon/new.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Resources/icon/object-select.svg b/Resources/icon/object-select.svg new file mode 100644 index 0000000000000000000000000000000000000000..442f9a2afbe9c986a906e1300d99e407dadbc633 --- /dev/null +++ b/Resources/icon/object-select.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Resources/icon/open-recent.svg b/Resources/icon/open-recent.svg new file mode 100644 index 0000000000000000000000000000000000000000..cc767ba350800127654390edc15672a139515643 --- /dev/null +++ b/Resources/icon/open-recent.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Resources/icon/open.svg b/Resources/icon/open.svg new file mode 100644 index 0000000000000000000000000000000000000000..fc7b5a11d2238bc2787cc09bbd7f6fa8ae8bcf28 --- /dev/null +++ b/Resources/icon/open.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Resources/icon/redo.svg b/Resources/icon/redo.svg new file mode 100644 index 0000000000000000000000000000000000000000..85770dd1f4b2efc24edc4edaacb40e5623482e56 --- /dev/null +++ b/Resources/icon/redo.svg @@ -0,0 +1,3 @@ + + + diff --git a/Resources/icon/render.svg b/Resources/icon/render.svg new file mode 100644 index 0000000000000000000000000000000000000000..c1d9e1237fe296711f09b87fe0a22b774ee2698d --- /dev/null +++ b/Resources/icon/render.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Resources/icon/rotate.svg b/Resources/icon/rotate.svg new file mode 100644 index 0000000000000000000000000000000000000000..a80774171d207f7b0ebd7e62c2cdc9148bb5caef --- /dev/null +++ b/Resources/icon/rotate.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Resources/icon/save-as.svg b/Resources/icon/save-as.svg new file mode 100644 index 0000000000000000000000000000000000000000..2526162197f4d231c9c92dd0d751afd0cfa4b0bb --- /dev/null +++ b/Resources/icon/save-as.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Resources/icon/save-small.svg b/Resources/icon/save-small.svg new file mode 100644 index 0000000000000000000000000000000000000000..42c288cd42d79f6ae1e4c7e6f824309c06db79c9 --- /dev/null +++ b/Resources/icon/save-small.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Resources/icon/save.svg b/Resources/icon/save.svg new file mode 100644 index 0000000000000000000000000000000000000000..d46547f57629e631938899778c5aa890a2ed8988 --- /dev/null +++ b/Resources/icon/save.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Resources/icon/scale.svg b/Resources/icon/scale.svg new file mode 100644 index 0000000000000000000000000000000000000000..c8a5dab0afa6043adf808dc565b5b26ed5b27096 --- /dev/null +++ b/Resources/icon/scale.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Resources/icon/smooth.svg b/Resources/icon/smooth.svg new file mode 100644 index 0000000000000000000000000000000000000000..d387eeefdb2d95d7fba9bd67dacd0bfa6ea098f6 --- /dev/null +++ b/Resources/icon/smooth.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Resources/icon/undo.svg b/Resources/icon/undo.svg new file mode 100644 index 0000000000000000000000000000000000000000..ec795058c72afa0fdce5732ec4a628f76ca3f611 --- /dev/null +++ b/Resources/icon/undo.svg @@ -0,0 +1,3 @@ + + + diff --git a/Resources/icon/vertex-select.svg b/Resources/icon/vertex-select.svg new file mode 100644 index 0000000000000000000000000000000000000000..4e9b293abc6f5e43a91134aed8037bb845736858 --- /dev/null +++ b/Resources/icon/vertex-select.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Resources/icon/wireframe.svg b/Resources/icon/wireframe.svg new file mode 100644 index 0000000000000000000000000000000000000000..d9f379cc1e8e3b86399ea489049732600e0826de --- /dev/null +++ b/Resources/icon/wireframe.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Resources/icon/workplane.svg b/Resources/icon/workplane.svg new file mode 100644 index 0000000000000000000000000000000000000000..92158fc5edbbd58ec5c08cd09bc0c8a16143dd04 --- /dev/null +++ b/Resources/icon/workplane.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Resources/images/BoolFuse.png b/Resources/images/BoolFuse.png deleted file mode 100644 index c1d10c5150cedfee7954ef4990686fa975033dfa..0000000000000000000000000000000000000000 Binary files a/Resources/images/BoolFuse.png and /dev/null differ diff --git a/Resources/images/BridgeEdge.png b/Resources/images/BridgeEdge.png deleted file mode 100644 index 1e9100cfe286537390476fcbc5a3c4eb183c82e7..0000000000000000000000000000000000000000 Binary files a/Resources/images/BridgeEdge.png and /dev/null differ diff --git a/Resources/images/Chamfer.png b/Resources/images/Chamfer.png deleted file mode 100644 index f6781af0123df29d90dcc140e88a9f5e547d3a8e..0000000000000000000000000000000000000000 Binary files a/Resources/images/Chamfer.png and /dev/null differ diff --git a/Resources/images/Circular.png b/Resources/images/Circular.png deleted file mode 100644 index 9ac6c5bf4edc8bd6f054be53052077ebf2dd9b75..0000000000000000000000000000000000000000 Binary files a/Resources/images/Circular.png and /dev/null differ diff --git a/Resources/images/CollapseEdge.png b/Resources/images/CollapseEdge.png deleted file mode 100644 index f370678788ed330a0fcbed36bf0b504da1a62084..0000000000000000000000000000000000000000 Binary files a/Resources/images/CollapseEdge.png and /dev/null differ diff --git a/Resources/images/Combine.png b/Resources/images/Combine.png deleted file mode 100644 index 19f51728765cc5f7e364f7c2ced0996f8bf5482b..0000000000000000000000000000000000000000 Binary files a/Resources/images/Combine.png and /dev/null differ diff --git a/Resources/images/Cone.png b/Resources/images/Cone.png deleted file mode 100644 index 0f68590ee4fe440a0cae4efcdaa9a7350acefd2b..0000000000000000000000000000000000000000 Binary files a/Resources/images/Cone.png and /dev/null differ diff --git a/Resources/images/Crease.png b/Resources/images/Crease.png deleted file mode 100644 index 18f2ef297f3fddb7121426b44dc7d9027ded3f10..0000000000000000000000000000000000000000 Binary files a/Resources/images/Crease.png and /dev/null differ diff --git a/Resources/images/Cube.png b/Resources/images/Cube.png deleted file mode 100644 index 42e5e1493dc572a6277b50b4b8576c4cc752421d..0000000000000000000000000000000000000000 Binary files a/Resources/images/Cube.png and /dev/null differ diff --git a/Resources/images/Cylinder.png b/Resources/images/Cylinder.png deleted file mode 100644 index af99cedfa6373f9c5ad86aa2f2f39af1853a7e63..0000000000000000000000000000000000000000 Binary files a/Resources/images/Cylinder.png and /dev/null differ diff --git a/Resources/images/DeleteFace.png b/Resources/images/DeleteFace.png deleted file mode 100644 index 8e3ed2c2548c7dd0fd8a42fbd518df1a63408814..0000000000000000000000000000000000000000 Binary files a/Resources/images/DeleteFace.png and /dev/null differ diff --git a/Resources/images/DeleteObject.png b/Resources/images/DeleteObject.png deleted file mode 100644 index e3646da4fdf40b255120dab3346dc9e4ff835fd5..0000000000000000000000000000000000000000 Binary files a/Resources/images/DeleteObject.png and /dev/null differ diff --git a/Resources/images/EmbedFace.png b/Resources/images/EmbedFace.png deleted file mode 100644 index 4c55b71cdeb24d885d58c4b15fdeffa0b3c4db53..0000000000000000000000000000000000000000 Binary files a/Resources/images/EmbedFace.png and /dev/null differ diff --git a/Resources/images/ExtrudeEdgeHorizontal.png b/Resources/images/ExtrudeEdgeHorizontal.png deleted file mode 100644 index 913b1d903f1af255eacfacb95c8d4cba6408e05e..0000000000000000000000000000000000000000 Binary files a/Resources/images/ExtrudeEdgeHorizontal.png and /dev/null differ diff --git a/Resources/images/ExtrudeEdgeVertical.png b/Resources/images/ExtrudeEdgeVertical.png deleted file mode 100644 index 6bfeacc51f16b36b5dc778f8aade8f98b603ce04..0000000000000000000000000000000000000000 Binary files a/Resources/images/ExtrudeEdgeVertical.png and /dev/null differ diff --git a/Resources/images/ExtrudeFace.png b/Resources/images/ExtrudeFace.png deleted file mode 100644 index c2446003659cbf546debe27f2aba97f6018ff14d..0000000000000000000000000000000000000000 Binary files a/Resources/images/ExtrudeFace.png and /dev/null differ diff --git a/Resources/images/FillHole.png b/Resources/images/FillHole.png deleted file mode 100644 index 935d43c2b8609acea95267373b819889a7aed52c..0000000000000000000000000000000000000000 Binary files a/Resources/images/FillHole.png and /dev/null differ diff --git a/Resources/images/MeshTriangulation.png b/Resources/images/MeshTriangulation.png deleted file mode 100644 index 718ec7696139946031420893049b3d105f689c8d..0000000000000000000000000000000000000000 Binary files a/Resources/images/MeshTriangulation.png and /dev/null differ diff --git a/Resources/images/Mirror.png b/Resources/images/Mirror.png deleted file mode 100644 index 78e2774e3b2023ecedfaf425fe9f90c7ffc0aa2b..0000000000000000000000000000000000000000 Binary files a/Resources/images/Mirror.png and /dev/null differ diff --git a/Resources/images/Plane.png b/Resources/images/Plane.png deleted file mode 100644 index 914b0bb9da89d3d0860b9593392e26ac02d950af..0000000000000000000000000000000000000000 Binary files a/Resources/images/Plane.png and /dev/null differ diff --git a/Resources/images/Polygon.png b/Resources/images/Polygon.png deleted file mode 100644 index 6a14349831d9b02caac562ac03662fbcfa6a683a..0000000000000000000000000000000000000000 Binary files a/Resources/images/Polygon.png and /dev/null differ diff --git a/Resources/images/Prism.png b/Resources/images/Prism.png deleted file mode 100644 index 9e273082fa8c52e715e8070ee50401ca6b75f997..0000000000000000000000000000000000000000 Binary files a/Resources/images/Prism.png and /dev/null differ diff --git a/Resources/images/QuadSphere.png b/Resources/images/QuadSphere.png deleted file mode 100644 index df59616b30da517f319a2ef2b96660c07c843704..0000000000000000000000000000000000000000 Binary files a/Resources/images/QuadSphere.png and /dev/null differ diff --git a/Resources/images/RCrease.png b/Resources/images/RCrease.png deleted file mode 100644 index 8d61a01245750bb288c602e15186f0975c596a96..0000000000000000000000000000000000000000 Binary files a/Resources/images/RCrease.png and /dev/null differ diff --git a/Resources/images/Repair.png b/Resources/images/Repair.png deleted file mode 100644 index ec3dd5f5019140dbbfb9409db8a241652b85e9f8..0000000000000000000000000000000000000000 Binary files a/Resources/images/Repair.png and /dev/null differ diff --git a/Resources/images/ReverseNormal.png b/Resources/images/ReverseNormal.png deleted file mode 100644 index 9305bb25d682b90183438a4be2b1a97623f04875..0000000000000000000000000000000000000000 Binary files a/Resources/images/ReverseNormal.png and /dev/null differ diff --git a/Resources/images/SelectEdge.png b/Resources/images/SelectEdge.png deleted file mode 100644 index a137a259f6f4cde953500a90f523726406ddad54..0000000000000000000000000000000000000000 Binary files a/Resources/images/SelectEdge.png and /dev/null differ diff --git a/Resources/images/SelectFace.png b/Resources/images/SelectFace.png deleted file mode 100644 index da86ace1c067bc08dcf7c87ffb0b04b1d1876f13..0000000000000000000000000000000000000000 Binary files a/Resources/images/SelectFace.png and /dev/null differ diff --git a/Resources/images/SelectObject.png b/Resources/images/SelectObject.png deleted file mode 100644 index 01f8674bb61fa20dc42a0ae8ce36957e3c7dd599..0000000000000000000000000000000000000000 Binary files a/Resources/images/SelectObject.png and /dev/null differ diff --git a/Resources/images/SelectVert.png b/Resources/images/SelectVert.png deleted file mode 100644 index 2ccae73a877bcc4956f5735fbc447285f0044a70..0000000000000000000000000000000000000000 Binary files a/Resources/images/SelectVert.png and /dev/null differ diff --git a/Resources/images/Separate.png b/Resources/images/Separate.png deleted file mode 100644 index 8b108086d7cc0f031ceca0f5b70085e6965ac3a6..0000000000000000000000000000000000000000 Binary files a/Resources/images/Separate.png and /dev/null differ diff --git a/Resources/images/SetCenter.png b/Resources/images/SetCenter.png deleted file mode 100644 index c58368cd63a8ca87a8c0c36386d97f31f61a0b65..0000000000000000000000000000000000000000 Binary files a/Resources/images/SetCenter.png and /dev/null differ diff --git a/Resources/images/SingalMeshFace.png b/Resources/images/SingalMeshFace.png deleted file mode 100644 index 08b3709558fae37d6ac57182ed76b512973bd5f9..0000000000000000000000000000000000000000 Binary files a/Resources/images/SingalMeshFace.png and /dev/null differ diff --git a/Resources/images/SplitEdgeOnly.png b/Resources/images/SplitEdgeOnly.png deleted file mode 100644 index bc1c24eaf23b95b3f00542449e74e39d7a8c9a5f..0000000000000000000000000000000000000000 Binary files a/Resources/images/SplitEdgeOnly.png and /dev/null differ diff --git a/Resources/images/SplitFace.png b/Resources/images/SplitFace.png deleted file mode 100644 index 51ecdc0e2bed9e3b6037ad9a1ac7abd4573abd83..0000000000000000000000000000000000000000 Binary files a/Resources/images/SplitFace.png and /dev/null differ diff --git a/Resources/images/SplitFace1.png b/Resources/images/SplitFace1.png deleted file mode 100644 index c837360b877b656b96d6061451f859d6ffe49f43..0000000000000000000000000000000000000000 Binary files a/Resources/images/SplitFace1.png and /dev/null differ diff --git a/Resources/images/SplitLoop.png b/Resources/images/SplitLoop.png deleted file mode 100644 index 8a2bc688c7fc31c05b9d0fb92e1e3564fb24e28a..0000000000000000000000000000000000000000 Binary files a/Resources/images/SplitLoop.png and /dev/null differ diff --git a/Resources/images/SubdiveCC.png b/Resources/images/SubdiveCC.png deleted file mode 100644 index 29a1d7ae68e133ca70815e5fbf5ba664a629480f..0000000000000000000000000000000000000000 Binary files a/Resources/images/SubdiveCC.png and /dev/null differ diff --git a/Resources/images/SubdiveLoop.png b/Resources/images/SubdiveLoop.png deleted file mode 100644 index 840fe7ce4612574d4f7000da3944042c0388b719..0000000000000000000000000000000000000000 Binary files a/Resources/images/SubdiveLoop.png and /dev/null differ diff --git a/Resources/images/SwapEdge.png b/Resources/images/SwapEdge.png deleted file mode 100644 index 08f925c86237386cdf06955cb3f8e2ffcb800138..0000000000000000000000000000000000000000 Binary files a/Resources/images/SwapEdge.png and /dev/null differ diff --git a/Resources/images/Thicken.png b/Resources/images/Thicken.png deleted file mode 100644 index add5299be52a122f2058cf799d93a7c5f6573430..0000000000000000000000000000000000000000 Binary files a/Resources/images/Thicken.png and /dev/null differ diff --git a/Resources/images/Torus.png b/Resources/images/Torus.png deleted file mode 100644 index bb28626a3148b25dd8096d6d696e2577596ebca4..0000000000000000000000000000000000000000 Binary files a/Resources/images/Torus.png and /dev/null differ diff --git a/Resources/images/UCSphere.png b/Resources/images/UCSphere.png deleted file mode 100644 index 972d7a9aab1c0774904a54df3c27ddb3fecdb139..0000000000000000000000000000000000000000 Binary files a/Resources/images/UCSphere.png and /dev/null differ diff --git a/Resources/images/Uniform.png b/Resources/images/Uniform.png deleted file mode 100644 index e9f784611a02493529d7d7e09da4357ff4f450fe..0000000000000000000000000000000000000000 Binary files a/Resources/images/Uniform.png and /dev/null differ diff --git a/Resources/images/Weld.png b/Resources/images/Weld.png deleted file mode 100644 index c81013e161a37519118a81484a5a1c8deaaab325..0000000000000000000000000000000000000000 Binary files a/Resources/images/Weld.png and /dev/null differ diff --git a/Resources/images/WorkPlane.png b/Resources/images/WorkPlane.png deleted file mode 100644 index ea00602cd4c5e6916dc727c39683158bfac7b326..0000000000000000000000000000000000000000 Binary files a/Resources/images/WorkPlane.png and /dev/null differ diff --git a/Resources/images/boolCommon.png b/Resources/images/boolCommon.png deleted file mode 100644 index b5a883a0e0ab2052dc152fd68d417903bfc252fe..0000000000000000000000000000000000000000 Binary files a/Resources/images/boolCommon.png and /dev/null differ diff --git a/Resources/images/boolCut.png b/Resources/images/boolCut.png deleted file mode 100644 index b53a7c4a88247a1bfd09973b893cbbdba97dbcc9..0000000000000000000000000000000000000000 Binary files a/Resources/images/boolCut.png and /dev/null differ diff --git a/Resources/images/copy.png b/Resources/images/copy.png deleted file mode 100644 index 32048eeb31b06ea6c5b7e721879cfb1ce45d819f..0000000000000000000000000000000000000000 Binary files a/Resources/images/copy.png and /dev/null differ diff --git a/Resources/images/fillet.png b/Resources/images/fillet.png deleted file mode 100644 index 98071f475ca659cccfa4fc555595c799aa6082a4..0000000000000000000000000000000000000000 Binary files a/Resources/images/fillet.png and /dev/null differ diff --git a/Resources/images/new.png b/Resources/images/new.png deleted file mode 100644 index 12131b01008a3ec29ec69f8b3f65c4b3c15b60d6..0000000000000000000000000000000000000000 Binary files a/Resources/images/new.png and /dev/null differ diff --git a/Resources/images/open.png b/Resources/images/open.png deleted file mode 100644 index 45fa2883a71fcb891f1ef7c0c217d71eeae284bc..0000000000000000000000000000000000000000 Binary files a/Resources/images/open.png and /dev/null differ diff --git a/Resources/images/paste.png b/Resources/images/paste.png deleted file mode 100644 index c14425cad1ff1b2c5628be5769c9e9e52b78635f..0000000000000000000000000000000000000000 Binary files a/Resources/images/paste.png and /dev/null differ diff --git a/Resources/images/save.png b/Resources/images/save.png deleted file mode 100644 index daba865fafd22fa18e7c0488eb699b79d3554170..0000000000000000000000000000000000000000 Binary files a/Resources/images/save.png and /dev/null differ diff --git a/Src/Core/ACAMCore.cpp b/Src/Core/ACAMCore.cpp index d08ddaba115726a6a0495674dfef5546256c591f..8bf06fa0523a6deed6c6fec6011ac2672da8a5ae 100644 --- a/Src/Core/ACAMCore.cpp +++ b/Src/Core/ACAMCore.cpp @@ -16,7 +16,6 @@ #include "ACAMCore.h" #include "DataManager.h" -#include "UndoRedoHandler.h" #include @@ -25,7 +24,7 @@ using namespace acamcad; -AMCore::AMCore() : dataManager_(nullptr) +AMCore::AMCore() : dataManager_(nullptr), listener_(nullptr) { init(); @@ -35,9 +34,9 @@ AMCore::AMCore() : dataManager_(nullptr) void AMCore::init() { - addNewDataTypeFile(FILE_EXT_AMCAX_BREP, DataType::BREP_TYPE); - addNewDataTypeFile(FILE_EXT_STEP, DataType::BREP_TYPE); - addNewDataTypeFile(FILE_EXT_STP, DataType::BREP_TYPE); + //addNewDataTypeFile(FILE_EXT_AMCAX_BREP, DataType::BREP_TYPE); + //addNewDataTypeFile(FILE_EXT_STEP, DataType::BREP_TYPE); + //addNewDataTypeFile(FILE_EXT_STP, DataType::BREP_TYPE); inlineSingleOperation(); inlineMultOperation(); @@ -486,6 +485,7 @@ void AMCore::OperateSelectedObject(const SelectModel& s_model, const SelectInfoW { if (obj->dataType() == DataType::MESH_TYPE) { //AdapterObject* adapter = dynamic_cast(obj); + ///obj->mesh->mesh()->numPolygons(); dataManager_->RecordModifyBegin({ obj }); @@ -739,8 +739,16 @@ void AMCore::OperateSelectedObject_Subset(const SelectModel& s_model, std::vecto if (s_id.empty()) return; - BaseObject* obj = dataManager_->getObjectByPersistentID(object_list[0]); + AdapterObject* obj = dataManager_->getObjectByPersistentID(object_list[0]); + AdapterObject* copy_obj = obj->duplicateFaces(s_id); + + if (copy_obj) { + copy_obj->updateDraw(); + dataManager_->addObject(copy_obj); + dataManager_->RecordAddObject({ copy_obj }); + + } } } @@ -912,88 +920,65 @@ void AMCore::Undo() void AMCore::createSubdSphereObject(const MPoint3& center, double radius, size_t subtime) { + if (subtime <= 0) + return; + //MSphere sphere(center, radius); CreateOperate_QuadballTSpline cQuadball(center, radius, subtime); + createObject(&cQuadball, DataType::TSPLINEU_TYPE); - //TSplineUObject* newObject = new TSplineUObject(); - //newObject->doOperate(&cQuadball); - - //dataManager_->addObject(newObject); - //createBackupAdd(newObject); - AdapterObject* object = new AdapterObject; - object->setDataType(DataType::TSPLINEU_TYPE); - cQuadball.operate(object); - dataManager_->addObject(object); - dataManager_->RecordAddObject({ object }); } void AMCore::createCylinderObject(const MPoint3& b_center, const AMCAX::Vector3& axis, double radius, double height, size_t rf_num, size_t vf_num, bool top, bool bottom) { + if (vf_num <= 0 || rf_num <= 0) + return; + //AMCAX::Frame3 frame(b_center, AMCAX::Direction3(axis.Coord())); //MCylinder cylinder(b_center, axis, radius, height); CreateOperate_CylinderTSpline cCylinder(b_center, axis, radius, height, rf_num, vf_num, top, bottom); + createObject(&cCylinder, DataType::TSPLINEU_TYPE); - AdapterObject* object = new AdapterObject; - object->setDataType(DataType::TSPLINEU_TYPE); - - cCylinder.operate(object); - dataManager_->addObject(object); - dataManager_->RecordAddObject({ object }); - - //TSplineUObject* newObject = new TSplineUObject(); - //newObject->doOperate(&cCylinder); - - //dataManager_->addObject(newObject); - //createBackupAdd(newObject); } void AMCore::createConeObject(const MPoint3& b_center, const AMCAX::Vector3& axis, double radius, double height, size_t rf_num, size_t vf_num, bool bottom) { + if (rf_num <= 0 || vf_num <= 0) + return; //MCone cone(b_center, axis, radius, height); CreateOperate_ConeTSpline cCone(b_center, axis, radius, height, rf_num, vf_num, bottom); - - AdapterObject* object = new AdapterObject; - object->setDataType(DataType::TSPLINEU_TYPE); - - cCone.operate(object); - - dataManager_->addObject(object); - dataManager_->RecordAddObject({ object }); + createObject(&cCone, DataType::TSPLINEU_TYPE); } void AMCore::creatTourObject(const MPoint3& center, double radius0, double radius1, size_t rf_num, size_t vf_num) { + if (rf_num <= 0 || vf_num <= 0) + return; + //MTorus tours(center, radius0, radius1); AMCAX::Coord3 coord(center.Coord()); CreateOperate_TorusTSpline cTorus(coord, AMCAX::Coord3(0.0, 0.0, 1.0), radius0, radius1, rf_num, vf_num); + createObject(&cTorus, DataType::TSPLINEU_TYPE); - AdapterObject* object = new AdapterObject; - object->setDataType(DataType::TSPLINEU_TYPE); - - cTorus.operate(object); - - dataManager_->addObject(object); - dataManager_->RecordAddObject({ object }); } void AMCore::createCircular(const MPoint3& center, double radius, double angle, size_t c_num) { - CreateOperate_CircularTSpline cCircular(center.Coord(), AMCAX::Coord3(0.0, 0.0, 1.0), radius, angle, c_num); - AdapterObject* object = new AdapterObject; - object->setDataType(DataType::TSPLINEU_TYPE); + if (c_num <= 0) + return; - cCircular.operate(object); - dataManager_->addObject(object); - dataManager_->RecordAddObject({ object }); + CreateOperate_CircularTSpline cCircular(center.Coord(), AMCAX::Coord3(0.0, 0.0, 1.0), radius, angle, c_num); + createObject(&cCircular, DataType::TSPLINEU_TYPE); + } diff --git a/Src/Core/ACAMCore.h b/Src/Core/ACAMCore.h index d1c6decf699848571671064630f8206ff9225a78..964634725bd211ca0739dfa37c6c731cb32b1243 100644 --- a/Src/Core/ACAMCore.h +++ b/Src/Core/ACAMCore.h @@ -20,15 +20,23 @@ #include "CoreDefine.h" #include "../Object/ObjectDefine.h" -#include "../Record/Record.h" #include +#define FILE_EXT_AMCAX "amcax" +#define FILE_EXT_AMCAX_BREP "abr" +#define FILE_EXT_AMCAX_TMS "tms" +#define FILE_EXT_AMCAX_SUBD "obj" +#define FILE_AMCAX_CONTROL "control.json" +#define FILE_EXT_STEP "step" +#define FILE_EXT_STP "stp" + namespace acamcad { - const QString FILE_EXT_AMCAX_BREP("abr"); - const QString FILE_EXT_STEP("step"); - const QString FILE_EXT_STP("stp"); + //const QString FILE_EXT_AMCAX("amcax"); + //const QString FILE_EXT_AMCAX_BREP("abr"); + //const QString FILE_EXT_STEP("step"); + //const QString FILE_EXT_STP("stp"); class DataManager; //class BackupManager; @@ -36,6 +44,13 @@ namespace acamcad class MultOperate; class UndoRedoHandler; + class AMCoreListener + { + public: + virtual void refresh_title(QString title) = 0; + virtual void refresh_file(QString title) = 0; + }; + class AMCore : public QObject { Q_OBJECT @@ -43,32 +58,43 @@ namespace acamcad public: AMCore(); + void setListener(AMCoreListener* listener) + { + listener_ = listener; + } + + void init(); ~AMCore(); + + void loadObjectsFromFile(const QString& filename); + public slots: void loadObjects(); void saveObjects(); - void saveObjects(const QString& filename); + bool saveObjects(const QString& filename); void saveSelectedObjects(); + bool newObjects(); + void saveAsObjects(); private: - void loadObjectsFromFile(const QString& filename); + bool loadObjectsFromFile1(const QString& filename); private: - void saveObjectsToFile(const QString& filename, std::vector::const_iterator& it_begin, std::vector::const_iterator& it_end); + bool saveObjectsToFile(const QString& filename, std::vector::const_iterator& it_begin, std::vector::const_iterator& it_end); public: //=============== BRep ======= - void createBRepObject(acamcad::MOperation* operate, const std::string& label); + void createObject(acamcad::MOperation* operate, acamcad::DataType type, const std::string& label=""); int getLastPersistentId(); - void createPlaneBRepObject(const AMCAX::Coord3& first, const AMCAX::Coord3& second, const std::string& label = ""); + void createPlaneBRepObject(const AMCAX::Coord2& first, const AMCAX::Coord2& second, const std::string& label = ""); void createCubeBRepObject(const AMCAX::Coord3& first, const AMCAX::Coord3& second, const std::string& label = ""); - void createSphereBRepObject(const AMCAX::Coord3& center, double radius, double angle1, double angle2, double angle3, const std::string& label = ""); + void createSphereBRepObject(const AMCAX::Coord3& center, double radius, double angle1, const std::string& label = ""); void createCylinderBRepObject(const AMCAX::Coord3& b_center, const AMCAX::Coord3& axis, double radius, double height, double angle, const std::string& label = ""); void createCylinderBRepObject(const AMCAX::Coord3& b_center, const AMCAX::Coord3& axis, const AMCAX::Coord3& xaxis, double radius, double height, double angle, const std::string& label = ""); void createConeBRepObject(const AMCAX::Coord3& b_center, const AMCAX::Coord3& axis, double radiusR, double radiusr, double height, double angle, const std::string& label = ""); @@ -88,12 +114,12 @@ namespace acamcad void createWedgeBRepObject(const AMCAX::Coord3& center, const AMCAX::Coord3& axis, const double dx, const double dy, const double dz, const double xmin, const double zmin, const double xmax, const double zmax, const std::string& label = ""); //Mesh - void createPlaneMeshObject(MPoint3& p0, MPoint3& p1, size_t u_num, size_t v_num); - void createCubeMeshObject(const MPoint3& first, const MPoint3& second, size_t x_num = 2, size_t y_num = 2, size_t z_num = 2); + void createPlaneMeshObject(AMCAX::Coord2& p0, AMCAX::Coord2& p1, size_t u_num, size_t v_num); + void createCubeMeshObject(const AMCAX::Coord3& first, const AMCAX::Coord3& second, size_t x_num = 2, size_t y_num = 2, size_t z_num = 2); //TSpline - void createPlaneObject(MPoint3& p0, MPoint3& p1, size_t u_num, size_t v_num); - void createCubeObject(const MPoint3& first, const MPoint3& second, size_t x_num, size_t y_num, size_t z_num); + void createPlaneObject(AMCAX::Coord2& p0, AMCAX::Coord2& p1, size_t u_num, size_t v_num); + void createCubeObject(const AMCAX::Coord3& first, const AMCAX::Coord3& second, size_t x_num, size_t y_num, size_t z_num); void createUVSphereObject(const MPoint3& center, double radius, size_t rf_num, size_t vf_num); void createSubdSphereObject(const MPoint3& center, double radius, size_t subtime); void createCylinderObject(const MPoint3& b_center, const AMCAX::Vector3& axis, double radius, double height, @@ -171,7 +197,7 @@ namespace acamcad //void createBackupNormalOp(BaseObject* object); public: - void addNewDataTypeFile(const QString&, const DataType&); + //void addNewDataTypeFile(const QString&, const DataType&); void setOperationDataType(OperationDataType type); void setMeshOperationType(MeshOperationType type); @@ -180,7 +206,7 @@ namespace acamcad MeshOperationType getMeshOperationType() const { return mesh_op_type_; } private: - std::map file_allowed_type_; //允许的文件类型,string对应到type + //std::map file_allowed_type_; //允许的文件类型,string对应到type OperationDataType op_datatype_; //only use it for some creat function MeshOperationType mesh_op_type_; @@ -198,12 +224,15 @@ namespace acamcad void setDataManager(DataManager* dataManager); DataManager* getDataManager() const; + QString file_; + //void setBackupManager(BackupManager* backManager); //void setRecord(Record& record) { record_ = &record; } private: DataManager* dataManager_; + AMCoreListener* listener_; //BackupManager* backupManager_; //UndoRedoHandler* undoRedoHandler_; }; diff --git a/Src/Core/ACAMCoreCreate.cpp b/Src/Core/ACAMCoreCreate.cpp index 38d2feccdb5fd2455a6e0745121c7f028e587f76..87d0f9d0dfd2307e273f17ff9effcd618dcbb03e 100644 --- a/Src/Core/ACAMCoreCreate.cpp +++ b/Src/Core/ACAMCoreCreate.cpp @@ -19,7 +19,6 @@ #include "../Operation/OperationDefine.h" #include -#include "UndoRedoHandler.h" #include "../Utils/MathUtils.h" #define M_EPSILON 1.0e-8 @@ -98,7 +97,7 @@ namespace acamcad bool arenum = (cR1 != FP_NAN) && (cR2 != FP_NAN) && (cH != FP_NAN) && (cA != FP_NAN); arenum &= (cR1 != FP_INFINITE) && (cR2 != FP_INFINITE) && (cH != FP_INFINITE) && (cA != FP_INFINITE); - bool arepositive = ((r2 > M_EPSILON && r2 > M_EPSILON) || // both positive, truncated cone + bool arepositive = ((r2 > M_EPSILON) || // both positive, truncated cone (cR1 == FP_ZERO && r2 > M_EPSILON) || (cR2 == FP_ZERO && r1 > M_EPSILON)) && // regular cone (h > M_EPSILON); @@ -198,20 +197,26 @@ namespace acamcad { //=================================== BRep =================================== - void AMCore::createBRepObject(MOperation* operate, const std::string& label) + void AMCore::createObject(MOperation* operate, acamcad::DataType type, const std::string& label) { AdapterObject* adapter_object = new AdapterObject; - adapter_object->setDataType(acamcad::DataType::BREP_TYPE); + adapter_object->setDataType(type); //adapter_object->bRep = std::make_unique(adapter_object); //BRepObject* newObject = new BRepObject(); adapter_object->setLabel(label); - operate->operate(adapter_object); - //adapter_object->doOperate(operate); + if (operate->operate(adapter_object)) { + //adapter_object->doOperate(operate); + + dataManager_->addObject(adapter_object); + dataManager_->RecordAddObject({ adapter_object }); + //undoRedoHandler_->recordOperation(adapter_object, ActionType::AddObject); + } + else + { + delete adapter_object; + } - dataManager_->addObject(adapter_object); - dataManager_->RecordAddObject({ adapter_object }); - //undoRedoHandler_->recordOperation(adapter_object, ActionType::AddObject); } @@ -219,14 +224,14 @@ namespace acamcad return dataManager_->getLastPersistentId(); } - void AMCore::createPlaneBRepObject(const AMCAX::Coord3& first, const AMCAX::Coord3& second, const std::string& label) + void AMCore::createPlaneBRepObject(const AMCAX::Coord2& p0p, const AMCAX::Coord2& p1p, const std::string& label) { - AMCAX::Coord2 p0p(first.X(), first.Y()); - AMCAX::Coord2 p1p(second.X(), second.Y()); + //AMCAX::Coord2 p0p(first.X(), first.Y()); + //AMCAX::Coord2 p1p(second.X(), second.Y()); CreateOperate_Plane cPlane(p0p, p1p, 1, 1); - createBRepObject(&cPlane, label); + createObject(&cPlane, acamcad::DataType::BREP_TYPE, label); } void AMCore::createCubeBRepObject(const AMCAX::Coord3& first, const AMCAX::Coord3& second, const std::string& label) @@ -237,21 +242,20 @@ namespace acamcad CreateOperate_Cube cCube(first, second, 1, 1, 1); - createBRepObject(&cCube, label); + createObject(&cCube, acamcad::DataType::BREP_TYPE, label); } void AMCore::createSphereBRepObject(const AMCAX::Coord3& center, double radius, - double angle1, double angle2, double angle3, - const std::string& label) + double angle1, const std::string& label) { if (!CheckSphereParamOK(radius, angle1)) { return; // angle2 and angle3 is currently not used. range unknown. } - CreateOperate_Sphere cSphere(center, radius, angle1, angle2, angle3); + CreateOperate_Sphere cSphere(center, radius, angle1); - createBRepObject(&cSphere, label); + createObject(&cSphere, acamcad::DataType::BREP_TYPE, label); } void AMCore::createCylinderBRepObject(const AMCAX::Coord3& b_center, const AMCAX::Coord3& axis, @@ -265,7 +269,7 @@ namespace acamcad CreateOperate_Cylinder cCylinder(b_center, radius, height, axis, angle); - createBRepObject(&cCylinder, label); + createObject(&cCylinder, acamcad::DataType::BREP_TYPE, label); } void AMCore::createCylinderBRepObject(const AMCAX::Coord3& b_center, const AMCAX::Coord3& axis, const AMCAX::Coord3& xaxis, @@ -279,7 +283,7 @@ namespace acamcad CreateOperate_Cylinder cCylinder(b_center, radius, height, axis, xaxis, angle); - createBRepObject(&cCylinder, label); + createObject(&cCylinder, acamcad::DataType::BREP_TYPE, label); } void AMCore::createConeBRepObject(const AMCAX::Coord3& b_center, const AMCAX::Coord3& axis, @@ -293,7 +297,7 @@ namespace acamcad CreateOperate_Cone cTCone(b_center, axis, radiusR, radiusr, height, angle); - createBRepObject(&cTCone, label); + createObject(&cTCone, acamcad::DataType::BREP_TYPE, label); } void AMCore::createConeBRepObject(const AMCAX::Coord3& b_center, const AMCAX::Coord3& axis, const AMCAX::Coord3& xaxis, @@ -306,7 +310,7 @@ namespace acamcad CreateOperate_Cone cTCone(b_center, axis, xaxis, radiusR, radiusr, height, angle); - createBRepObject(&cTCone, label); + createObject(&cTCone, acamcad::DataType::BREP_TYPE, label); } void AMCore::createTorusBRepObject(const AMCAX::Coord3& center, const AMCAX::Coord3& axis, @@ -320,7 +324,7 @@ namespace acamcad } CreateOperate_Torus cTorus(center, radius0, radius1, axis, angle); - createBRepObject(&cTorus, label); + createObject(&cTorus, acamcad::DataType::BREP_TYPE, label); } void AMCore::createTorusBRepObject(const AMCAX::Coord3& center, const AMCAX::Coord3& axis, const AMCAX::Coord3& xaxis, @@ -334,7 +338,7 @@ namespace acamcad CreateOperate_Torus cTorus(center, radius0, radius1, axis, xaxis, angle); - createBRepObject(&cTorus, label); + createObject(&cTorus, acamcad::DataType::BREP_TYPE, label); } void AMCore::createPolygonBRepObject(const std::vector& points, const std::string& label) @@ -345,7 +349,7 @@ namespace acamcad } CreateOperate_Polygon cPolygon(points); - createBRepObject(&cPolygon, label); + createObject(&cPolygon, acamcad::DataType::BREP_TYPE, label); } void AMCore::createPrismBRepObject(const std::vector& points, const AMCAX::Coord3d& prism_axis, const std::string& label) @@ -356,7 +360,7 @@ namespace acamcad } CreateOperate_Prism cPrism(points, prism_axis); - createBRepObject(&cPrism, label); + createObject(&cPrism, acamcad::DataType::BREP_TYPE, label); } void AMCore::createRoundedPolygonBRepObject(const std::vector& points, const std::string& label) @@ -364,7 +368,7 @@ namespace acamcad // TODO: CheckParamOk CreateOperate_RoundedPolygon cRPolygon(points); - createBRepObject(&cRPolygon, label); + createObject(&cRPolygon, acamcad::DataType::BREP_TYPE, label); } void AMCore::createRoundedPrismBRepObject(const std::vector& points, const AMCAX::Coord3& prism_axis, const std::string& label) @@ -372,7 +376,7 @@ namespace acamcad // TODO: CheckParamOk CreateOperate_RoundedPrism cRPrism(points, prism_axis); - createBRepObject(&cRPrism, label); + createObject(&cRPrism, acamcad::DataType::BREP_TYPE, label); } void AMCore::createWedgeBRepObject(const double dx, const double dy, const double dz, const double ltx, @@ -380,7 +384,7 @@ namespace acamcad { CreateOperate_Wedge cWedge(dx, dy, dz, ltx); - createBRepObject(&cWedge, label); + createObject(&cWedge, acamcad::DataType::BREP_TYPE, label); } void AMCore::createWedgeBRepObject(const AMCAX::Coord3& center, @@ -389,7 +393,7 @@ namespace acamcad { CreateOperate_Wedge cWedge(center, dx, dy, dz, ltx); - createBRepObject(&cWedge, label); + createObject(&cWedge, acamcad::DataType::BREP_TYPE, label); } void AMCore::createWedgeBRepObject(const AMCAX::Coord3& center, const AMCAX::Coord3& axis, @@ -397,7 +401,7 @@ namespace acamcad const std::string& label) { CreateOperate_Wedge cWedge(center, axis, dx, dy, dz, ltx); - createBRepObject(&cWedge, label); + createObject(&cWedge, acamcad::DataType::BREP_TYPE, label); } void AMCore::createWedgeBRepObject(const double dx, const double dy, const double dz, @@ -406,7 +410,7 @@ namespace acamcad { CreateOperate_Wedge cWedge(dx, dy, dz, xmin, zmin, xmax, zmax); - createBRepObject(&cWedge, label); + createObject(&cWedge, acamcad::DataType::BREP_TYPE, label); } void AMCore::createWedgeBRepObject(const AMCAX::Coord3& center, @@ -416,7 +420,7 @@ namespace acamcad { CreateOperate_Wedge cWedge(center, dx, dy, dz, xmin, zmin, xmax, zmax); - createBRepObject(&cWedge, label); + createObject(&cWedge, acamcad::DataType::BREP_TYPE, label); } void AMCore::createWedgeBRepObject(const AMCAX::Coord3& center, const AMCAX::Coord3& axis, @@ -425,104 +429,59 @@ namespace acamcad { CreateOperate_Wedge cWedge(center, axis, dx, dy, dz, xmin, zmin, xmax, zmax); - createBRepObject(&cWedge, label); + createObject(&cWedge, acamcad::DataType::BREP_TYPE, label); } //Mesh - void AMCore::createPlaneMeshObject(MPoint3& p0, MPoint3& p1, size_t u_num, size_t v_num) + void AMCore::createPlaneMeshObject(AMCAX::Coord2& p0p, AMCAX::Coord2& p1p, size_t u_num, size_t v_num) { + if (u_num <= 0 || v_num <= 0) + return; //MPlane plane; - AMCAX::Coord2 p0p(p0.X(), p0.Y()); - AMCAX::Coord2 p1p(p1.X(), p1.Y()); + //AMCAX::Coord2 p0p(p0.X(), p0.Y()); + //AMCAX::Coord2 p1p(p1.X(), p1.Y()); CreateOperate_PlaneMesh cPlane(p0p, p1p, u_num, v_num); - - AdapterObject* adapter = new AdapterObject; - adapter->setDataType(acamcad::DataType::MESH_TYPE); - - //adapter->doOperate(&cPlane); - - cPlane.operate(adapter); - - - //MeshObject* newObject = new MeshObject(); - //newObject->doOperate(&cPlane); - - dataManager_->addObject(adapter); - //createBackupAdd(newObject); - //undoRedoHandler_->recordOperation(adapter, ActionType::AddObject); - dataManager_->RecordAddObject({ adapter }); + createObject(&cPlane, acamcad::DataType::MESH_TYPE); } - void AMCore::createCubeMeshObject(const MPoint3& first, const MPoint3& second, + void AMCore::createCubeMeshObject(const AMCAX::Coord3& c0, const AMCAX::Coord3& c1, size_t x_num, size_t y_num, size_t z_num) { - AMCAX::Coord3 c0(first.X(), first.Y(), first.Z()), c1(second.X(), second.Y(), second.Z()); + //AMCAX::Coord3 c0(first.X(), first.Y(), first.Z()), c1(second.X(), second.Y(), second.Z()); + if (x_num <= 0 || y_num <= 0 || z_num <= 0) + return; CreateOperate_CubeMesh cCube(c0, c1, x_num, y_num, z_num); - - AdapterObject* adapter = new AdapterObject; - adapter->setDataType(acamcad::DataType::MESH_TYPE); - - cCube.operate(adapter); - - - dataManager_->addObject(adapter); - //dataManager_->addObject(adapter); - //createBackupAdd(newObject); - //undoRedoHandler_->recordOperation(adapter, ActionType::AddObject); - dataManager_->RecordAddObject({ adapter }); + createObject(&cCube, acamcad::DataType::MESH_TYPE); } - void AMCore::createPlaneObject(MPoint3& p0, MPoint3& p1, size_t u_num, size_t v_num) + void AMCore::createPlaneObject(AMCAX::Coord2& p0p, AMCAX::Coord2& p1p, size_t u_num, size_t v_num) { - //MPlane plane; - AMCAX::Coord2 p0p(p0.X(), p0.Y()); - AMCAX::Coord2 p1p(p1.X(), p1.Y()); - CreateOperate_PlaneTSpline cPlane(p0p, p1p, u_num, v_num); - - AdapterObject* adapter = new AdapterObject; - adapter->setDataType(acamcad::DataType::TSPLINEU_TYPE); - - cPlane.operate(adapter); - - dataManager_->addObject(adapter); - - dataManager_->RecordAddObject({ adapter }); + if (u_num <= 0 || v_num <= 0) + return; - //createBackupAdd(newObject); + CreateOperate_PlaneTSpline cPlane(p0p, p1p, u_num, v_num); + createObject(&cPlane, acamcad::DataType::TSPLINEU_TYPE); } - void AMCore::createCubeObject(const MPoint3& first, const MPoint3& second, size_t x_num, size_t y_num, size_t z_num) + void AMCore::createCubeObject(const AMCAX::Coord3& min_bb, const AMCAX::Coord3& max_bb, size_t x_num, size_t y_num, size_t z_num) { - AMCAX::Coord3 min_bb = first.Coord(), - max_bb = second.Coord(); + if (x_num <= 0 || y_num <= 0 || z_num <= 0) + return; + //AMCAX::Coord3 min_bb = first.Coord(), + // max_bb = second.Coord(); CreateOperate_CubeTSpline cCube(min_bb, max_bb, x_num, y_num, z_num); - - - AdapterObject* adapter = new AdapterObject; - adapter->setDataType(acamcad::DataType::TSPLINEU_TYPE); - - cCube.operate(adapter); - - dataManager_->addObject(adapter); - - dataManager_->RecordAddObject({ adapter }); + createObject(&cCube, acamcad::DataType::TSPLINEU_TYPE); } void AMCore::createUVSphereObject(const MPoint3& center, double radius, size_t rf_num, size_t vf_num) { + if (rf_num <= 0 || vf_num <= 0) + return; //MSphere sphere(center, radius); CreateOperate_SphereTSpline cSphere(center.Coord(), radius, rf_num, vf_num); - - AdapterObject* adapter = new AdapterObject; - adapter->setDataType(acamcad::DataType::TSPLINEU_TYPE); - - cSphere.operate(adapter); - - dataManager_->addObject(adapter); - - dataManager_->RecordAddObject({ adapter }); + createObject(&cSphere, acamcad::DataType::TSPLINEU_TYPE); } //=========================================================== diff --git a/Src/Core/ACAMCoreFile.cpp b/Src/Core/ACAMCoreFile.cpp index 337fbff529b0403349575ae6c139907069c6853e..fa8465bea2f89bd705f60e6e9a12a2cf9361c1df 100644 --- a/Src/Core/ACAMCoreFile.cpp +++ b/Src/Core/ACAMCoreFile.cpp @@ -28,61 +28,156 @@ #include #include #include - +#include +#include +#include +#include #include "DataManager.h" +#include +#include +#include +#include + +namespace +{ + QString defaultDir = "."; +} + namespace acamcad { - void AMCore::addNewDataTypeFile(const QString& str, const DataType& dt) - { - file_allowed_type_[str] = dt; - } + //void AMCore::addNewDataTypeFile(const QString& str, const DataType& dt) + //{ + // file_allowed_type_[str] = dt; + //} void AMCore::loadObjects() { - static QString defaultDir = "."; - QString filter = "AMCAX BRep files (" "*." + FILE_EXT_AMCAX_BREP + ")"; - QString stepFilter = "STEP files (" "*." + FILE_EXT_STP + " " "*." + FILE_EXT_STEP + ")"; - QString finalFilter = filter + ";;" + stepFilter; + ///QZipReader zipreader(""); + //static QString defaultDir = "."; + QString filterAM = "AMCAX files (" "*." FILE_EXT_AMCAX ")"; + QString filter = "AMCAX BRep files (" "*." FILE_EXT_AMCAX_BREP ")"; + QString stepFilter = "STEP files (" "*." FILE_EXT_STP " " "*." FILE_EXT_STEP ")"; + QString finalFilter = filterAM + ";;" + filter + ";;" + stepFilter; QString fileName = QFileDialog::getOpenFileName(NULL, "", defaultDir, finalFilter); + loadObjectsFromFile(fileName); + } + + void AMCore::loadObjectsFromFile(const QString& fileName) + { if (!fileName.isEmpty()) { - auto lpos = std::max(fileName.lastIndexOf("/"), fileName.lastIndexOf("\\")); - defaultDir = fileName.left(lpos + 1); - loadObjectsFromFile(fileName); + bool result = loadObjectsFromFile1(fileName); + + if (result && file_.isEmpty()) + { + file_ = fileName; + + if (listener_) + listener_->refresh_title(file_); + } + + if (listener_) + listener_->refresh_file(fileName); } } void AMCore::saveObjects() { - static QString defaultDir = "."; - QString filter = "AMCAX BRep files (" "*." + FILE_EXT_AMCAX_BREP + ")"; - QString stepFilter = "STEP files (" "*." + FILE_EXT_STP + " " "*." + FILE_EXT_STEP + ")"; - QString finalFilter = filter + ";;" + stepFilter; + //std::cout << "AMCore::saveObjects " << QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation).toStdString() << std::endl; + if (file_.isEmpty()) + { + //static QString defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + QString filterAM = "AMCAX files (" "*." FILE_EXT_AMCAX ")"; + //QString filter = "AMCAX BRep files (" "*." FILE_EXT_AMCAX_BREP ")"; + //QString stepFilter = "STEP files (" "*." FILE_EXT_STP " " "*." FILE_EXT_STEP ")"; + QString finalFilter = filterAM; + QString fileName = QFileDialog::getSaveFileName(NULL, "", defaultDir, finalFilter); + + if (saveObjects(fileName)) + { + file_ = fileName; + + if (listener_) + listener_->refresh_title(file_); + } + + } + else + { + saveObjects(file_); + } + + + } + + bool AMCore::newObjects() + { + //QString defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + QString filterAM = "AMCAX files (" "*." FILE_EXT_AMCAX ")"; + //QString filter = "AMCAX BRep files (" "*." FILE_EXT_AMCAX_BREP ")"; + //QString stepFilter = "STEP files (" "*." FILE_EXT_STP " " "*." FILE_EXT_STEP ")"; + QString finalFilter = filterAM; + QString fileName = QFileDialog::getSaveFileName(NULL, "", defaultDir, finalFilter); + + if (saveObjects(fileName)) + { + file_ = fileName; + + if (listener_) + listener_->refresh_title(file_); + + return true; + } + else + return false; + } + + + void AMCore::saveAsObjects() + { + ///QString defaultDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + QString filterAM = "AMCAX files (" "*." FILE_EXT_AMCAX ")"; + //QString filter = "AMCAX BRep files (" "*." FILE_EXT_AMCAX_BREP ")"; + QString stepFilter = "STEP files (" "*." FILE_EXT_STP " " "*." FILE_EXT_STEP ")"; + QString finalFilter = filterAM + ";;" + stepFilter; QString fileName = QFileDialog::getSaveFileName(NULL, "", defaultDir, finalFilter); - saveObjects(fileName); + if (saveObjects(fileName)) + { + file_ = fileName; + + if (listener_) + listener_->refresh_title(file_); + } + } - void AMCore::saveObjects(const QString& filename) + bool AMCore::saveObjects(const QString& filename) { if (!filename.isEmpty()) { CObjectIter it_begin = dataManager_->objects_begin(); CObjectIter it_end = dataManager_->objects_end(); - saveObjectsToFile(filename, it_begin, it_end); + + + if (listener_) + listener_->refresh_file(filename); + + return saveObjectsToFile(filename, it_begin, it_end); } } void AMCore::saveSelectedObjects() { - static QString defaultDir = "."; - QString filter = "AMCAX BRep files (" "*." + FILE_EXT_AMCAX_BREP + ")"; - QString stepFilter = "STEP files (" "*." + FILE_EXT_STP + " " "*." + FILE_EXT_STEP + ")"; - QString finalFilter = filter + ";;" + stepFilter; + //static QString defaultDir = "."; + QString filterAM = "AMCAX files (" "*." FILE_EXT_AMCAX ")"; + //QString filter = "AMCAX BRep files (" "*." FILE_EXT_AMCAX_BREP ")"; + QString stepFilter = "STEP files (" "*." FILE_EXT_STP " " "*." FILE_EXT_STEP ")"; + QString finalFilter = filterAM + ";;" + stepFilter; QString filename = QFileDialog::getSaveFileName(NULL, "", defaultDir, finalFilter); if (!filename.isEmpty()) @@ -96,26 +191,28 @@ namespace acamcad } } - void AMCore::loadObjectsFromFile(const QString& filename) + bool AMCore::loadObjectsFromFile1(const QString& filename) { + auto lpos = std::max(filename.lastIndexOf("/"), filename.lastIndexOf("\\")); + defaultDir = filename.left(lpos + 1); + + QFileInfo fi(filename); QString filepath = fi.absoluteFilePath(); QFile file(filepath); QString ext1 = fi.suffix(); if (fi.isDir() || !file.exists()) - return; + return false; if (!file.open(QIODevice::ReadOnly)) - return; + return false; std::vector vec; - - - QTextStream in(&file); - QString content = in.readAll(); - std::istringstream fin(content.toStdString(), std::istream::in); if (auto lext1 = ext1.toLower(); lext1 == FILE_EXT_STP || lext1 == FILE_EXT_STEP) { //stp file + QTextStream in(&file); + QString content = in.readAll(); + std::istringstream fin(content.toStdString(), std::istream::in); AMCAX::STEP::StepReader stepReader(fin); stepReader.Read(); AMCAX::STEP::StepDataList shapes = stepReader.GetShapes(); @@ -136,6 +233,9 @@ namespace acamcad } else if (ext1 == FILE_EXT_AMCAX_BREP) //abr file { + QTextStream in(&file); + QString content = in.readAll(); + std::istringstream fin(content.toStdString(), std::istream::in); AMCAX::TopoShape shapeCmp; AMCAX::ShapeTool::Read(shapeCmp, fin); @@ -153,34 +253,99 @@ namespace acamcad vec.push_back(adapter_object); } } + else if (ext1 == FILE_EXT_AMCAX) + { + file.close(); + QZipReader zip(filename); + + if (zip.isReadable()) + { + { + QByteArray array = zip.fileData(FILE_AMCAX_CONTROL); + + QJsonParseError jsonError; + QJsonDocument doc = QJsonDocument::fromJson(array, &jsonError); + + if (jsonError.error != QJsonParseError::NoError && !doc.isNull()) + { + return false; + } + + if (doc["version"].toInt() != 1) + { + return false; + } + + } + + for (int i = 0; i < zip.fileInfoList().size(); i++) + { + QByteArray dt = zip.fileInfoList().at(i).filePath.toUtf8(); + QString strtemp = QString::fromLocal8Bit(dt); + + if (strtemp == FILE_AMCAX_CONTROL) + continue; + + QByteArray array = zip.fileData(strtemp); + + QFileInfo info(strtemp); + + std::istringstream context(array.toStdString(), std::istream::in); + + AdapterObject* adapter_object = new AdapterObject; + if (info.suffix() == FILE_EXT_AMCAX_BREP) + { + adapter_object->setDataType(acamcad::DataType::BREP_TYPE); + } + else if (info.suffix() == FILE_EXT_AMCAX_SUBD) + { + adapter_object->setDataType(acamcad::DataType::MESH_TYPE); + } + else if (info.suffix() == FILE_EXT_AMCAX_TMS) + { + adapter_object->setDataType(acamcad::DataType::TSPLINEU_TYPE); + } + + if (adapter_object->readStream(context)) + { + adapter_object->setVisible(true); + adapter_object->updateDraw(); + vec.push_back(adapter_object); + dataManager_->addObject(adapter_object); + } + else + { + delete adapter_object; + } + } + dataManager_->RecordAddObject(vec); + return true; + } + + } dataManager_->RecordAddObject(vec); + + return false; } - void AMCore::saveObjectsToFile(const QString& filename, std::vector::const_iterator& it_begin, std::vector::const_iterator& it_end) + bool AMCore::saveObjectsToFile(const QString& filename, std::vector::const_iterator& it_begin, std::vector::const_iterator& it_end) { - if (it_begin == it_end) { - return; - } + auto lpos = std::max(filename.lastIndexOf("/"), filename.lastIndexOf("\\")); + defaultDir = filename.left(lpos + 1); DataType file_type; QString ext = QFileInfo(filename).suffix(); QFileInfo f_info = QFileInfo(filename); - if (file_allowed_type_.find(ext) != file_allowed_type_.end()) - { - file_type = file_allowed_type_[ext]; - } - else - { - file_type = DataType::CUSTOM_TYPE; - return; - } - if (auto lext = ext.toLower(); lext == FILE_EXT_STP || lext == FILE_EXT_STEP) // stp file { + if (it_begin == it_end) { + return false; + } + std::ostringstream fout; AMCAX::STEP::StepWriter stepWriter(fout); stepWriter.Init(); @@ -200,29 +365,92 @@ namespace acamcad file.close(); } } - else if (ext == FILE_EXT_AMCAX_BREP) // abr file + else if (ext == FILE_EXT_AMCAX) // cmcax file { - AMCAX::TopoCompound comp; - AMCAX::TopoBuilder build; - build.MakeCompound(comp); + QZipWriter write(filename); + + if (!write.isWritable()) + return false; + { + QJsonObject json; + json.insert("version", 1); + + QJsonDocument doc; + doc.setObject(json); + + write.addFile(QString(FILE_AMCAX_CONTROL), doc.toJson()); + + } + + std::vector brep, obj, tms; + for (CObjectIter o_it = it_begin; o_it != it_end; o_it++) { - AdapterObject* brep_object = *o_it; + AdapterObject* object = *o_it; + + switch (object->dataType()) + { + case acamcad::DataType::BREP_TYPE: + brep.push_back(object); + break; + case acamcad::DataType::MESH_TYPE: + obj.push_back(object); + break; + case acamcad::DataType::TSPLINEU_TYPE: + tms.push_back(object); + break; + default: + break; + } - if (brep_object->dataType() == acamcad::DataType::BREP_TYPE) - build.Add(comp, brep_object->bRep->getShape()); } - std::ostringstream fout; - AMCAX::ShapeTool::Write(comp, fout); - QFile file(filename); - if (file.open(QIODevice::WriteOnly | QIODevice::Text)) + for (int i = 0; i < brep.size(); i++) { - QTextStream out(&file); - out << fout.str().c_str(); - file.close(); + std::ostringstream fout; + AMCAX::ShapeTool::Write(brep[i]->bRep->getShape(), fout); + QByteArray arr; + arr.append(fout.str().c_str()); + write.addFile(QString("B%1.%2").arg(i).arg(FILE_EXT_AMCAX_BREP), arr); + } + + + for (int i = 0; i < obj.size(); i++) + { + std::ostringstream fout; + + if (AMCAX::SubD::PolyMeshIO::WriteMeshStreamOBJ(fout, obj[i]->mesh->getShape())) { + QByteArray arr; + arr.append(fout.str().c_str()); + + write.addFile(QString("M%1.%2").arg(i).arg(FILE_EXT_AMCAX_SUBD), arr); + } + + } + + for (int i = 0; i < tms.size(); i++) + { + std::ostringstream fout; + AMCAX::TMS::TMSplineIO io; + + if (io.WriteTMSplineStream(fout, tms[i]->tSpline->getShape())) + { + QByteArray arr; + arr.append(fout.str().c_str()); + + write.addFile(QString("T%1.%2").arg(i).arg(FILE_EXT_AMCAX_TMS), arr); + } + } + + write.close(); + + return true; + } + + + return false; } } diff --git a/Src/Core/DataManager.cpp b/Src/Core/DataManager.cpp index 6c66dc56ec9c5618890d76d9354643a705733190..efc26872cb0e5489c2bfa95e6e5adf1798b1e857 100644 --- a/Src/Core/DataManager.cpp +++ b/Src/Core/DataManager.cpp @@ -15,25 +15,18 @@ */ #include "DataManager.h" #include "../Object/BaseObject.h" +#include "LocalDataManager.h" using namespace acamcad; DataManager::DataManager() : data_persistent_(0) { - + //local.initHistory(); } DataManager::~DataManager() { - if (objects_.size() != 0) - { - for (ObjectIter o_it = objects_begin(); o_it != objects_end(); ++o_it) - { - BaseObject* obj = *o_it; - delete obj; - } - } - objects_.clear(); + deleteAllObject(); } @@ -152,6 +145,19 @@ void DataManager::deleteObject(AdapterObject* object) } } +void DataManager::deleteAllObject() +{ + if (objects_.size() != 0) + { + for (ObjectIter o_it = objects_begin(); o_it != objects_end(); ++o_it) + { + BaseObject* obj = *o_it; + delete obj; + } + } + objects_.clear(); +} + void DataManager::restoreObject(AdapterObject* object) { object->setPersistentId(data_persistent_); data_persistent_++; @@ -162,18 +168,20 @@ void DataManager::restoreObject(AdapterObject* object) void DataManager::addObject_UndoManage(AdapterObject* object) { - AdapterObject* new_object = new AdapterObject(*object, true); + AdapterObject* new_object = object; + + new_object->setVisible(true); new_object->updateDraw(); objects_.push_back(new_object); } -void DataManager::deleteObject_UndoManage(AdapterObject* object) +void DataManager::deleteObject_UndoManage(int persistentId) { for (ObjectIter o_it = objects_begin(); o_it != objects_end(); o_it++) { - if ((*o_it)->persistentId() == object->persistentId()) + if ((*o_it)->persistentId() == persistentId) { AdapterObject* obj = *o_it; delete obj; diff --git a/Src/Core/DataManager.h b/Src/Core/DataManager.h index 285404d5c9c88fb587a24c8076fc1ed506e7d396..058de8c018c0c9e9f7839739301165f7f727fca7 100644 --- a/Src/Core/DataManager.h +++ b/Src/Core/DataManager.h @@ -53,11 +53,12 @@ namespace acamcad AdapterObject* copyObject(AdapterObject* object); void deleteObject(AdapterObject* object); + void deleteAllObject(); void restoreObject(AdapterObject* object); //恢复一个被删除的单元,在备份的时候使用 //UndoManager virtual void addObject_UndoManage(AdapterObject* object) override; - virtual void deleteObject_UndoManage(AdapterObject* object) override; + virtual void deleteObject_UndoManage(int persistentId) override; virtual void sortObject_UndoManage() override; diff --git a/Src/Core/LocalDataManager.cpp b/Src/Core/LocalDataManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd3e810750040b98439a9b72cc46e3a4b98e56c3 --- /dev/null +++ b/Src/Core/LocalDataManager.cpp @@ -0,0 +1,242 @@ +#include "LocalDataManager.h" +#include +#include +#include +#include + +namespace acamcad +{ + LocalDataManager::LocalDataManager() + { + //initData(); + initData(database_, "history.db"); + } + + LocalDataManager::~LocalDataManager() + { + QSqlQuery query; + query.exec("DELETE FROM model;"); + database_.close(); + } + + + + bool LocalDataManager::initData(QSqlDatabase& database, QString db_name) + { + + ///QSqlDatabase database; + QDir dir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + if (!dir.exists()) + { + dir.mkdir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + } + + if (QSqlDatabase::contains("qt_sql_default_connection")) + { + database_ = QSqlDatabase::database("qt_sql_default_connection"); + } + else + { + // SQliteݿ + database_ = QSqlDatabase::addDatabase("QSQLITE"); + // ݿļ + //QString file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/MyDataBase.db"; + QString file = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/" + db_name; + //QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + //std::cout << "LocalDataManager::initHistory :" << file.toStdString() << std::endl; + database_.setDatabaseName(file); + std::cout << "database:" << file.toStdString() << std::endl; + } + + if (!database_.open()) + { + std::cout << "Error: Failed to connect database." << database_.lastError().nativeErrorCode().toStdString() << std::endl; + return false; + } + + + + // do something + // ִsqlĶ + QSqlQuery sqlQuery; + + // ִsql + if (!sqlQuery.exec("CREATE TABLE version (\ + name TEXT NOT NULL primary key,\ + ver INT NOT NULL)")) + { + std::cout << "Error: Fail to create table version. " << sqlQuery.lastError().nativeErrorCode().toStdString() << std::endl; + } + + + // ִsql + if (!sqlQuery.exec("CREATE TABLE model (\ + step INT NOT NULL,\ + persistentId INT NOT NULL,\ + type INT NOT NULL,\ + data TEXT NOT NULL)")) + { + std::cout << "Error: Fail to create table model. " << sqlQuery.lastError().nativeErrorCode().toStdString() << std::endl; + } + else + sqlQuery.exec("insert into version(name,ver) values('model',1)"); + + sqlQuery.exec("DELETE FROM model;"); + + + if (!sqlQuery.exec("CREATE TABLE file_recent (\ + file TEXT NOT NULL,\ + time INTEGER NOT NULL)")) + { + std::cout << "Error: Fail to create table file_recent. " << sqlQuery.lastError().nativeErrorCode().toStdString() << std::endl; + } + else + sqlQuery.exec("insert into version(name,ver) values('file_recent',1)"); + + // ִsql + if (!sqlQuery.exec("CREATE TABLE position (\ + id INT NOT NULL primary key,\ + array BLOB NOT NULL)")) + { + std::cout << "Error: Fail to create table position. " << sqlQuery.lastError().nativeErrorCode().toStdString() << std::endl; + } + else + { + sqlQuery.exec("insert into version(name,ver) values('position',1)"); + sqlQuery.exec("insert into position(id,array) values(1,'')"); + } + + + + //sqlQuery.exec("VACUUM;PRAGMA auto_vacuum = 2;PRAGMA page_size = 4096;"); + return true; + } + + void LocalDataManager::execStore(const std::vector& models) + { + //std::cout << "LocalDataManager::exe!" << std::endl; + //QString qsQuery; + QSqlQuery query(database_); + + query.prepare("insert into model(step,persistentId,type,data) values(?,?,?,?)"); + + QVariantList steps, persistentIds, types, datas; + + for (int i = 0; i < models.size(); i++) + { + auto& data = models[i]; + + steps << data.step; + persistentIds << data.persistentId; + types << data.type; + datas << data.data; + } + + query.addBindValue(steps); + query.addBindValue(persistentIds); + query.addBindValue(types); + query.addBindValue(datas); + + if (!query.execBatch()) + { + std::cout << "LocalDataManager::exec " << query.lastError().databaseText().toStdString() << std::endl; + } + + } + + void LocalDataManager::execStore(const std::vector& models) + { + QVariantList files, times; + for (auto& iter : models) + { + files << iter.file; + times << iter.time; + } + + QSqlQuery query(database_); + query.prepare("delete from file_recent where file = ?"); + query.addBindValue(files); + query.execBatch(); + + query.prepare("insert into file_recent(file,time) values(?,?)"); + query.addBindValue(files); + query.addBindValue(times); + + if (!query.execBatch()) + { + std::cout << "LocalDataManager::exec " << query.lastError().databaseText().toStdString() << std::endl; + } + + if (query.exec("select file from file_recent order by time DESC limit 10 offset 20")) + { + QVariantList files1; + while (query.next()) + { + files1 << query.value(0).toString(); + } + + if (!files1.isEmpty()) + { + query.prepare("delete from file_recent where file = ?"); + query.addBindValue(files1); + + query.execBatch(); + } + + } + + + } + + void LocalDataManager::execGet(std::vector& models) + { + QSqlQuery query(database_); + if (query.exec("select file from file_recent order by time DESC limit 20")) + { + while (query.next()) + { + //DataModelRecent data(query.value(0).) + models.push_back({ query.value(0).toString(),0 }); + } + } + + } + + + void LocalDataManager::execStore(const DataModelPosition& position) + { + + QSqlQuery sqlQuery; + sqlQuery.prepare("update position set array = ? where id = 1"); + + ///QVariant bFullScreen, x, y, w, h; + sqlQuery.addBindValue(QVariant::fromValue(position.array)); + + sqlQuery.exec(); + //bFullScreen.setValue(position.bFullScreen); + + + } + + void LocalDataManager::execGet(DataModelPosition& models) + { + QSqlQuery sqlQuery; + + if (sqlQuery.exec("select array from position where id = 1")) + { + while (sqlQuery.next()) + { + //QArrayData d(data); + models.array = sqlQuery.value(0).value(); + } + + } + } + + //bool LocalDataManager::execGet(QSqlQuery& query) + //{ + // //QSqlQuery query(database_); + // return query.exec(); + + //} +} \ No newline at end of file diff --git a/Src/Core/UndoRedoHandler.h b/Src/Core/LocalDataManager.h similarity index 31% rename from Src/Core/UndoRedoHandler.h rename to Src/Core/LocalDataManager.h index 7a55149d6e652525140f75d177d9bb86f5e2f0c3..e08cc3f46dd2ca68ea754c98a974825e164828e8 100644 --- a/Src/Core/UndoRedoHandler.h +++ b/Src/Core/LocalDataManager.h @@ -1,78 +1,72 @@ /* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. +* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. * All rights reserved. * -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this +* This software is licensed under the GNU Affero General Public License +* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this * software under the terms of either license. * -* For more information about the AGPLv3.0 license, please visit: +* For more information about the AGPLv3.0 license, please visit: * https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please +* For licensing inquiries or to obtain a commercial license, please * contact Hefei Jiushao Intelligent Technology Co., Ltd. * =================================================================== -* Author: +* Author: */ -#include +#pragma once + +#include +#include +#include +#include +#include +#include +#include -#include -#include -#include namespace acamcad { - enum class ActionType { - AddObject, - DeleteObject, - OperateObject - }; + class LocalDataManager + { + public: + LocalDataManager(); + ~LocalDataManager(); - class BaseObject; - class DataManager; + struct DataModelHistory + { + int step; + int persistentId; + int type; + QString data; + }; - class ContextHolder { - public: - ContextHolder(int opId); - ContextHolder(int opId, BaseObject* object, ActionType op_type); - ~ContextHolder(); - public : - int getOperationId() { return operationId_; } - int getObjectPid() { return objectPid_; } - const AMCAX::TopoShape& getBackShape() { return backshape_; } - private: - int operationId_; - int objectPid_; // the persistentId of the object in datamanager; used to query whether the object still exists in data manager. - AMCAX::TopoShape backshape_; // the backup shape data for the object; - }; + void execStore(const std::vector& models); + //bool execGet(QSqlQuery& query); - typedef std::pair OperateContext; - class UndoRedoHandler { - public: - UndoRedoHandler(DataManager* dataManager, int max_queue_size); - ~UndoRedoHandler(); + struct DataModelRecent + { + QString file; + long long time; + }; + + void execStore(const std::vector& models); + void execGet(std::vector& models); - public: - void increaseSequence(); // - void recordOperation(BaseObject* object, ActionType opType, bool incSeq = true); + struct DataModelPosition + { + QByteArray array; - void undo(); - void redo(); - bool canUndo(); - bool canRedo(); + }; + void execStore(const DataModelPosition& position); + void execGet(DataModelPosition& models); - private: - void revokeOp(const OperateContext& context); - void applyOp(const OperateContext& context); + protected: - private: - std::deque undo_queue_; - std::deque redo_queue_; - void releaseQueueItem(OperateContext& context); + bool initData(QSqlDatabase& database, QString db_name); - private: - int max_queue_size_; - int operation_seq_; //operations that share the same sequence - DataManager* dataManager_; + protected: + QSqlDatabase database_; + ///QString db_name_; }; } \ No newline at end of file diff --git a/Src/Core/UndoManage.cpp b/Src/Core/UndoManage.cpp index c4de3d68f3569dcf4804574c5f8b140b66a2b3eb..44c3564916cf63ef90d77bef7e82797ea47184c1 100644 --- a/Src/Core/UndoManage.cpp +++ b/Src/Core/UndoManage.cpp @@ -1,9 +1,17 @@ #include "UndoManage.h" #include +#include "../Windows/RibbonViewer.h" +#include +#include +#include + +#include +#include +#include namespace acamcad { - UndoManager::UndoManager() :data_index_(0) + UndoManager::UndoManager() :LocalDataManager(), data_index_size_(0), data_size_(0) { } @@ -44,77 +52,99 @@ namespace acamcad return; } - for (auto& iter : oldData) { - std::shared_ptr object = getObjectInData(iter); - oldObjects_.push_back(std::move(object)); - } + addBegin(oldData); } void UndoManager::RecordModifyEnd(std::vector curData) { if (!curData.empty() && !oldObjects_.empty()) { - Data data{ {} , oldObjects_ }; + //Data data{ {} , oldObjects_ }; + + addEnd(curData); + nextStep(oldObjects_); - for (auto& iter : curData) { - std::shared_ptr object = std::make_shared(*iter, false); - data.curObjects.push_back(std::move(object)); - } - nextStep(data); } + + oldObjects_.clear(); } - void UndoManager::nextStep(std::vector curData, std::vector oldData) + void UndoManager::addBegin(const std::vector& oldData) { - if (curData.empty() && oldData.empty()) - return; + for (auto& iter : oldData) + { + DataModelHistory data{ 2 * data_index_size_,iter->persistentId(),-1,"" }; + //std::shared_ptr object = getObjectInData(iter); + oldObjects_.push_back(data); + } + } - Data data; - //std::vector> curVector, oldVector; + void UndoManager::addEnd(const std::vector& curData) + { for (auto& iter : curData) { - std::shared_ptr object = std::make_shared(*iter, false); - data.curObjects.push_back(std::move(object)); - } + //std::shared_ptr object = std::make_shared(*iter, false); + //data.curObjects.push_back(std::move(object)); + DataModelHistory data{ 2 * data_index_size_ + 1,iter->persistentId(),iter->dataType(),"" }; + //std::shared_ptr object = getObjectInData(iter); + std::ostringstream fout; - for (auto& iter : oldData) { - std::shared_ptr object = getObjectInData(iter); - data.oldObjects.push_back(std::move(object)); - } + iter->writeStream(fout); - nextStep(data); - //data_.push_back({ curVector,oldVector }); + data.data = fout.str().c_str(); + + oldObjects_.push_back(data); + } } - void UndoManager::nextStep(const Data& data) + + void UndoManager::nextStep(std::vector curData, std::vector oldData) { - if (data_index_ < data_.size()) { + if (curData.empty() && oldData.empty()) + return; - auto iter = data_.begin(); + oldObjects_.clear(); - for (int i = 0; i < data_index_; i++) { - iter++; - } + addBegin(oldData); + addEnd(curData); + + //std::vector data; + //std::vector> curVector, oldVector - data_.erase(iter, data_.end()); + nextStep(oldObjects_); + oldObjects_.clear(); + //data_.push_back({ curVector,oldVector }); + } + void UndoManager::nextStep(const std::vector& data) + { + if (data_index_size_ != data_size_) + { + QSqlQuery query; + query.prepare("delete from model where step >= ?"); + query.addBindValue(data_index_size_ * 2); + query.exec(); } - data_index_++; + execStore(data); - data_.push_back(data); + data_size_ = ++data_index_size_; + + //data_.push_back(data); + func_(); } bool UndoManager::canUndo() { - return data_index_ > 0; + return data_index_size_ > 0; } + bool UndoManager::canRedo() { - return data_index_ < data_.size(); + return data_index_size_ < data_size_; } @@ -127,26 +157,59 @@ namespace acamcad clearModify(); - --data_index_; - - //int index = 0; - auto iter = data_.begin(); - - for (int i = 0; i < data_index_; i++) { - iter++; - } + --data_index_size_; - for (auto cur = iter->curObjects.begin(); cur != iter->curObjects.end(); cur++) { - deleteObject_UndoManage(cur->get()); + QSqlQuery query; + query.prepare("select persistentId from model where step = ?"); + query.addBindValue(2 * data_index_size_ + 1); + if (query.exec()) + { + while (query.next()) + { + deleteObject_UndoManage(query.value(0).toInt()); + } } - for (auto old = iter->oldObjects.begin(); old != iter->oldObjects.end(); old++) + query.prepare("select persistentId from model where step = ?"); + query.addBindValue(2 * data_index_size_); + if (query.exec()) { - addObject_UndoManage(old->get()); + while (query.next()) + { + QSqlQuery query2; + query2.prepare("select persistentId,type,data from model where persistentId = ? and step < ? and step % 2 = 1 order by step desc limit 1"); + query2.addBindValue(query.value(0).toInt()); + query2.addBindValue(2 * data_index_size_); + + if (query2.exec() && query2.next()) + { + AdapterObject* object = new AdapterObject; + object->setPersistentId(query2.value(0).toInt()); + + DataType type = (DataType)query2.value(1).toInt(); + object->setDataType(type); + std::istringstream fin(query2.value(2).toString().toStdString()); + + if (object->readStream(fin)) + { + addObject_UndoManage(object); + } + else + { + delete object; + } + + + } + + ///deleteObject_UndoManage(query.value(0).toInt()); + } } sortObject_UndoManage(); + func_(); + } void UndoManager::Redo() { @@ -155,45 +218,96 @@ namespace acamcad return; } clearModify(); - //int index = 0; - auto iter = data_.begin(); - for (int i = 0; i < data_index_; i++) { - iter++; + QSqlQuery query; + query.prepare("select persistentId from model where step = ?"); + query.addBindValue(2 * data_index_size_); + if (query.exec()) + { + while (query.next()) + { + deleteObject_UndoManage(query.value(0).toInt()); + } } - for (auto cur = iter->curObjects.begin(); cur != iter->curObjects.end(); cur++) { - addObject_UndoManage(cur->get()); - } - for (auto old = iter->oldObjects.begin(); old != iter->oldObjects.end(); old++) + query.prepare("select persistentId,type,data from model where step = ?"); + query.addBindValue(2 * data_index_size_ + 1); + if (query.exec()) { - deleteObject_UndoManage(old->get()); + while (query.next()) + { + + AdapterObject* object = new AdapterObject; + object->setPersistentId(query.value(0).toInt()); + + DataType type = (DataType)query.value(1).toInt(); + object->setDataType(type); + + std::istringstream fin(query.value(2).toString().toStdString()); + + if (object->readStream(fin)) + { + addObject_UndoManage(object); + } + else + { + delete object; + } + + } } + ++data_index_size_; + sortObject_UndoManage(); - ++data_index_; + func_(); } - //void UndoManager::getObject() - std::shared_ptr UndoManager::getObjectInData(AdapterObject* object) + void UndoManager::clearHistory() { - ///std::shared_ptr result; - - for (auto iter = data_.rbegin(); iter != data_.rend(); iter++) + if (data_index_size_ == 0 && data_size_ == 0) { - for (auto& iter1 : iter->curObjects) - { - if (iter1->persistentId() == object->persistentId()) - { - return iter1; - } - } + return; } - std::shared_ptr result = std::make_shared(*object, false); - return result; + + data_index_size_ = data_size_ = 0; + + + QSqlQuery query; + query.prepare("delete from model where step >= ?"); + query.addBindValue(data_index_size_ * 2); + query.exec(); + + + func_(); + } + void UndoManager::setFunc(std::function func) + { + func_ = func; + } + + ////void UndoManager::getObject() + //std::shared_ptr UndoManager::getObjectInData(AdapterObject* object) + //{ + // ///std::shared_ptr result; + + // for (auto iter = data_.rbegin(); iter != data_.rend(); iter++) + // { + // for (auto& iter1 : iter->curObjects) + // { + // if (iter1->persistentId() == object->persistentId()) + // { + // return iter1; + // } + // } + // } + // std::shared_ptr result = std::make_shared(*object, false); + // return result; + //} + } \ No newline at end of file diff --git a/Src/Core/UndoManage.h b/Src/Core/UndoManage.h index 18adb30c01e40148d1143581e1c9c73a0bd80d2e..f0357124709248b9cc5b538376cca55064075364 100644 --- a/Src/Core/UndoManage.h +++ b/Src/Core/UndoManage.h @@ -19,15 +19,17 @@ #include #include - #include "../Object/ObjectDefine.h" +#include "LocalDataManager.h" + +//class RibbonViewer; namespace acamcad { //class AdapterObject - class UndoManager + class UndoManager :public LocalDataManager { public: UndoManager(); @@ -55,32 +57,41 @@ namespace acamcad void Undo(); void Redo(); + void clearHistory(); + + void setFunc(std::function func); + protected: + void addBegin(const std::vector& oldData); + void addEnd(const std::vector& curData); + virtual void addObject_UndoManage(AdapterObject* object) = 0; - virtual void deleteObject_UndoManage(AdapterObject* object) = 0; + virtual void deleteObject_UndoManage(int persistentId) = 0; virtual void sortObject_UndoManage() = 0; private: - struct Data - { - ///DataType type; - std::list> curObjects; - std::list> oldObjects; - }; + //struct Data + //{ + // ///DataType type; + // std::list> curObjects; + // std::list> oldObjects; + //}; void nextStep(std::vector curData, std::vector oldData); - void nextStep(const Data& data); + void nextStep(const std::vector& data); - std::shared_ptr getObjectInData(AdapterObject* object); + //std::shared_ptr getObjectInData(AdapterObject* object); protected: - std::list data_; - int data_index_; + //std::list data_; + int data_index_size_; + int data_size_; - std::list> oldObjects_; + std::vector oldObjects_; + std::function func_; //std::vector> auto_olds; //std::vector> auto_news; diff --git a/Src/Core/UndoRedoHandler.cpp b/Src/Core/UndoRedoHandler.cpp deleted file mode 100644 index 0143b756752f84ccc27d03bba7848371c68e9741..0000000000000000000000000000000000000000 --- a/Src/Core/UndoRedoHandler.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. -* All rights reserved. -* -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this -* software under the terms of either license. -* -* For more information about the AGPLv3.0 license, please visit: -* https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please -* contact Hefei Jiushao Intelligent Technology Co., Ltd. -* =================================================================== -* Author: -*/ -#include "UndoRedoHandler.h" -#include "../Object/BRepObject.h" -#include "DataManager.h" -#include -#include "../Object/ObjectType.h" -#include - -namespace acamcad { - - ContextHolder::ContextHolder(int opId) : operationId_(opId), objectPid_(-1) { - backshape_.Nullify(); - } - - ContextHolder::ContextHolder(int opId, BaseObject* object, ActionType actionType) : operationId_(opId), objectPid_(-1) { - if (nullptr == object || object->dataType() != DataType::BREP_TYPE) { - return; - } - - objectPid_ = object->persistentId(); - - AdapterObject* object_old = dynamic_cast(object); - - //BRepObject* object_old = dynamic_cast (object); - backshape_ = AMCAX::CopyShape(object_old->bRep->getShape()); - } - - ContextHolder::~ContextHolder() - { - backshape_.Nullify(); - } - - - UndoRedoHandler::UndoRedoHandler(DataManager* dataManager, int max_queue_size) : dataManager_(dataManager), max_queue_size_(max_queue_size), operation_seq_(0) - { - } - - UndoRedoHandler::~UndoRedoHandler() { - while (!undo_queue_.empty()) { - OperateContext item = undo_queue_.back(); - undo_queue_.pop_back(); - releaseQueueItem(item); - } - - while (!redo_queue_.empty()) { - OperateContext item = redo_queue_.back(); - redo_queue_.pop_back(); - releaseQueueItem(item); - } - } - - void UndoRedoHandler::increaseSequence() - { - operation_seq_++; - } - - void UndoRedoHandler::recordOperation(BaseObject* object, ActionType actionType, bool incSeq) - { - if (incSeq) { - operation_seq_++; - } - - ContextHolder* holder = new ContextHolder(operation_seq_, object, actionType); - OperateContext context = std::make_pair(actionType, holder); - undo_queue_.push_back(context); - - if (undo_queue_.size() >= max_queue_size_) { - OperateContext item = undo_queue_.back(); - undo_queue_.pop_front(); - releaseQueueItem(item); - } - } - - void UndoRedoHandler::revokeOp(const OperateContext& context) - { - ActionType op_type = context.first; - ContextHolder* p_holder = context.second; - - if (p_holder == nullptr) { - return; - } - - if (op_type == ActionType::AddObject) { //revoke add, need to find the object in data manager and delete it. - std::cout << "undo add object" << std::endl; - - BaseObject* obj = dataManager_->getObjectByPersistentID(p_holder->getObjectPid()); - if (obj == nullptr) { - //Object gone, cannot revoke add operation; - return; - } - //dataManager_->deleteObject(obj); - } - else if (op_type == ActionType::DeleteObject) { // revoke delete, need to restore the object from shape and add into data manager; and add another copy - std::cout << "undo delete object" << std::endl; - - BaseObject* obj = dataManager_->getObjectByPersistentID(p_holder->getObjectPid()); - if (obj != nullptr) { - return; //Object already exists, no need to revoke delete now. - } - //ONLY support bRep object now; - AdapterObject* adapter_object = new AdapterObject; - adapter_object->setDataType(acamcad::DataType::BREP_TYPE); - //adapter_object->bRep = std::make_unique(adapter_object); - - //BRepObject* brep_obj = new BRepObject(); - adapter_object->bRep->applyBRep(p_holder->getBackShape()); - dataManager_->addObject(adapter_object); - adapter_object->setPersistentId(p_holder->getObjectPid()); - adapter_object->bRep->updateDraw(); - } - else { //revoke operate, need to find the backup data and the object in data manager, apply the old data to the object; - - } - } - - void UndoRedoHandler::applyOp(const OperateContext& context) - { - ActionType actionType = context.first; - ContextHolder* p_holder = context.second; - - if (p_holder == nullptr) { - return; - } - - if (actionType == ActionType::AddObject) { //redo add, need to restore the object from shape and add into data manager; - std::cout << "redo add object" << std::endl; - BaseObject* obj = dataManager_->getObjectByPersistentID(p_holder->getObjectPid()); - if (obj != nullptr) { - return; //Object already exists, no need to revoke delete now. - } - //ONLY support bRep object now; - AdapterObject* adapter_object = new AdapterObject; - adapter_object->setDataType(acamcad::DataType::BREP_TYPE); - //adapter_object->bRep = std::make_unique(adapter_object); - - ///BRepObject* brep_obj = new BRepObject(); - adapter_object->bRep->applyBRep(p_holder->getBackShape()); - dataManager_->addObject(adapter_object); - adapter_object->setPersistentId(p_holder->getObjectPid()); - adapter_object->bRep->updateDraw(); - } - else if (actionType == ActionType::DeleteObject) { //redo delete, need to find the object in data manager and delete it. - std::cout << "redo delete object" << std::endl; - - BaseObject* obj = dataManager_->getObjectByPersistentID(p_holder->getObjectPid()); - if (obj == nullptr) { - //Object gone, cannot revoke add operation; - return; - } - //dataManager_->deleteObject(obj); - } - - } - - void UndoRedoHandler::undo() - { - if (undo_queue_.empty()) { - return; - } - - bool hasNext = false; - do { - OperateContext context = undo_queue_.back(); - int currentSeq = (context.second)->getOperationId(); - revokeOp(context); - - undo_queue_.pop_back(); - redo_queue_.push_back(context); - if (redo_queue_.size() >= max_queue_size_) { - - OperateContext item = redo_queue_.front(); - redo_queue_.pop_front(); - releaseQueueItem(item); - } - - if (undo_queue_.empty()) { - return; - } - OperateContext next_context = undo_queue_.back(); - int nextSeq = (next_context.second)->getOperationId(); - hasNext = (nextSeq == currentSeq); - } while (hasNext); - - } - - void UndoRedoHandler::redo() - { - - if (redo_queue_.empty()) { - return; - } - - bool hasNext = false; - do { - OperateContext context = redo_queue_.back(); - int currentSeq = (context.second)->getOperationId(); - applyOp(context); - - redo_queue_.pop_back(); - undo_queue_.push_back(context); - if (undo_queue_.size() >= max_queue_size_) { - - OperateContext item = undo_queue_.front(); - undo_queue_.pop_front(); - releaseQueueItem(item); - } - - if (redo_queue_.empty()) { - return; - } - OperateContext next_context = redo_queue_.back(); - int nextSeq = (next_context.second)->getOperationId(); - hasNext = (nextSeq == currentSeq); - } while (hasNext); - } - - bool UndoRedoHandler::canUndo() - { - return (!undo_queue_.empty()); - } - - bool UndoRedoHandler::canRedo() - { - return (!redo_queue_.empty()); - } - - void UndoRedoHandler::releaseQueueItem(OperateContext& context) - { - if (context.second != nullptr) { - delete context.second; - context.second = nullptr; - } - } -} \ No newline at end of file diff --git a/Src/Main.cpp b/Src/Main.cpp index 621fa981f8491dab8a9af215f624884df6524686..b432e49cba597013010f77ed1bf9d5dd46153c5b 100644 --- a/Src/Main.cpp +++ b/Src/Main.cpp @@ -1,38 +1,53 @@ /* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. +* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. * All rights reserved. * -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this +* This software is licensed under the GNU Affero General Public License +* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this * software under the terms of either license. * -* For more information about the AGPLv3.0 license, please visit: +* For more information about the AGPLv3.0 license, please visit: * https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please +* For licensing inquiries or to obtain a commercial license, please * contact Hefei Jiushao Intelligent Technology Co., Ltd. * =================================================================== -* Author: +* Author: */ -#include "Windows/ACAMWindow.h" #include #include #include +#include "Windows/RibbonViewer.h" +#include "SARibbonBar.h" +#include +#include int main(int argc, char** argv) { - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -// QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Round); - // Read command lines arguments. + + // 仅仅允许一个进程 + QSharedMemory singleton("AmcaxDaemon-SINGLETON"); + if (!singleton.create(1)) { + return -1; + } + + + SARibbonBar::initHighDpi(); + + QApplication application(argc, argv); + QFont f = application.font(); + f.setFamily(u8"微软雅黑"); + application.setFont(f); // Instantiate the viewer. - ACAMWindow window; - - window.setWindowTitle("九韶精灵"); + RibbonViewer window; + window.setRibbonTheme(SARibbonMainWindow::RibbonThemeOffice2016Blue); + //window.setWindowTitle("九韶精灵"); + //ndow.initFile(); // Make the viewer window visible on screen. - window.show(); + //window.showFullScreen(); // Run main loop. - return application.exec(); + return (application.exec()); } diff --git a/Src/Object/AdapterObject.cpp b/Src/Object/AdapterObject.cpp index 61693e35d51a1182d7e9794d3c2c22610107c423..01eb6e3d3f3125c192f2187998238713caf3832a 100644 --- a/Src/Object/AdapterObject.cpp +++ b/Src/Object/AdapterObject.cpp @@ -8,6 +8,11 @@ #include #include #include +#include +#include +#include +#include +#include namespace acamcad { @@ -267,7 +272,7 @@ namespace acamcad break; case acamcad::DataType::MESH_TYPE: { - auto box = AMCAX::SubD::MeshTool::MeshBoundingBox(mesh->mesh()); + auto box = AMCAX::SubD::MeshTool::MeshBoundingBox(mesh->getShape()); min = box.CornerMin().Coord(); max = box.CornerMax().Coord(); @@ -276,7 +281,7 @@ namespace acamcad case acamcad::DataType::TSPLINEU_TYPE: { AMCAX::BoundingBox3 box; - AMCAX::TMS::TMSplineTool::UpdateTMSplineBndBox(tSpline->getTSpline(), box); + AMCAX::TMS::TMSplineTool::UpdateTMSplineBndBox(tSpline->getShape(), box); min = box.CornerMin().Coord(); max = box.CornerMax().Coord(); @@ -301,12 +306,12 @@ namespace acamcad break; case acamcad::DataType::MESH_TYPE: { - auto vs = AMCAX::SubD::MeshTool::FaceVertexIndexs(mesh->mesh(), face_id); + auto vs = AMCAX::SubD::MeshTool::FaceVertexIndexs(mesh->getShape(), face_id); double x_min(0), y_min(0), z_min(0), x_max(0), y_max(0), z_max(0); for (int i = 0; i < vs.size(); i++) { - auto& p = AMCAX::SubD::MeshTool::Position(mesh->mesh(), vs[i]); + auto& p = AMCAX::SubD::MeshTool::Position(mesh->getShape(), vs[i]); if (i == 0) { @@ -336,12 +341,12 @@ namespace acamcad break; case acamcad::DataType::TSPLINEU_TYPE: { - auto ids = AMCAX::TMS::TMSplineTool::FaceVertexIndexs(tSpline->getTSpline(), face_id); + auto ids = AMCAX::TMS::TMSplineTool::FaceVertexIndexs(tSpline->getShape(), face_id); double x_min(0), y_min(0), z_min(0), x_max(0), y_max(0), z_max(0); for (int i = 0; i < ids.size(); i++) { - auto& p = AMCAX::TMS::TMSplineTool::ControlPosition(tSpline->getTSpline(), ids[i]); + auto& p = AMCAX::TMS::TMSplineTool::ControlPosition(tSpline->getShape(), ids[i]); if (i == 0) { x_min = x_max = p.X(); @@ -385,9 +390,9 @@ namespace acamcad case acamcad::DataType::MESH_TYPE: { int v0, v1; - AMCAX::SubD::MeshTool::EdgeVertexIndexs(mesh->mesh(), edge_id, v0, v1); - auto& p0 = AMCAX::SubD::MeshTool::Position(mesh->mesh(), v0), - & p1 = AMCAX::SubD::MeshTool::Position(mesh->mesh(), v1); + AMCAX::SubD::MeshTool::EdgeVertexIndexs(mesh->getShape(), edge_id, v0, v1); + auto& p0 = AMCAX::SubD::MeshTool::Position(mesh->getShape(), v0), + & p1 = AMCAX::SubD::MeshTool::Position(mesh->getShape(), v1); min.SetCoord(std::min(p0.X(), p1.X()), std::min(p0.Y(), p1.Y()), std::min(p0.Z(), p1.Z())); max.SetCoord(std::max(p0.X(), p1.X()), std::max(p0.Y(), p1.Y()), std::max(p0.Z(), p1.Z())); @@ -400,9 +405,9 @@ namespace acamcad case acamcad::DataType::TSPLINEU_TYPE: { int v0, v1; - AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(tSpline->getTSpline(), edge_id, v0, v1); - auto p0 = AMCAX::TMS::TMSplineTool::ControlPosition(tSpline->getTSpline(), v0), - p1 = AMCAX::TMS::TMSplineTool::ControlPosition(tSpline->getTSpline(), v1); + AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(tSpline->getShape(), edge_id, v0, v1); + auto p0 = AMCAX::TMS::TMSplineTool::ControlPosition(tSpline->getShape(), v0), + p1 = AMCAX::TMS::TMSplineTool::ControlPosition(tSpline->getShape(), v1); min.SetCoord(std::min(p0.X(), p1.X()), std::min(p0.Y(), p1.Y()), std::min(p0.Z(), p1.Z())); @@ -430,14 +435,14 @@ namespace acamcad break; case acamcad::DataType::MESH_TYPE: { - auto& p = AMCAX::SubD::MeshTool::Position(mesh->mesh(), vetex_id); + auto& p = AMCAX::SubD::MeshTool::Position(mesh->getShape(), vetex_id); position = p.Coord(); } break; case acamcad::DataType::TSPLINEU_TYPE: { - auto& p = AMCAX::TMS::TMSplineTool::ControlPosition(tSpline->getTSpline(), vetex_id); + auto& p = AMCAX::TMS::TMSplineTool::ControlPosition(tSpline->getShape(), vetex_id); position = p.Coord(); } break; @@ -500,5 +505,126 @@ namespace acamcad tSpline->updateDraw(); } + bool AdapterObject::writeStream(std::ostringstream& out) + { + bool bResult = false; + switch (dataType()) + { + case DataType::BREP_TYPE: + { + if (!bRep->getShape().IsNull()) + { + bResult = true; + AMCAX::ShapeTool::Write(bRep->getShape(), out); + } + //data.data = fout.str().c_str(); + + + } + break; + case DataType::MESH_TYPE: + { + bResult = AMCAX::SubD::PolyMeshIO::WriteMeshStreamOBJ(out, mesh->getShape()); + + } + break; + case DataType::TSPLINEU_TYPE: + { + AMCAX::TMS::TMSplineIO io; + + bResult = io.WriteTMSplineStream(out, tSpline->getShape()); + } + break; + default: + break; + } + + return bResult; + } + + + bool AdapterObject::readStream(std::istringstream& in) + { + bool bResult = false; + + switch (dataType()) + { + case DataType::BREP_TYPE: + { + //std::istringstream fin(query2.value(2).toString().toStdString()); + AMCAX::TopoShape shape; + AMCAX::ShapeTool::Read(shape, in); + + if (shape.IsNull()) + { + break; + } + + bResult = true; + + bRep->SetShape(shape); + } + break; + case DataType::MESH_TYPE: + { + mesh->assignShape(); + //std::istringstream fin(query2.value(2).toString().toStdString()); + bResult = AMCAX::SubD::PolyMeshIO::LoadMeshStreamOBJ(in, mesh->getShape()); + } + break; + case DataType::TSPLINEU_TYPE: + { + tSpline->assignShape(); + + //std::istringstream fin(query2.value(2).toString().toStdString()); + + AMCAX::TMS::TMSplineIO io; + bResult = io.LoadTMSplineStream(in, tSpline->getShape()); + } + break; + default: + break; + } + + return bResult; + } + + AdapterObject* AdapterObject::duplicateFaces(std::vector& vec) + { + std::sort(vec.begin(), vec.end(), std::greater()); + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + + AdapterObject* object = nullptr; + + switch (dataType()) + { + case DataType::MESH_TYPE: + { + object = new AdapterObject; + object->setDataType(dataType()); + + object->mesh->setShape(AMCAX::SubD::MeshCopy::ExtractMeshFaces(mesh->getShape(), + vec)); + + } + break; + case DataType::TSPLINEU_TYPE: + { + object = new AdapterObject; + object->setDataType(dataType()); + + AMCAX::TMS::TMSplineCopy tool; + + object->tSpline->setShape(tool.ExtractTMSplineFace(tSpline->getShape(), + vec)); + } + break; + default: + break; + } + + return object; + } + } \ No newline at end of file diff --git a/Src/Object/AdapterObject.h b/Src/Object/AdapterObject.h index 622f124a9eb4b77639bf55f03d230c2592d9802f..07c3420bea1be71a09f2be8153a09f4346997422 100644 --- a/Src/Object/AdapterObject.h +++ b/Src/Object/AdapterObject.h @@ -18,6 +18,9 @@ #include "BaseObject.h" #include +#include +#include +#include #include #include @@ -52,7 +55,6 @@ namespace acamcad virtual void mesh_OPvert_Backup(); virtual void meshMove(const AMCAX::Coord3& v, double dis); - //在有了原来的顶点之后,所有的缩放都是一样的,没有体,边,面,点的区别了。 void meshScaleSingle(const AMCAX::Coord3& center, const AMCAX::Coord3& axis, double scale); void meshScalePlane(const AMCAX::Coord3& center, const AMCAX::Coord3& normal, double scale); void meshRotation(const AMCAX::Coord3& center, const AMCAX::Coord3& axis, double angle); @@ -73,6 +75,11 @@ namespace acamcad virtual void updateDraw() override; const virtual ObjectDraw* getDrawTool() const override; + bool writeStream(std::ostringstream& out); + bool readStream(std::istringstream& in); + + AdapterObject* duplicateFaces(std::vector& vec); + public: std::unique_ptr bRep; std::unique_ptr mesh; diff --git a/Src/Object/BRepObject.cpp b/Src/Object/BRepObject.cpp index bb5c118e9c62893dee2c65c4876fb2aff2ec2ad4..98639b2a37ad3626dfb34d0415e645ca053f7644 100644 --- a/Src/Object/BRepObject.cpp +++ b/Src/Object/BRepObject.cpp @@ -219,10 +219,11 @@ namespace acamcad AMCAX::Transformation3 tr; tr.SetMirror(AMCAX::Frame3(P, v)); - AMCAX::TransformShape trans(brep_trans_back_, tr); + AMCAX::TransformShape trans(shape_, tr); shape_ = trans.Shape(); - + updateShapeInfo(); + draw_tool_->updatedrawState(); //BRepObject* brep_object_new = new BRepObject(); //brep_object_new->shape_ = trans.Shape(); //brep_object_new->updateDraw(); diff --git a/Src/Object/MeshObject.cpp b/Src/Object/MeshObject.cpp index 27f36ebf34f39cb9289903e065a79f5f2bb3a311..6d983c51a40afee835d27ad2b46e510a87339cfc 100644 --- a/Src/Object/MeshObject.cpp +++ b/Src/Object/MeshObject.cpp @@ -1,12 +1,10 @@ #include "MeshObject.h" #include -//#include +// #include #include "../ObjectDraw/MeshObjectDraw.h" #include "../Operation/MOperate.h" - - #include #include #include @@ -23,32 +21,33 @@ #include "../Utils/MathUtils.h" -//#include -//#include +// #include +// #include -namespace acamcad { +namespace acamcad +{ - MeshObject::MeshObject(AdapterObject* parent) : - AdapterObjectParent(parent), mesh_(nullptr)//, mesh_subdive_(nullptr) + MeshObject::MeshObject(AdapterObject *parent) : AdapterObjectParent(parent) //, mesh_(nullptr)//, mesh_subdive_(nullptr) { - ///setDataType(DataType::MESH_TYPE); + /// setDataType(DataType::MESH_TYPE); draw_tool_ = std::make_unique(parent_); } - MeshObject::MeshObject(AdapterObject* parent, const MeshObject& object, bool isDraw) : - AdapterObjectParent(parent), mesh_(nullptr)//, mesh_subdive_(nullptr) + MeshObject::MeshObject(AdapterObject *parent, const MeshObject &object, bool isDraw) : AdapterObjectParent(parent) //, mesh_(nullptr)//, mesh_subdive_(nullptr) { if (object.mesh_ != nullptr) { if (object.mesh_ != nullptr) { - //mesh_ = new polymesh::PolyMesh(); - mesh_ = AMCAX::SubD::MeshCopy::CopyMesh(object.mesh_); + // mesh_ = new polymesh::PolyMesh(); + mesh_.reset( + AMCAX::SubD::MeshCopy::CopyMesh(object.getShape())); } } - //setDataType(DataType::MESH_TYPE); - if (isDraw) { + // setDataType(DataType::MESH_TYPE); + if (isDraw) + { draw_tool_ = std::make_unique(parent_); draw_tool_->updatedrawState(); } @@ -56,131 +55,80 @@ namespace acamcad { MeshObject::~MeshObject() { - if (mesh_ != nullptr) - { - mesh_->clear(); - delete mesh_; - } - } - - void MeshObject::SetMesh(polymesh::PolyMesh* mesh) - { - mesh_ = mesh; + // if (mesh_ != nullptr) + //{ + // mesh_->clear(); + // delete mesh_; + // } } - polymesh::PolyMesh* MeshObject::mesh() + void MeshObject::setShape(polymesh::PolyMesh *mesh) { - return mesh_; + mesh_.reset(mesh); } - const polymesh::PolyMesh* MeshObject::mesh() const + polymesh::PolyMesh *MeshObject::getShape() { - return mesh_; + return mesh_.get(); } - bool MeshObject::loadMesh(QString _filename) + const polymesh::PolyMesh *MeshObject::getShape() const { - parent_->setFromFileName(_filename); - - // call the local function to update names - parent_->setName(parent_->name()); - - std::string filename = _filename.toStdString(); - - // load file - bool ok = polymesh::PolyMeshIO::LoadMesh(filename, mesh_); - if (ok) - { - ///mesh_->updateMeshNormal(); - - parent_->show(); - } - else - { - std::cout << "Load File fall" << std::endl; - } - - return ok; - } - - bool MeshObject::saveMesh(QString _filename) - { - std::string filename = _filename.toStdString(); - - bool ok = polymesh::PolyMeshIO::WriteMesh(filename, mesh_); - - if (!ok) - { - std::cout << "Save File fall" << std::endl; - } - return ok; - } - - bool MeshObject::saveTSpline(QString _filename) - { - std::string filename = _filename.toStdString(); - - bool ok = polymesh::PolyMeshIO::WriteToTMSpline(filename, mesh_); - - if (!ok) - std::cout << "Save File TSpline failure!" << std::endl; - - return ok; + return mesh_.get(); } //=========================================================== - void MeshObject::doOperate(MOperation* operate) + void MeshObject::doOperate(MOperation *operate) { operate->operate(parent_); } //=========================================================== - void MeshObject::assignMesh() + void MeshObject::assignShape() { if (mesh_ == nullptr) - mesh_ = new polymesh::PolyMesh(); + mesh_ = std::make_unique(); // new polymesh::PolyMesh(); } //=========================================================== - bool MeshObject::combineObject(const MeshObject* mesh_object) + bool MeshObject::combineObject(const MeshObject *mesh_object) { if (mesh_ == nullptr) return false; if (mesh_object->mesh_ == nullptr) return false; - polymesh::MeshCopy::CombineMesh(mesh_, mesh_object->mesh_); + polymesh::MeshCopy::CombineMesh(getShape(), mesh_object->getShape()); return true; } - void MeshObject::duplicateFaces(MeshObject* mesh_object, std::vector& f_list) - { - std::sort(f_list.begin(), f_list.end(), std::greater()); - f_list.erase(std::unique(f_list.begin(), f_list.end()), f_list.end()); - - if (mesh_object->mesh_ == nullptr) - mesh_object->mesh_ = new polymesh::PolyMesh(); + // void MeshObject::duplicateFaces(MeshObject* mesh_object, std::vector& f_list) + //{ + // std::sort(f_list.begin(), f_list.end(), std::greater()); + // f_list.erase(std::unique(f_list.begin(), f_list.end()), f_list.end()); - //polymesh::copyMeshFaces(mesh_, mesh_object->mesh_, f_list); - mesh_object->mesh_ = polymesh::MeshCopy::ExtractMeshFaces(mesh_, f_list); - } + // if (mesh_object->mesh_ == nullptr) + // mesh_object->mesh_ = std::make_unique(); + // //polymesh::copyMeshFaces(mesh_, mesh_object->mesh_, f_list); + // mesh_object->mesh_.reset(polymesh::MeshCopy::ExtractMeshFaces(getShape(), f_list)); + //} - void MeshObject::mirrorMeshObject(const MVector3& center, const MVector3& normal) + void MeshObject::mirrorMeshObject(const MVector3 ¢er, const MVector3 &normal) { - //if (mesh_ == nullptr) + // if (mesh_ == nullptr) // return nullptr; - //AMCAX::SubD::PolyMesh* copy_mesh = AMCAX::SubD::MeshCopy::CopyMesh(mesh_); + // AMCAX::SubD::PolyMesh* copy_mesh = AMCAX::SubD::MeshCopy::CopyMesh(mesh_); - //AdapterObject* adapter_object = new AdapterObject; - //MeshObject* mesh_object_new = new MeshObject(adapter_object); - //mesh_object_new->mesh_ = AMCAX::SubD::MeshCopy::CopyMesh(mesh_); - ///mesh_object_new->mesh_ = polymesh::meshMioor(mesh_, center.point(), normal); + // AdapterObject* adapter_object = new AdapterObject; + // MeshObject* mesh_object_new = new MeshObject(adapter_object); + // mesh_object_new->mesh_ = AMCAX::SubD::MeshCopy::CopyMesh(mesh_); + /// mesh_object_new->mesh_ = polymesh::meshMioor(mesh_, center.point(), normal); AMCAX::Point3 ct(center.X(), center.Y(), center.Z()); AMCAX::Frame3 ft(ct, AMCAX::Direction3(normal)); @@ -188,169 +136,173 @@ namespace acamcad { AMCAX::Transformation3 trsfMove; trsfMove.SetMirror(ft); trsfF.SetTransformation(trsfMove); - trsfF.TranformMesh(mesh_); + trsfF.TranformMesh(getShape()); - - //return mesh_object_new; + // return mesh_object_new; } //=========================================================== void MeshObject::mesh_OPvert_Backup() { - //AMCAX::SubD::Mes + // AMCAX::SubD::Mes scale_ = 1; mesh_v_id_back_.resize(mesh_->numVertices()); - //mesh_v_position_back_.resize(mesh_->numVertices()); + // mesh_v_position_back_.resize(mesh_->numVertices()); for (int i = 0; i < mesh_->numVertices(); i++) { mesh_v_id_back_[i] = i; - //mesh_v_position_back_[i] = AMCAX::SubD::MeshTool::Position(mesh_, i);// mesh_->vert(i)->position(); + // mesh_v_position_back_[i] = AMCAX::SubD::MeshTool::Position(mesh_, i);// mesh_->vert(i)->position(); } } - void MeshObject::mesh_OPvert_BackupVertes(const std::vector& vert_id) + void MeshObject::mesh_OPvert_BackupVertes(const std::vector &vert_id) { mesh_v_id_back_ = vert_id; scale_ = 1; - //mesh_v_position_back_.resize(vert_id.size()); - //for (int i = 0; i < vert_id.size(); i++) + // mesh_v_position_back_.resize(vert_id.size()); + // for (int i = 0; i < vert_id.size(); i++) //{ // mesh_v_position_back_[i] = AMCAX::SubD::MeshTool::Position(mesh_, i);//mesh_->vert(vert_id[i])->position(); - //} + // } } - void MeshObject::mesh_OPvert_BackupEdges(const std::vector& edge_id) + void MeshObject::mesh_OPvert_BackupEdges(const std::vector &edge_id) { scale_ = 1; - std::vector vert_id; vert_id.clear(); vert_id.reserve(edge_id.size() * 2); + std::vector vert_id; + vert_id.clear(); + vert_id.reserve(edge_id.size() * 2); for (size_t i = 0; i < edge_id.size(); i++) { - //polymesh::MEdge* e = mesh_->edge(edge_id[i]); - //vert_id.push_back(e->getVert(0)->index()); - //vert_id.push_back(e->getVert(1)->index()); + // polymesh::MEdge* e = mesh_->edge(edge_id[i]); + // vert_id.push_back(e->getVert(0)->index()); + // vert_id.push_back(e->getVert(1)->index()); int id0, id1; - AMCAX::SubD::MeshTool::EdgeVertexIndexs(mesh_, edge_id[i], id0, id1); + AMCAX::SubD::MeshTool::EdgeVertexIndexs(getShape(), edge_id[i], id0, id1); vert_id.push_back(id0); vert_id.push_back(id1); - } std::sort(vert_id.begin(), vert_id.end()); vert_id.erase(std::unique(vert_id.begin(), vert_id.end()), vert_id.end()); mesh_v_id_back_ = vert_id; - //mesh_v_position_back_.resize(vert_id.size()); - //for (int i = 0; i < vert_id.size(); i++) + // mesh_v_position_back_.resize(vert_id.size()); + // for (int i = 0; i < vert_id.size(); i++) //{ // mesh_v_position_back_[i] = AMCAX::SubD::MeshTool::Position(mesh_, i);//mesh_->vert(vert_id[i])->position(); - //} + // } } - void MeshObject::mesh_OPvert_BackupFaces(const std::vector& face_id) + void MeshObject::mesh_OPvert_BackupFaces(const std::vector &face_id) { scale_ = 1; - std::vector vert_id; vert_id.clear(); vert_id.reserve(face_id.size() * 4); + std::vector vert_id; + vert_id.clear(); + vert_id.reserve(face_id.size() * 4); for (size_t i = 0; i < face_id.size(); i++) { - auto ids = AMCAX::SubD::MeshTool::FaceVertexIndexs(mesh_, face_id[i]); + auto ids = AMCAX::SubD::MeshTool::FaceVertexIndexs(getShape(), face_id[i]); - for (auto iter : ids) { + for (auto iter : ids) + { vert_id.push_back(iter); } - } std::sort(vert_id.begin(), vert_id.end()); vert_id.erase(std::unique(vert_id.begin(), vert_id.end()), vert_id.end()); mesh_v_id_back_ = vert_id; - //mesh_v_position_back_.resize(vert_id.size()); - //for (int i = 0; i < vert_id.size(); i++) + // mesh_v_position_back_.resize(vert_id.size()); + // for (int i = 0; i < vert_id.size(); i++) //{ // mesh_v_position_back_[i] = AMCAX::SubD::MeshTool::Position(mesh_, i); - //} + // } } void MeshObject::mesh_Clear_OPvertBackup() { mesh_v_id_back_.clear(); - //mesh_v_position_back_.clear(); + // mesh_v_position_back_.clear(); scale_ = 1; - AMCAX::SubD::MeshTool::UpdateNormal(mesh_); + AMCAX::SubD::MeshTool::UpdateNormal(getShape()); } - void MeshObject::meshScaleSingle(const MVector3& center, const MVector3& axis, double scale) + void MeshObject::meshScaleSingle(const MVector3 ¢er, const MVector3 &axis, double scale) { scale_ = scale / scale_; - if (std::isnan(scale_)) { + if (std::isnan(scale_)) + { scale_ = 1; return; } - ///std::cout << "meshScaleSingle center:(" << center.X() << "," << center.Y() << "," << center.Z() << ") scale:" << scale << std::endl; + /// std::cout << "meshScaleSingle center:(" << center.X() << "," << center.Y() << "," << center.Z() << ") scale:" << scale << std::endl; AMCAX::Point3 point(center.X(), center.Y(), center.Z()); AMCAX::Direction3 dir(axis.X(), axis.Y(), axis.Z()); - //AMCAX::Axis3 + // AMCAX::Axis3 AMCAX::Frame3 ct(point, dir); - //AMCAX::Frame3 ft(ct, AMCAX::Direction3(normal)); + // AMCAX::Frame3 ft(ct, AMCAX::Direction3(normal)); AMCAX::SubD::MeshTransform trsfF; - //AMCAX::Transformation3 trfScale; + // AMCAX::Transformation3 trfScale; AMCAX::GeneralTransformation3 trfScale; - //trfRotation.SetRotation(AMCAX::Axis3(ct, AMCAX::Direction3(axis.X(), axis.Y(), axis.Z())), angle); - //trfScale.SetScale(ct, scale); - //trfScale.Set + // trfRotation.SetRotation(AMCAX::Axis3(ct, AMCAX::Direction3(axis.X(), axis.Y(), axis.Z())), angle); + // trfScale.SetScale(ct, scale); + // trfScale.Set // trsfF.SetGTransformation(trfScale); trfScale.SetAffinity(ct, scale_); trsfF.SetGTransformation(trfScale); - trsfF.TransformMeshVertices(mesh_, mesh_v_id_back_); + trsfF.TransformMeshVertices(getShape(), mesh_v_id_back_); scale_ = scale; } - void MeshObject::meshScalePlane(const MVector3& center, const MVector3& normal, double scale) + void MeshObject::meshScalePlane(const MVector3 ¢er, const MVector3 &normal, double scale) { - ///std::cout << "meshScalePlane is error,scale:" << scale << std::endl; + /// std::cout << "meshScalePlane is error,scale:" << scale << std::endl; scale_ = scale / scale_; - if (std::isnan(scale_)) { + if (std::isnan(scale_)) + { scale_ = 1; return; } AMCAX::Point3 point(center.X(), center.Y(), center.Z()); AMCAX::Direction3 dir(normal.X(), normal.Y(), normal.Z()); - //AMCAX::Axis3 + // AMCAX::Axis3 AMCAX::Axis3 ct(point, dir); - //AMCAX::Frame3 ft(ct, AMCAX::Direction3(normal)); + // AMCAX::Frame3 ft(ct, AMCAX::Direction3(normal)); AMCAX::SubD::MeshTransform trsfF; - //AMCAX::Transformation3 trfScale; + // AMCAX::Transformation3 trfScale; AMCAX::GeneralTransformation3 trfScale; - //trfRotation.SetRotation(AMCAX::Axis3(ct, AMCAX::Direction3(axis.X(), axis.Y(), axis.Z())), angle); - //trfScale.SetScale(ct, scale); - //trfScale.Set + // trfRotation.SetRotation(AMCAX::Axis3(ct, AMCAX::Direction3(axis.X(), axis.Y(), axis.Z())), angle); + // trfScale.SetScale(ct, scale); + // trfScale.Set // trsfF.SetGTransformation(trfScale); trfScale.SetAffinity(ct, scale_); trsfF.SetGTransformation(trfScale); - trsfF.TransformMeshVertices(mesh_, mesh_v_id_back_); + trsfF.TransformMeshVertices(getShape(), mesh_v_id_back_); scale_ = scale; } - void MeshObject::meshRotation(const MVector3& center, const MVector3& axis, double angle) + void MeshObject::meshRotation(const MVector3 ¢er, const MVector3 &axis, double angle) { - //std::cout << "MeshObject::meshRotation angle:" << angle << std::endl; + // std::cout << "MeshObject::meshRotation angle:" << angle << std::endl; AMCAX::Point3 ct(center.X(), center.Y(), center.Z()); - //AMCAX::Frame3 ft(ct, AMCAX::Direction3(normal)); + // AMCAX::Frame3 ft(ct, AMCAX::Direction3(normal)); AMCAX::SubD::MeshTransform trsfF; AMCAX::Transformation3 trfRotation; trfRotation.SetRotation(AMCAX::Axis3(ct, AMCAX::Direction3(axis.X(), axis.Y(), axis.Z())), angle); trsfF.SetTransformation(trfRotation); - trsfF.TransformMeshVertices(mesh_, mesh_v_id_back_); + trsfF.TransformMeshVertices(getShape(), mesh_v_id_back_); /* @@ -363,7 +315,6 @@ namespace acamcad { */ - } void MeshObject::meshMoveUsingNormal(double dis) @@ -371,65 +322,63 @@ namespace acamcad { std::cout << "MeshObject::meshMoveUsingNormal" << std::endl; } - void MeshObject::meshMove(const MVector3& v, double dis) + void MeshObject::meshMove(const MVector3 &v, double dis) { AMCAX::Vector3 vh(v.Normalized() * dis); AMCAX::Transformation3 trshMove; - //AMCAX::Axis3 ax; + // AMCAX::Axis3 ax; trshMove.SetTranslation(vh); AMCAX::SubD::MeshTransform trsf; trsf.SetTransformation(trshMove); - trsf.TransformMeshVertices(mesh_, mesh_v_id_back_); - - - + trsf.TransformMeshVertices(getShape(), mesh_v_id_back_); } // select vert edge face - void MeshObject::selectVertWithFaceId(const MVector3& begin, const MVector3& end, int f_id, int& sv_id) + void MeshObject::selectVertWithFaceId(const MVector3 &begin, const MVector3 &end, int f_id, int &sv_id) { sv_id = -1; - auto ids = AMCAX::SubD::MeshTool::FaceVertexIndexs(mesh_, f_id); + auto ids = AMCAX::SubD::MeshTool::FaceVertexIndexs(getShape(), f_id); AMCAX::Coord3 coord; double len_min; for (int i = 0; i < ids.size(); i++) { - AMCAX::Point3 position = AMCAX::SubD::MeshTool::Position(mesh_, ids[i]); + AMCAX::Point3 position = AMCAX::SubD::MeshTool::Position(getShape(), ids[i]); double len = MathUtils::distPointLineSquared(position.Coord(), begin.Coord(), end.Coord(), coord); - if (len > 0.1) { + if (len > 0.1) + { continue; } - if (sv_id == -1 || len_min > len) { sv_id = ids[i]; len_min = len; } } - } - void MeshObject::selectEdgeWithFaceId(const MVector3& begin, const MVector3& end, int f_id, int& se_id) + void MeshObject::selectEdgeWithFaceId(const MVector3 &begin, const MVector3 &end, int f_id, int &se_id) { se_id = -1; - auto ids = AMCAX::SubD::MeshTool::FaceEdgeIndexs(mesh_, f_id); + auto ids = AMCAX::SubD::MeshTool::FaceEdgeIndexs(getShape(), f_id); AMCAX::Coord3 coord0, coord1; double len_min; - for (int i = 0; i < ids.size(); i++) { + for (int i = 0; i < ids.size(); i++) + { int id0, id1; - AMCAX::SubD::MeshTool::EdgeVertexIndexs(mesh_, ids[i], id0, id1); - AMCAX::Point3 p0 = AMCAX::SubD::MeshTool::Position(mesh_, id0), - p1 = AMCAX::SubD::MeshTool::Position(mesh_, id1); + AMCAX::SubD::MeshTool::EdgeVertexIndexs(getShape(), ids[i], id0, id1); + AMCAX::Point3 p0 = AMCAX::SubD::MeshTool::Position(getShape(), id0), + p1 = AMCAX::SubD::MeshTool::Position(getShape(), id1); double len = MathUtils::distLine_SLineSquared(p0.Coord(), p1.Coord(), begin.Coord(), end.Coord(), coord0, coord1); - if (len > 0.1) { + if (len > 0.1) + { continue; } @@ -438,66 +387,64 @@ namespace acamcad { se_id = ids[i]; len_min = len; } - } - } - //=========================================================== + ////=========================================================== - void MeshObject::backMesh(polymesh::PolyMesh*& bmesh) - { - if (mesh_ != nullptr) - { - if (bmesh == nullptr) - bmesh = new polymesh::PolyMesh(); + // void MeshObject::backMesh(polymesh::PolyMesh*& bmesh) + //{ + // if (mesh_ != nullptr) + // { + // if (bmesh == nullptr) + // bmesh = new polymesh::PolyMesh(); - bmesh = AMCAX::SubD::MeshCopy::CopyMesh(mesh_); + // bmesh = AMCAX::SubD::MeshCopy::CopyMesh(getShape()); - //polymesh::CopyMesh(mesh_, bmesh); - } - else - { - bmesh = nullptr; - } - } + // //polymesh::CopyMesh(mesh_, bmesh); + // } + // else + // { + // bmesh = nullptr; + // } + //} - void MeshObject::applyMesh(const polymesh::PolyMesh* amesh) - { - mesh_->clear(); - mesh_ = AMCAX::SubD::MeshCopy::CopyMesh(amesh); - } + // void MeshObject::applyMesh(const polymesh::PolyMesh* amesh) + //{ + // mesh_->clear(); + // mesh_.reset(AMCAX::SubD::MeshCopy::CopyMesh(amesh)); + // } //=========================================================== void MeshObject::meshTriangulating() { - //polymesh::meshTriangles(mesh_); - AMCAX::SubD::MeshTool::MeshTriangles(mesh_); + // polymesh::meshTriangles(mesh_); + AMCAX::SubD::MeshTool::MeshTriangles(getShape()); } - void MeshObject::meshQuadrilateralizatuon() - { - //polymesh::meshQuadrilate(mesh_); - std::cout << "MeshObject::meshQuadrilateralizatuon error" << std::endl; - } + // void MeshObject::meshQuadrilateralizatuon() + //{ + // //polymesh::meshQuadrilate(mesh_); + // std::cout << "MeshObject::meshQuadrilateralizatuon error" << std::endl; + // } void MeshObject::subdivemeshCC(int sub_time) { - //polymesh::catmullclark_Subdive(mesh_, sub_time); - AMCAX::SubD::MeshSubdiveHE::CatmullClarkSubdive(mesh_, sub_time); + // polymesh::catmullclark_Subdive(mesh_, sub_time); + + AMCAX::SubD::MeshSubdiveHE::CatmullClarkSubdive(getShape(), sub_time); draw_tool_->updatedrawState(); } void MeshObject::subdivemeshLoop(int sub_time) { - //if (mesh_->isTriangleMesh()) error + // if (mesh_->isTriangleMesh()) error - AMCAX::SubD::MeshSubdiveHE::LoopSubdive(mesh_, sub_time); + AMCAX::SubD::MeshSubdiveHE::LoopSubdive(getShape(), sub_time); draw_tool_->updatedrawState(); - //polymesh::loop_Subdive(mesh_, sub_time); - + // polymesh::loop_Subdive(mesh_, sub_time); } void MeshObject::updateDraw() @@ -507,26 +454,25 @@ namespace acamcad { //=========================================================== - void MeshObject::weldEdge(std::vector& e_list) + void MeshObject::weldEdge(std::vector &e_list) { - //polymesh::meshWeldEdges(mesh_, e_list); - AMCAX::SubD::MeshWeld::MeshWeldEdges(mesh_, e_list); + // polymesh::meshWeldEdges(mesh_, e_list); + AMCAX::SubD::MeshWeld::MeshWeldEdges(getShape(), e_list); } - - //=========================================================== - QString MeshObject::getObjectinfo() { + QString MeshObject::getObjectinfo() + { QString output; output += "========================================================================\n"; output += parent_->getObjectinfo(); output += "Object Contains Poly Mesh : "; - output += QString::number(mesh()->numVertices()) + " vertices, "; - output += QString::number(mesh()->numEdges()) += " edges "; - output += QString::number(mesh()->numPolygons()) += " faces.\n"; + output += QString::number(getShape()->numVertices()) + " vertices, "; + output += QString::number(getShape()->numEdges()) += " edges "; + output += QString::number(getShape()->numPolygons()) += " faces.\n"; output += "========================================================================\n"; return output; diff --git a/Src/Object/MeshObject.h b/Src/Object/MeshObject.h index 6aedf0df8055b757dbcf311e2bf50b66235cb763..ea37951cb6f6265e7b78586fe90c4031e14aa25e 100644 --- a/Src/Object/MeshObject.h +++ b/Src/Object/MeshObject.h @@ -32,12 +32,12 @@ namespace acamcad { virtual ~MeshObject(); public: - void SetMesh(AMCAX::SubD::PolyMesh* mesh); - polymesh::PolyMesh* mesh(); - const polymesh::PolyMesh* mesh() const; + void setShape(polymesh::PolyMesh* mesh); + polymesh::PolyMesh* getShape(); + const polymesh::PolyMesh* getShape() const; private: - polymesh::PolyMesh* mesh_; + std::unique_ptr mesh_; //polymesh::PolyMesh* mesh_subdive_; std::vector mesh_v_id_back_; @@ -45,12 +45,12 @@ namespace acamcad { //std::vector mesh_normal_back_; float scale_ = 1; - public: - /// Load a mesh from the given file - bool loadMesh(QString filename); - /// Save a mesh to the given file - bool saveMesh(QString filename); - bool saveTSpline(QString filename); + //public: + // /// Load a mesh from the given file + // bool loadMesh(QString filename); + // /// Save a mesh to the given file + // bool saveMesh(QString filename); + // bool saveTSpline(QString filename); public: const virtual ObjectDraw* getDrawTool() const { return draw_tool_.get(); } @@ -62,16 +62,16 @@ namespace acamcad { virtual void doOperate(MOperation* operate); public: - void assignMesh(); + void assignShape(); - public: - void backMesh(polymesh::PolyMesh*& bmesh); - void applyMesh(const polymesh::PolyMesh* amesh); + //public: + // ///void backMesh(polymesh::PolyMesh*& bmesh); + // void applyMesh(const polymesh::PolyMesh* amesh); public: bool combineObject(const MeshObject* mesh_object); - void duplicateFaces(MeshObject* mesh_object, std::vector& f_list); + //void duplicateFaces(MeshObject* mesh_object, std::vector& f_list); void mirrorMeshObject(const MVector3& center, const MVector3& normal); @@ -103,7 +103,7 @@ namespace acamcad { public: void meshTriangulating(); - void meshQuadrilateralizatuon(); + //void meshQuadrilateralizatuon(); ///void reverseMeshNormal(); diff --git a/Src/Object/TSplineObject.cpp b/Src/Object/TSplineObject.cpp index 129426e58bf135aed23d095e6d87cfdfd49d2032..b49725442e19868ec6f15bf12382a6301af08a15 100644 --- a/Src/Object/TSplineObject.cpp +++ b/Src/Object/TSplineObject.cpp @@ -16,20 +16,20 @@ namespace acamcad { TSplineObject::TSplineObject(AdapterObject* parent) :AdapterObjectParent(parent), - spline_(nullptr), scale_(1) + scale_(1) { //setDataType(DataType::TSPLINEU_TYPE); draw_tool_ = std::make_unique(parent); } TSplineObject::TSplineObject(AdapterObject* parent, const TSplineObject& object, bool isDraw) : - AdapterObjectParent(parent), spline_(nullptr), scale_(1) + AdapterObjectParent(parent), scale_(1) //BaseObject(object), spline_(nullptr) { if (object.spline_ != nullptr) { //AMCAX::TMS::TMSplineCopy copy; - spline_ = AMCAX::TMS::TMSplineCopy().CloneTMSpline(object.spline_); + spline_.reset(AMCAX::TMS::TMSplineCopy().CloneTMSpline(object.getShape())); //tcopy.copyTspline(object.spline_, spline_); } if (isDraw) { @@ -41,40 +41,35 @@ namespace acamcad TSplineObject::~TSplineObject() { - if (spline_ != nullptr) - { - spline_->clear(); - delete spline_; - } } - AMCAX::TMS::TMSpline* TSplineObject::getTSpline() + AMCAX::TMS::TMSpline* TSplineObject::getShape() { - return spline_; + return spline_.get(); } - const AMCAX::TMS::TMSpline* TSplineObject::getTSpline() const + const AMCAX::TMS::TMSpline* TSplineObject::getShape() const { - return spline_; + return spline_.get(); } - void TSplineObject::setTSpline(AMCAX::TMS::TMSpline* tsp) + void TSplineObject::setShape(AMCAX::TMS::TMSpline* tsp) { - if (spline_ != nullptr) - { - delete spline_; - spline_ = nullptr; + //if (spline_ != nullptr) + //{ + // delete spline_; + // spline_ = nullptr; - draw_tool_->clear(); - } + // draw_tool_->clear(); + //} - spline_ = tsp; + spline_.reset(tsp); } - void TSplineObject::assignTSpline() + void TSplineObject::assignShape() { if (spline_ == nullptr) - spline_ = new AMCAX::TMS::TMSpline(); + spline_ = std::make_unique(); } // select vert edge face @@ -83,14 +78,14 @@ namespace acamcad sv_id = -1; //auto ids = AMCAX::SubD::MeshTool::FaceVertexIndexs(mesh_, f_id); - auto ids = AMCAX::TMS::TMSplineTool::FaceVertexIndexs(spline_, f_id); + auto ids = AMCAX::TMS::TMSplineTool::FaceVertexIndexs(getShape(), f_id); AMCAX::Coord3 coord; double len_min; for (int i = 0; i < ids.size(); i++) { - AMCAX::Point3 position = AMCAX::TMS::TMSplineTool::ControlPosition(spline_, ids[i]); + AMCAX::Point3 position = AMCAX::TMS::TMSplineTool::ControlPosition(getShape(), ids[i]); double len = MathUtils::distPointLineSquared(position.Coord(), begin.Coord(), end.Coord(), coord); if (len > 0.1) { @@ -110,15 +105,15 @@ namespace acamcad void TSplineObject::selectEdgeWithFaceId(const AMCAX::Vector3& begin, const AMCAX::Vector3& end, int f_id, int& se_id) { se_id = -1; - auto ids = AMCAX::TMS::TMSplineTool::FaceEdgeIndexs(spline_, f_id); + auto ids = AMCAX::TMS::TMSplineTool::FaceEdgeIndexs(getShape(), f_id); AMCAX::Coord3 coord0, coord1; double len_min = 0; for (int i = 0; i < ids.size(); i++) { int id0, id1; - AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(spline_, ids[i], id0, id1); - AMCAX::Point3 p0 = AMCAX::TMS::TMSplineTool::ControlPosition(spline_, id0), - p1 = AMCAX::TMS::TMSplineTool::ControlPosition(spline_, id1); + AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(getShape(), ids[i], id0, id1); + AMCAX::Point3 p0 = AMCAX::TMS::TMSplineTool::ControlPosition(getShape(), id0), + p1 = AMCAX::TMS::TMSplineTool::ControlPosition(getShape(), id1); double len = MathUtils::distLine_SLineSquared(p0.Coord(), p1.Coord(), begin.Coord(), end.Coord(), coord0, coord1); if (len > 0.1) { @@ -155,57 +150,57 @@ namespace acamcad //=========================================================================== - bool TSplineObject::loadSpline(const QString& _filename) - { - parent_->setFromFileName(_filename); + //bool TSplineObject::loadSpline(const QString& _filename) + //{ + // parent_->setFromFileName(_filename); - // call the local function to update names - //parent_->setName(parent_->name()); + // // call the local function to update names + // //parent_->setName(parent_->name()); - std::string filename = _filename.toStdString(); + // std::string filename = _filename.toStdString(); - AMCAX::TMS::TMSplineIO io; + // AMCAX::TMS::TMSplineIO io; - bool ok = io.LoadTMSpline(filename, spline_); - if (ok) - { - updateDraw(); - parent_->show(); - } - else - { - std::cout << "Load File fall" << std::endl; - } + // bool ok = io.LoadTMSpline(filename, getShape()); + // if (ok) + // { + // updateDraw(); + // parent_->show(); + // } + // else + // { + // std::cout << "Load File fall" << std::endl; + // } - return ok; - } + // return ok; + //} - bool TSplineObject::saveSpline(const QString& _filename) - { - std::string filename = _filename.toStdString(); + //bool TSplineObject::saveSpline(const QString& _filename) + //{ + // std::string filename = _filename.toStdString(); - AMCAX::TMS::TMSplineIO io; + // AMCAX::TMS::TMSplineIO io; - bool ok = io.WriteTMSpline(filename, spline_); + // bool ok = io.WriteTMSpline(filename, spline_); - if (!ok) - { - std::cout << "Save File fall" << std::endl; - } - return ok; - } + // if (!ok) + // { + // std::cout << "Save File fall" << std::endl; + // } + // return ok; + //} - bool TSplineObject::saveSplineSTL(const QString& filename) - { - //TSplineObjectDraw* dt = dynamic_cast(draw_tool_); - return draw_tool_->saveSTL(filename.toStdString()); - } + //bool TSplineObject::saveSplineSTL(const QString& filename) + //{ + // //TSplineObjectDraw* dt = dynamic_cast(draw_tool_); + // return draw_tool_->saveSTL(filename.toStdString()); + //} - bool TSplineObject::saveSplineOBJ(const QString& filename) - { - //TSplineObjectDraw* dt = dynamic_cast(draw_tool_); - return draw_tool_->saveOBJ(filename.toStdString()); - } + //bool TSplineObject::saveSplineOBJ(const QString& filename) + //{ + // //TSplineObjectDraw* dt = dynamic_cast(draw_tool_); + // return draw_tool_->saveOBJ(filename.toStdString()); + //} //bool TSplineObject::saveSplineFrep(const QString& filename) //{ @@ -227,7 +222,7 @@ namespace acamcad draw_tool_->updatedrawState(); if (spline_) - AMCAX::TMS::TMSplineTool::UpdateNormal(spline_); + AMCAX::TMS::TMSplineTool::UpdateNormal(getShape()); } //=========================================================================== @@ -265,7 +260,7 @@ namespace acamcad //for() for (int i = 0; i < edge_id.size(); i++) { int v0, v1; - AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(spline_, edge_id[i], v0, v1); + AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(getShape(), edge_id[i], v0, v1); mesh_v_id_back_.push_back(v0); mesh_v_id_back_.push_back(v1); @@ -287,7 +282,7 @@ namespace acamcad //vert_id.erase(std::unique(vert_id.begin(), vert_id.end()), vert_id.end()); for (int i = 0; i < face_id.size(); i++) { - std::vector vert_id = AMCAX::TMS::TMSplineTool::FaceVertexIndexs(spline_, face_id[i]); + std::vector vert_id = AMCAX::TMS::TMSplineTool::FaceVertexIndexs(getShape(), face_id[i]); //mesh_v_id_back_.push_back( vert_id); @@ -330,9 +325,9 @@ namespace acamcad trfScale.SetAffinity(ct, scale_); trf.SetGTransformation(trfScale); - trf.TransformTMSplineVertices(spline_, mesh_v_id_back_); + trf.TransformTMSplineVertices(getShape(), mesh_v_id_back_); scale_ = scale; - trf.TransformReprocessing(spline_); + trf.TransformReprocessing(getShape()); } void TSplineObject::meshScalePlane(const AMCAX::Vector3& center, const AMCAX::Vector3& normal, double scale) @@ -357,9 +352,9 @@ namespace acamcad trfScale.SetAffinity(ct, scale_); trf.SetGTransformation(trfScale); - trf.TransformTMSplineVertices(spline_, mesh_v_id_back_); + trf.TransformTMSplineVertices(getShape(), mesh_v_id_back_); scale_ = scale; - trf.TransformReprocessing(spline_); + trf.TransformReprocessing(getShape()); } void TSplineObject::meshRotation(const AMCAX::Vector3& center, const AMCAX::Vector3& axis, double angle) @@ -372,8 +367,8 @@ namespace acamcad trsf.SetTransformation(trfRotation); - trsf.TransformTMSplineVertices(spline_, mesh_v_id_back_); - trsf.TransformReprocessing(spline_); + trsf.TransformTMSplineVertices(getShape(), mesh_v_id_back_); + trsf.TransformReprocessing(getShape()); } void TSplineObject::meshMove(const AMCAX::Vector3& v, double dis) @@ -385,9 +380,9 @@ namespace acamcad AMCAX::TMS::TMSplineTransform trf; trf.SetTransformation(trMove); - trf.TransformTMSplineVertices(spline_, mesh_v_id_back_); + trf.TransformTMSplineVertices(getShape(), mesh_v_id_back_); - trf.TransformReprocessing(spline_); + trf.TransformReprocessing(getShape()); } const ObjectDraw* TSplineObject::getDrawTool() const @@ -405,9 +400,9 @@ namespace acamcad trfMirror.SetMirror(frame); trf.SetTransformation(trfMirror); - trf.TransformTMSpline(spline_); + trf.TransformTMSpline(getShape()); - trf.TransformReprocessing(spline_); + trf.TransformReprocessing(getShape()); } //=========================================================== diff --git a/Src/Object/TSplineObject.h b/Src/Object/TSplineObject.h index 69363caf051221d4bb4cecd7cce41cd7a402601e..aca19c71fb919c8efcfc684895edc7a1a0be8065 100644 --- a/Src/Object/TSplineObject.h +++ b/Src/Object/TSplineObject.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "BaseObject.h" #include @@ -31,13 +31,13 @@ namespace acamcad virtual void updateDraw(); public: - AMCAX::TMS::TMSpline* getTSpline(); - const AMCAX::TMS::TMSpline* getTSpline() const; + AMCAX::TMS::TMSpline* getShape(); + const AMCAX::TMS::TMSpline* getShape() const; /// This function is Very danger, only using in creat operation, I am trying other ways to replace it - void setTSpline(AMCAX::TMS::TMSpline* tsp); + void setShape(AMCAX::TMS::TMSpline* tsp); - void assignTSpline(); + void assignShape(); // select vert edge face void selectVertWithFaceId(const AMCAX::Vector3& begin, const AMCAX::Vector3& end, int f_id, int& sv_id); @@ -47,34 +47,32 @@ namespace acamcad //void backSpline(AMCAX::TMS::TMSpline*& back_spline); //void applySpline(const AMCAX::TMS::TMSpline* apply_spline); - public: - /// Load a mesh from the given file - bool loadSpline(const QString& filename); - /// Save a mesh to the given file - bool saveSpline(const QString& filename); + //public: + // /// Load a mesh from the given file + // bool loadSpline(const QString& filename); + // /// Save a mesh to the given file + // bool saveSpline(const QString& filename); - bool saveSplineSTL(const QString& filename); - bool saveSplineOBJ(const QString& filename); + // bool saveSplineSTL(const QString& filename); + // bool saveSplineOBJ(const QString& filename); //bool saveSplineFrep(const QString& filename); //std::set frepFixFaceIndex; //std::set frepForceFaceIndex; private: - AMCAX::TMS::TMSpline* spline_; + std::unique_ptr spline_; public: - //存储原来点的位置 void mesh_OPvert_Backup(); void mesh_OPvert_BackupVertes(const std::vector& vert_id); void mesh_OPvert_BackupEdges(const std::vector& edge_id); void mesh_OPvert_BackupFaces(const std::vector& face_id); void mesh_Clear_OPvertBackup(); - //在有了原来的顶点之后,所有的缩放都是一样的,没有体,边,面,点的区别了。 void meshScaleSingle(const AMCAX::Vector3& center, const AMCAX::Vector3& axis, double scale); void meshScalePlane(const AMCAX::Vector3& center, const AMCAX::Vector3& normal, double scale); - void meshRotation(const AMCAX::Vector3& center, const AMCAX::Vector3& axis, double angle); //rotation 没存储原来的位置 + void meshRotation(const AMCAX::Vector3& center, const AMCAX::Vector3& axis, double angle); ///void meshMoveUsingNormal(double dis); void meshMove(const AMCAX::Vector3& v, double dis); diff --git a/Src/ObjectDraw/MeshObjectDraw.cpp b/Src/ObjectDraw/MeshObjectDraw.cpp index a8a0889a4dc9ed3433af2ed4b5c79f8168e08e81..933df51fe32b3faa69b2b56ad5419066dc5ff777 100644 --- a/Src/ObjectDraw/MeshObjectDraw.cpp +++ b/Src/ObjectDraw/MeshObjectDraw.cpp @@ -29,7 +29,7 @@ void MeshObjectDraw::draw(const DrawModel& dmodel, bool is_show_point) const if (!parent_->isVisible()) return; - const polymesh::PolyMesh* mesh = parent_->mesh->mesh_; + const polymesh::PolyMesh* mesh = parent_->mesh->getShape(); if (mesh == nullptr) { return; } if (mesh->numVertices() == 0) { return; } @@ -118,7 +118,7 @@ void MeshObjectDraw::draw(const DrawModel& dmodel, bool is_show_point) const void MeshObjectDraw::drawSelected(const SelectModel& s_model, int s_id) const { - polymesh::PolyMesh* mesh = parent_->mesh->mesh_; + polymesh::PolyMesh* mesh = parent_->mesh->getShape(); if (s_model == SelectModel::VERTEX_MODEL) { @@ -174,7 +174,7 @@ void MeshObjectDraw::drawWithObjectID() const if (!parent_->isVisible()) return; - const polymesh::PolyMesh* mesh = parent_->mesh->mesh_; + const polymesh::PolyMesh* mesh = parent_->mesh->getShape(); if (mesh == nullptr) { return; } if (mesh->numVertices() == 0) { return; } @@ -194,7 +194,7 @@ void MeshObjectDraw::drawWithObjectID() const void MeshObjectDraw::drawWithFaceID() const { - polymesh::PolyMesh* mesh = parent_->mesh->mesh(); + polymesh::PolyMesh* mesh = parent_->mesh->getShape(); //auto indexs = AMCAX::SubD::MeshTool::FaceVertexIndexs(mesh, i); for (int i = 0; i < mesh->numPolygons(); i++) { @@ -219,7 +219,7 @@ void MeshObjectDraw::drawWithFaceID() const void MeshObjectDraw::drawWithEdgeID() const { - auto mesh = parent_->mesh->mesh(); + auto mesh = parent_->mesh->getShape(); for (int i = 0; i < mesh->numEdges(); i++) { @@ -240,7 +240,7 @@ void MeshObjectDraw::drawWithEdgeID() const void MeshObjectDraw::drawWithVertexID() const { - auto mesh = parent_->mesh->mesh(); + auto mesh = parent_->mesh->getShape(); for (int i = 0; i < mesh->numVertices(); i++) diff --git a/Src/ObjectDraw/TSplineObjectDraw.cpp b/Src/ObjectDraw/TSplineObjectDraw.cpp index 9c70b70f480b22070b9d553a71db7bffec167754..dced0d0eb2117180ff801c2c34d2a0e5958c8954 100644 --- a/Src/ObjectDraw/TSplineObjectDraw.cpp +++ b/Src/ObjectDraw/TSplineObjectDraw.cpp @@ -42,7 +42,7 @@ namespace acamcad void TSplineObjectDraw::draw(const DrawModel& dmodel, bool is_show_point) const { bool is_selected = parent_->isSelect(); - auto mesh = parent_->tSpline->getTSpline(); + auto mesh = parent_->tSpline->getShape(); if (mesh == nullptr) { return; } if (mesh->numVertices() == 0) { return; } @@ -97,7 +97,7 @@ namespace acamcad void TSplineObjectDraw::drawSelected(const SelectModel& s_model, int s_id) const { - auto mesh = parent_->tSpline->getTSpline(); + auto mesh = parent_->tSpline->getShape(); if (s_model == SelectModel::VERTEX_MODEL) { @@ -212,7 +212,7 @@ namespace acamcad if (!parent_->isVisible()) return; - auto mesh = parent_->tSpline->getTSpline(); + auto mesh = parent_->tSpline->getShape(); if (mesh == nullptr) { return; } if (mesh->numVertices() == 0) { return; } @@ -231,7 +231,7 @@ namespace acamcad } void TSplineObjectDraw::drawWithFaceID() const { - auto mesh = parent_->tSpline->getTSpline(); + auto mesh = parent_->tSpline->getShape(); int size = mesh->numFaces(); for (int i = 0; i < size; i++) { @@ -252,7 +252,7 @@ namespace acamcad void TSplineObjectDraw::drawWithEdgeID() const { - auto mesh = parent_->tSpline->getTSpline(); + auto mesh = parent_->tSpline->getShape(); for (int i = 0; i < mesh->numEdges(); i++) { @@ -274,7 +274,7 @@ namespace acamcad void TSplineObjectDraw::drawWithVertexID() const { - auto mesh = parent_->tSpline->getTSpline(); + auto mesh = parent_->tSpline->getShape(); for (int i = 0; i < mesh->numVertices(); i++) { @@ -556,7 +556,7 @@ namespace acamcad if (!parent_->tSpline) return; - auto spl = parent_->tSpline->getTSpline(); + auto spl = parent_->tSpline->getShape(); if (spl == nullptr) { @@ -736,10 +736,10 @@ namespace acamcad void TSplineObjectDraw::initMesh(int subtime) { - if (tsp_mesh_ == nullptr && parent_->tSpline->getTSpline()) + if (tsp_mesh_ == nullptr && parent_->tSpline->getShape()) { - tsp_mesh_ = std::make_unique(parent_->tSpline->getTSpline(), subtime); + tsp_mesh_ = std::make_unique(parent_->tSpline->getShape(), subtime); } } diff --git a/Src/Operation/CreateOperate_CircularTSpline.cpp b/Src/Operation/CreateOperate_CircularTSpline.cpp index c5f6eaeb749164c92f8379189538a24ec1564197..415572893062c86d431a7d4ff4c4a345bc628714 100644 --- a/Src/Operation/CreateOperate_CircularTSpline.cpp +++ b/Src/Operation/CreateOperate_CircularTSpline.cpp @@ -21,9 +21,9 @@ namespace acamcad AMCAX::TMS::TMSplineMakeCircular shape(frame, radius_, angle_, seg_); - object->tSpline->setTSpline(shape.BuildSpline()); + object->tSpline->setShape(shape.BuildSpline()); object->tSpline->updateDraw(); - return true; + return object->tSpline->getShape(); } diff --git a/Src/Operation/CreateOperate_ConeTSpline.cpp b/Src/Operation/CreateOperate_ConeTSpline.cpp index 78a1a332054c29547d6dfcda9c61cab0ee314bc2..ee6334bc90b4373ec35deeb9d08d1af72c51c716 100644 --- a/Src/Operation/CreateOperate_ConeTSpline.cpp +++ b/Src/Operation/CreateOperate_ConeTSpline.cpp @@ -19,10 +19,10 @@ namespace acamcad AMCAX::TMS::TMSplineMakeCone cone(frame, radius_, height_, bottom_, rf_num_, vf_num_); - object->tSpline->setTSpline(cone.BuildSpline()); + object->tSpline->setShape(cone.BuildSpline()); object->tSpline->updateDraw(); - return true; + return object->tSpline->getShape(); } } \ No newline at end of file diff --git a/Src/Operation/CreateOperate_CubeMesh.cpp b/Src/Operation/CreateOperate_CubeMesh.cpp index b16f1512f08ea28ec1acdae5fb62eead1b70e003..1856b13e30ea908044f2799a98b0979cec8446d4 100644 --- a/Src/Operation/CreateOperate_CubeMesh.cpp +++ b/Src/Operation/CreateOperate_CubeMesh.cpp @@ -18,7 +18,7 @@ namespace acamcad AMCAX::Point3 bb_max = AMCAX::Point3(bb_max_.X(), bb_max_.Y(), bb_max_.Z()); AMCAX::SubD::MeshMakeCube cube(bb_min, bb_max, segX_, segY_, segZ_); - adapter->mesh->SetMesh(cube.BuildMesh()); + adapter->mesh->setShape(cube.BuildMesh()); adapter->updateDraw(); return true; diff --git a/Src/Operation/CreateOperate_CubeTSpline.cpp b/Src/Operation/CreateOperate_CubeTSpline.cpp index 8f18de8742067fa11a0b9169ac17c95cc29f1662..3df26e209c8ee7b9bef3862f04337a4ad598922a 100644 --- a/Src/Operation/CreateOperate_CubeTSpline.cpp +++ b/Src/Operation/CreateOperate_CubeTSpline.cpp @@ -22,11 +22,11 @@ namespace acamcad AMCAX::TMS::TMSplineMakeCube cube(bb_min, bb_max, segX_, segY_, segZ_); - adapter->tSpline->setTSpline(cube.BuildSpline()); + adapter->tSpline->setShape(cube.BuildSpline()); //adapter->mesh->SetMesh(cube.BuildMesh()); adapter->updateDraw(); - return true; + return adapter->tSpline->getShape(); //AMCAX::Point3 bb_min, bb_max_; //brep_object->SetShape(*AMCAX::SubD::MeshMakeCube(bb_min, bb_max, segX_, segY_, segZ_).BuildMesh()); } diff --git a/Src/Operation/CreateOperate_CylinderTSpline.cpp b/Src/Operation/CreateOperate_CylinderTSpline.cpp index 7845c6a5901af91baf47ee9a3940cde86422b2b4..3d988d5a9dd49e133027a052a4aa97bc5d9502c2 100644 --- a/Src/Operation/CreateOperate_CylinderTSpline.cpp +++ b/Src/Operation/CreateOperate_CylinderTSpline.cpp @@ -17,11 +17,11 @@ namespace acamcad AMCAX::TMS::TMSplineMakeCylinder shape(frame, radius_, height_, top_, bottom_, rf_num_, vf_num_); - object->tSpline->setTSpline(shape.BuildSpline()); + object->tSpline->setShape(shape.BuildSpline()); object->updateDraw(); - return true; + return object->tSpline->getShape(); } diff --git a/Src/Operation/CreateOperate_PlaneMesh.cpp b/Src/Operation/CreateOperate_PlaneMesh.cpp index c451feeafe5800ad08ad6fc6991758a7b858dc03..575249b0fcbaa656efa0a8cc1a3f3ed9adbf9ac1 100644 --- a/Src/Operation/CreateOperate_PlaneMesh.cpp +++ b/Src/Operation/CreateOperate_PlaneMesh.cpp @@ -37,7 +37,7 @@ namespace acamcad AMCAX::SubD::MeshMakeRectangle mesh(p0, p1, segU_, segV_); - adapter->mesh->SetMesh(mesh.BuildMesh()); + adapter->mesh->setShape(mesh.BuildMesh()); //brep_object->bRep->getShape() = AMCAX::MakeFace(AMCAX::Plane(), xmin, xmax, ymin, ymax); //brep_object->bRep->updateDraw(); diff --git a/Src/Operation/CreateOperate_PlaneTSpline.cpp b/Src/Operation/CreateOperate_PlaneTSpline.cpp index bc3f3ac5ef745d64e1ced00de1eae1ee798d5c77..26d624318258e06a1775dc1764521100068422c4 100644 --- a/Src/Operation/CreateOperate_PlaneTSpline.cpp +++ b/Src/Operation/CreateOperate_PlaneTSpline.cpp @@ -38,13 +38,13 @@ namespace acamcad //AMCAX::SubD::MeshMakeRectangle mesh(p0, p1, segU_, segV_); - adapter->tSpline->setTSpline(mesh.BuildSpline()); + adapter->tSpline->setShape(mesh.BuildSpline()); //brep_object->bRep->getShape() = AMCAX::MakeFace(AMCAX::Plane(), xmin, xmax, ymin, ymax); //brep_object->bRep->updateDraw(); adapter->updateDraw(); - return true; + return adapter->tSpline->getShape(); } } \ No newline at end of file diff --git a/Src/Operation/CreateOperate_QuadballTSpline.cpp b/Src/Operation/CreateOperate_QuadballTSpline.cpp index 64bfad15d2de0a5c4932a5b8e20cbf7195c1be0c..3d4bcdedc93eab7618229e1eb8b3f511ccee4011 100644 --- a/Src/Operation/CreateOperate_QuadballTSpline.cpp +++ b/Src/Operation/CreateOperate_QuadballTSpline.cpp @@ -15,10 +15,10 @@ namespace acamcad AMCAX::TMS::TMSplineMakeQuadball qb(center_, radius_, seg_); - object->tSpline->setTSpline(qb.BuildSpline()); + object->tSpline->setShape(qb.BuildSpline()); object->tSpline->updateDraw(); - return true; + return object->tSpline->getShape(); } } \ No newline at end of file diff --git a/Src/Operation/CreateOperate_SingleFaceMesh.cpp b/Src/Operation/CreateOperate_SingleFaceMesh.cpp index 55df5e6522a270b7d294e704d65f8f24c047be64..47e9f57a661de33bbade7c04b122483ef73d712b 100644 --- a/Src/Operation/CreateOperate_SingleFaceMesh.cpp +++ b/Src/Operation/CreateOperate_SingleFaceMesh.cpp @@ -10,7 +10,7 @@ namespace acamcad bool CreateOperate_SingleFaceMesh::DoOperate(AdapterObject* object) { - object->mesh->assignMesh(); + object->mesh->assignShape(); std::vector pList; for (auto& iter : point_list_) @@ -18,7 +18,7 @@ namespace acamcad pList.push_back(MPoint3(iter)); } - AMCAX::SubD::MeshInsertFace::AddSingleFace(object->mesh->mesh(), pList); + AMCAX::SubD::MeshInsertFace::AddSingleFace(object->mesh->getShape(), pList); object->updateDraw(); diff --git a/Src/Operation/CreateOperate_SingleFaceTSpline.cpp b/Src/Operation/CreateOperate_SingleFaceTSpline.cpp index 5dabd82ae4488e1842ae0d23f13078da0078dae1..f28c8c0da7e95f880df655321857d72dc0c80bc8 100644 --- a/Src/Operation/CreateOperate_SingleFaceTSpline.cpp +++ b/Src/Operation/CreateOperate_SingleFaceTSpline.cpp @@ -12,7 +12,7 @@ namespace acamcad bool CreateOperate_SingleFaceTSpline::DoOperate(AdapterObject* object) { - object->tSpline->assignTSpline(); + object->tSpline->assignShape(); std::vector pList; for (auto& iter : point_list_) @@ -20,7 +20,7 @@ namespace acamcad pList.push_back(MPoint3(iter)); } - bool result = AMCAX::TMS::TMSplineAddFace().AddSingleFace(object->tSpline->getTSpline(), pList); + bool result = AMCAX::TMS::TMSplineAddFace().AddSingleFace(object->tSpline->getShape(), pList); //AMCAX::SubD::MeshInsertFace::AddSingleFace(object->mesh->mesh(), pList); diff --git a/Src/Operation/CreateOperate_Sphere.cpp b/Src/Operation/CreateOperate_Sphere.cpp index ea661e0f0fb06794e1ed9891a3b7de21dcd102ac..c61f09a54b19a7ad4b0ae312f00c6ba3395e6ec7 100644 --- a/Src/Operation/CreateOperate_Sphere.cpp +++ b/Src/Operation/CreateOperate_Sphere.cpp @@ -29,8 +29,8 @@ namespace acamcad { } - CreateOperate_Sphere::CreateOperate_Sphere(const AMCAX::Coord3& center, double radius, double angle1, double angle2, double angle3) : - center_(center), radius_(radius), segR_(), segV_(), angle1_(angle1), angle2_(angle2), angle3_(angle3) + CreateOperate_Sphere::CreateOperate_Sphere(const AMCAX::Coord3& center, double radius, double angle1) : + center_(center), radius_(radius), segR_(), segV_(), angle1_(angle1)//, angle2_(angle2), angle3_(angle3) { } diff --git a/Src/Operation/CreateOperate_Sphere.h b/Src/Operation/CreateOperate_Sphere.h index 79272e17b357156f36df4a43cd718b2e3f50eab5..de3190b4be63aa95337ff967c46094a9224c2edf 100644 --- a/Src/Operation/CreateOperate_Sphere.h +++ b/Src/Operation/CreateOperate_Sphere.h @@ -26,7 +26,7 @@ namespace acamcad public: CreateOperate_Sphere(); CreateOperate_Sphere(const AMCAX::Coord3& center, double radius, size_t rotation_seg, size_t vertical_seg); - CreateOperate_Sphere(const AMCAX::Coord3& center, double radius, double angle1, double angle2, double angle3); + CreateOperate_Sphere(const AMCAX::Coord3& center, double radius, double angle1); virtual ~CreateOperate_Sphere(); @@ -40,7 +40,7 @@ namespace acamcad double radius_; size_t segR_, segV_; - double angle1_, angle2_, angle3_; + double angle1_;// , angle2_, angle3_; }; } diff --git a/Src/Operation/CreateOperate_SphereTSpline.cpp b/Src/Operation/CreateOperate_SphereTSpline.cpp index e3385957346ef7419f732fc0d760e7ba7ef03e43..92d266f804af5bcde80f5adbb132ba430d9b14d3 100644 --- a/Src/Operation/CreateOperate_SphereTSpline.cpp +++ b/Src/Operation/CreateOperate_SphereTSpline.cpp @@ -29,17 +29,19 @@ namespace acamcad { //brep_object->bRep->getShape() = AMCAX::MakeSphere(AMCAX::Point3(center_[0], center_[1], center_[2]), radius_, angle1_); //brep_object->updateDraw(); - AMCAX::TMS::TMSplineMakeSphere sphre(radius_, segR_, segV_); + AMCAX::Frame3 frame(AMCAX::Point3(center_), AMCAX::Direction3(0.0, 0.0, 1.0)); - adapter->tSpline->setTSpline(sphre.BuildSpline()); + AMCAX::TMS::TMSplineMakeSphere sphre(frame, radius_, segR_, segV_); - adapter->mesh_OPvert_Backup(); - adapter->meshMove(center_, AMCAX::Point3(center_).Distance(AMCAX::Point3(0.0, 0.0, 0.0))); - adapter->mesh_Clear_OPvertBackup(); + adapter->tSpline->setShape(sphre.BuildSpline()); + + //adapter->mesh_OPvert_Backup(); + //adapter->meshMove(center_, AMCAX::Point3(center_).Distance(AMCAX::Point3(0.0, 0.0, 0.0))); + //adapter->mesh_Clear_OPvertBackup(); adapter->updateDraw(); - return true; + return adapter->tSpline->getShape(); } diff --git a/Src/Operation/CreateOperate_TorusTSpline.cpp b/Src/Operation/CreateOperate_TorusTSpline.cpp index eb5e6cfc15852cec09dc15b7b596f3e1835a6924..1d6c1786913b0aaa0345ebe04a3952c856c9d7ef 100644 --- a/Src/Operation/CreateOperate_TorusTSpline.cpp +++ b/Src/Operation/CreateOperate_TorusTSpline.cpp @@ -19,10 +19,10 @@ namespace acamcad AMCAX::Frame3 frame(p, dir); AMCAX::TMS::TMSplineMakeTorus torus(frame, radiusMajor_, radiusMinor_, segMajor_, segMinor_); - object->tSpline->setTSpline(torus.BuildSpline()); + object->tSpline->setShape(torus.BuildSpline()); object->tSpline->updateDraw(); - return true; + return object->tSpline->getShape(); } } \ No newline at end of file diff --git a/Src/Operation/MOperate_getDirection.cpp b/Src/Operation/MOperate_getDirection.cpp index fb045551fbc38a8a93b1c6788f3bf1dfaae845ff..084273feef23421a5ff9539065d93793258d2974 100644 --- a/Src/Operation/MOperate_getDirection.cpp +++ b/Src/Operation/MOperate_getDirection.cpp @@ -67,9 +67,9 @@ namespace acamcad case acamcad::DataType::MESH_TYPE: { int id0, id1; - AMCAX::SubD::MeshTool::EdgeVertexIndexs(adapter->mesh->mesh(), s_id, id0, id1); - AMCAX::Point3 p0 = AMCAX::SubD::MeshTool::Position(adapter->mesh->mesh(), id0), - p1 = AMCAX::SubD::MeshTool::Position(adapter->mesh->mesh(), id1); + AMCAX::SubD::MeshTool::EdgeVertexIndexs(adapter->mesh->getShape(), s_id, id0, id1); + AMCAX::Point3 p0 = AMCAX::SubD::MeshTool::Position(adapter->mesh->getShape(), id0), + p1 = AMCAX::SubD::MeshTool::Position(adapter->mesh->getShape(), id1); direction_ = (p1.Coord() - p0.Coord()).Normalized(); } @@ -101,7 +101,7 @@ namespace acamcad break; case acamcad::DataType::MESH_TYPE: { - direction_ = AMCAX::SubD::MeshTool::FaceNormal(adapter->mesh->mesh(), s_id).Coord().Normalized(); + direction_ = AMCAX::SubD::MeshTool::FaceNormal(adapter->mesh->getShape(), s_id).Coord().Normalized(); } break; default: diff --git a/Src/Operation/MultOperate_Combine.cpp b/Src/Operation/MultOperate_Combine.cpp index 1986211150903ac65f60ac14a445438344b6cd62..28a8661c3f7e6032e0e702e436bdd33f2ad216ae 100644 --- a/Src/Operation/MultOperate_Combine.cpp +++ b/Src/Operation/MultOperate_Combine.cpp @@ -58,11 +58,11 @@ acamcad::BaseObject* acamcad::MultOperate_Combine::operate() { AdapterObject* adapter_object = new AdapterObject; adapter_object->setDataType(acamcad::DataType::MESH_TYPE); - adapter_object->mesh->assignMesh(); + adapter_object->mesh->assignShape(); for (int i = 0; i < objectList_.size(); i++) { AdapterObject* bobj = objectList_[i]; - AMCAX::SubD::MeshCopy::CombineMesh(adapter_object->mesh->mesh(), bobj->mesh->mesh()); + AMCAX::SubD::MeshCopy::CombineMesh(adapter_object->mesh->getShape(), bobj->mesh->getShape()); } adapter_object->updateDraw(); return adapter_object; @@ -72,11 +72,11 @@ acamcad::BaseObject* acamcad::MultOperate_Combine::operate() { AdapterObject* adapter_object = new AdapterObject; adapter_object->setDataType(acamcad::DataType::TSPLINEU_TYPE); - adapter_object->tSpline->assignTSpline(); + adapter_object->tSpline->assignShape(); for (int i = 0; i < objectList_.size(); i++) { - AMCAX::TMS::TMSplineCopy().AppendToTMSpline(adapter_object->tSpline->getTSpline(), - objectList_[i]->tSpline->getTSpline()); + AMCAX::TMS::TMSplineCopy().AppendToTMSpline(adapter_object->tSpline->getShape(), + objectList_[i]->tSpline->getShape()); } adapter_object->updateDraw(); return adapter_object; diff --git a/Src/Operation/SGLOperate_Bridge.cpp b/Src/Operation/SGLOperate_Bridge.cpp index 0c351aa2bfffd161d7193060213f5ebe46778b09..7283f2dd6bf8d454258e3056cc2953958dd3afcb 100644 --- a/Src/Operation/SGLOperate_Bridge.cpp +++ b/Src/Operation/SGLOperate_Bridge.cpp @@ -29,7 +29,7 @@ bool SGLOperate_Bridge::DoOperate(AdapterObject* adapter) { std::vector s_id_list = getSelectSubList(s_info_list_); - result = AMCAX::SubD::MeshInsertFace::AddFaceByEdge(adapter->mesh->mesh(), s_id_list[0], s_id_list[1]); + result = AMCAX::SubD::MeshInsertFace::AddFaceByEdge(adapter->mesh->getShape(), s_id_list[0], s_id_list[1]); } break; case DataType::TSPLINEU_TYPE: @@ -38,10 +38,10 @@ bool SGLOperate_Bridge::DoOperate(AdapterObject* adapter) AMCAX::TMS::TMSplineBridgeEdge tool; - result = tool.CanBridgeEdge(adapter->tSpline->getTSpline(), s_id_list[0], s_id_list[1]); + result = tool.CanBridgeEdge(adapter->tSpline->getShape(), s_id_list[0], s_id_list[1]); if (result) { - tool.BridgeEdge(adapter->tSpline->getTSpline(), s_id_list[0], s_id_list[1]); + tool.BridgeEdge(adapter->tSpline->getShape(), s_id_list[0], s_id_list[1]); adapter->updateDraw(); } } diff --git a/Src/Operation/SGLOperate_ChamferEdge.cpp b/Src/Operation/SGLOperate_ChamferEdge.cpp index bf6c18b3604655945640152820888bdae99c3790..4ffc1a6d719f9485660a830cdc044c4e882074e6 100644 --- a/Src/Operation/SGLOperate_ChamferEdge.cpp +++ b/Src/Operation/SGLOperate_ChamferEdge.cpp @@ -59,10 +59,10 @@ bool acamcad::SGLOperate_ChamferEdge::DoOperate(AdapterObject* brep_object) { AMCAX::TMS::TMSplineSampleChamfer tool(0.1); - result = tool.CanEdgesSampleChamfer(brep_object->tSpline->getTSpline(), s_id_list); + result = tool.CanEdgesSampleChamfer(brep_object->tSpline->getShape(), s_id_list); if (result) { - tool.SampleChamfer(brep_object->tSpline->getTSpline(), s_id_list); + tool.SampleChamfer(brep_object->tSpline->getShape(), s_id_list); brep_object->updateDraw(); } } diff --git a/Src/Operation/SGLOperate_Crease.cpp b/Src/Operation/SGLOperate_Crease.cpp index 8dc7584355dddd5cc4fe21a1f5b772cecbd34180..f901e2e79c2432ad542057f7b6c5b8a94b655916 100644 --- a/Src/Operation/SGLOperate_Crease.cpp +++ b/Src/Operation/SGLOperate_Crease.cpp @@ -17,7 +17,7 @@ namespace acamcad std::vector s_id_list = getSelectSubList(s_info_list_); - AMCAX::TMS::TMSplineCreaseTool::AddCreaseEdge(object->tSpline->getTSpline(), s_id_list); + AMCAX::TMS::TMSplineCreaseTool::AddCreaseEdge(object->tSpline->getShape(), s_id_list); object->updateDraw(); diff --git a/Src/Operation/SGLOperate_CreaseRemove.cpp b/Src/Operation/SGLOperate_CreaseRemove.cpp index 347968b5ab4f30c10e1dd238219572ee198fc58f..4393f9cd4e0f0c7d5fdf54f6076da24cd25ea648 100644 --- a/Src/Operation/SGLOperate_CreaseRemove.cpp +++ b/Src/Operation/SGLOperate_CreaseRemove.cpp @@ -16,7 +16,7 @@ namespace acamcad std::vector s_id_list = getSelectSubList(s_info_list_); - AMCAX::TMS::TMSplineCreaseTool::RemoveCreaseEdge(object->tSpline->getTSpline(), s_id_list); + AMCAX::TMS::TMSplineCreaseTool::RemoveCreaseEdge(object->tSpline->getShape(), s_id_list); object->updateDraw(); diff --git a/Src/Operation/SGLOperate_DeleteElement.cpp b/Src/Operation/SGLOperate_DeleteElement.cpp index 6c76ff22387e98c66f356cb3edc13e2707a7a878..c48852c7827198a012aa4b32b5b190be79eca548 100644 --- a/Src/Operation/SGLOperate_DeleteElement.cpp +++ b/Src/Operation/SGLOperate_DeleteElement.cpp @@ -29,7 +29,7 @@ bool SGLOperate_Delete::DoOperate(AdapterObject* adapter) } std::vector s_id_list = getSelectSubList(s_info_list_); result = true; - AMCAX::SubD::MeshReduce::DeleteFaces(adapter->mesh->mesh(), s_id_list); + AMCAX::SubD::MeshReduce::DeleteFaces(adapter->mesh->getShape(), s_id_list); } break; case DataType::TSPLINEU_TYPE: @@ -45,10 +45,10 @@ bool SGLOperate_Delete::DoOperate(AdapterObject* adapter) AMCAX::TMS::TMSplineReduce reduce; - result = reduce.CanRemoveVertices(adapter->tSpline->getTSpline(), s_id_list); + result = reduce.CanRemoveVertices(adapter->tSpline->getShape(), s_id_list); if (result) { - reduce.RemoveEdges(adapter->tSpline->getTSpline(), s_id_list); + reduce.RemoveEdges(adapter->tSpline->getShape(), s_id_list); } } break; @@ -60,10 +60,10 @@ bool SGLOperate_Delete::DoOperate(AdapterObject* adapter) AMCAX::TMS::TMSplineReduce reduce; - result = reduce.CanRemoveEdges(adapter->tSpline->getTSpline(), s_id_list); + result = reduce.CanRemoveEdges(adapter->tSpline->getShape(), s_id_list); if (result) { - reduce.RemoveEdges(adapter->tSpline->getTSpline(), s_id_list); + reduce.RemoveEdges(adapter->tSpline->getShape(), s_id_list); } } break; @@ -75,10 +75,10 @@ bool SGLOperate_Delete::DoOperate(AdapterObject* adapter) AMCAX::TMS::TMSplineReduce reduce; - result = reduce.CanDeleteFaces(adapter->tSpline->getTSpline(), s_id_list); + result = reduce.CanDeleteFaces(adapter->tSpline->getShape(), s_id_list); if (result) { - reduce.DeleteFaces(adapter->tSpline->getTSpline(), s_id_list); + reduce.DeleteFaces(adapter->tSpline->getShape(), s_id_list); } } break; diff --git a/Src/Operation/SGLOperate_EmbedFace.cpp b/Src/Operation/SGLOperate_EmbedFace.cpp index 1d91c561b1530b185a290447168a9e09f828704d..27e20f48bda23092e06807241148654bf4d8fcb3 100644 --- a/Src/Operation/SGLOperate_EmbedFace.cpp +++ b/Src/Operation/SGLOperate_EmbedFace.cpp @@ -28,7 +28,7 @@ namespace acamcad //result = true; std::vector s_id_list = getSelectSubList(s_info_list_); - result = AMCAX::SubD::MeshInsertFace::EmbedSingleFace(adapter->mesh->mesh(), s_id_list) != nullptr; + result = AMCAX::SubD::MeshInsertFace::EmbedSingleFace(adapter->mesh->getShape(), s_id_list) != nullptr; adapter->updateDraw(); } else if (selmodel_ == SelectModel::EDGE_MODEL) @@ -40,7 +40,7 @@ namespace acamcad //polymesh::MEdge* e1 = mesh_object->mesh()->edge(s_id_list[1]); //polymesh::addFaceByEdge(mesh_object->mesh(), e0, e1); - result = AMCAX::SubD::MeshInsertFace::AddFaceByEdge(adapter->mesh->mesh(), s_id_list[0], s_id_list[1]) != nullptr; + result = AMCAX::SubD::MeshInsertFace::AddFaceByEdge(adapter->mesh->getShape(), s_id_list[0], s_id_list[1]) != nullptr; adapter->updateDraw(); @@ -61,7 +61,7 @@ namespace acamcad for (int i = 0; i < s_id_list.size(); i++) { AMCAX::Point3 point = - AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getTSpline(), s_id_list[i]); + AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getShape(), s_id_list[i]); points.push_back(point); } @@ -69,10 +69,10 @@ namespace acamcad AMCAX::TMS::TMSplineAddFace tool; - result = tool.CanAddSingleFace(adapter->tSpline->getTSpline(), points); + result = tool.CanAddSingleFace(adapter->tSpline->getShape(), points); if (result) { - tool.AddSingleFace(adapter->tSpline->getTSpline(), points); + tool.AddSingleFace(adapter->tSpline->getShape(), points); adapter->updateDraw(); } @@ -93,10 +93,10 @@ namespace acamcad AMCAX::TMS::TMSplineAddFace tool; - result = tool.CanAddFaceByEdge(adapter->tSpline->getTSpline(), s_id_list[0], s_id_list[1]); + result = tool.CanAddFaceByEdge(adapter->tSpline->getShape(), s_id_list[0], s_id_list[1]); if (result) { - tool.AddFaceByEdge(adapter->tSpline->getTSpline(), s_id_list[0], s_id_list[1]); + tool.AddFaceByEdge(adapter->tSpline->getShape(), s_id_list[0], s_id_list[1]); adapter->updateDraw(); } } diff --git a/Src/Operation/SGLOperate_ExtrudeEdge.cpp b/Src/Operation/SGLOperate_ExtrudeEdge.cpp index d84dde42d02dbc0d8624634d6ae4d2fcc4d0f58f..212ac17c9705525310c00f9a609af4ce01582e25 100644 --- a/Src/Operation/SGLOperate_ExtrudeEdge.cpp +++ b/Src/Operation/SGLOperate_ExtrudeEdge.cpp @@ -32,15 +32,15 @@ bool acamcad::SGLOperate_ExtrudeEdgeH::DoOperate(AdapterObject* adapter) for (int i = 0; i < s_id_list.size(); i++) { - std::vector faces = AMCAX::SubD::MeshTool::EdgeFaceIndexs(adapter->mesh->mesh(), + std::vector faces = AMCAX::SubD::MeshTool::EdgeFaceIndexs(adapter->mesh->getShape(), s_id_list[i]); - AMCAX::Vector3 nor = AMCAX::SubD::MeshTool::FaceNormal(adapter->mesh->mesh(), faces[0]); + AMCAX::Vector3 nor = AMCAX::SubD::MeshTool::FaceNormal(adapter->mesh->getShape(), faces[0]); int p0, p1; - AMCAX::SubD::MeshTool::EdgeVertexIndexs(adapter->mesh->mesh(), s_id_list[i], p0, p1); - AMCAX::Point3 position0 = AMCAX::SubD::MeshTool::Position(adapter->mesh->mesh(), p0), - position1 = AMCAX::SubD::MeshTool::Position(adapter->mesh->mesh(), p1); + AMCAX::SubD::MeshTool::EdgeVertexIndexs(adapter->mesh->getShape(), s_id_list[i], p0, p1); + AMCAX::Point3 position0 = AMCAX::SubD::MeshTool::Position(adapter->mesh->getShape(), p0), + position1 = AMCAX::SubD::MeshTool::Position(adapter->mesh->getShape(), p1); AMCAX::Vector3 ve = AMCAX::Vector3(position1.Coord() - position0.Coord()); @@ -50,7 +50,7 @@ bool acamcad::SGLOperate_ExtrudeEdgeH::DoOperate(AdapterObject* adapter) dire_.Normalize(); - result = AMCAX::SubD::MeshExtrude::ExtrudeEdge(adapter->mesh->mesh(), + result = AMCAX::SubD::MeshExtrude::ExtrudeEdge(adapter->mesh->getShape(), s_id_list, s_id_list_new); if (result) @@ -68,19 +68,19 @@ bool acamcad::SGLOperate_ExtrudeEdgeH::DoOperate(AdapterObject* adapter) { for (int i = 0; i < s_id_list.size(); i++) { - std::vector faces = AMCAX::TMS::TMSplineTool::EdgeFaceIndexs(adapter->tSpline->getTSpline(), + std::vector faces = AMCAX::TMS::TMSplineTool::EdgeFaceIndexs(adapter->tSpline->getShape(), s_id_list[i]); //AMCAX::TMS::TMSplineTool::ControlFaceNormal(adapter->tSpline->getTSpline(), faces[0]); - AMCAX::Vector3 nor = AMCAX::TMS::TMSplineTool::ControlFaceNormal(adapter->tSpline->getTSpline(), faces[0]); + AMCAX::Vector3 nor = AMCAX::TMS::TMSplineTool::ControlFaceNormal(adapter->tSpline->getShape(), faces[0]); //EdgeFaceIndexs int p0, p1; - AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(adapter->tSpline->getTSpline(), s_id_list[i], p0, p1); - AMCAX::Point3 position0 = AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getTSpline(), p0), - position1 = AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getTSpline(), p1); + AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(adapter->tSpline->getShape(), s_id_list[i], p0, p1); + AMCAX::Point3 position0 = AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getShape(), p0), + position1 = AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getShape(), p1); AMCAX::Vector3 ve = AMCAX::Vector3(position1.Coord() - position0.Coord()); @@ -91,11 +91,11 @@ bool acamcad::SGLOperate_ExtrudeEdgeH::DoOperate(AdapterObject* adapter) AMCAX::TMS::TMSplineExtrude tool; - result = tool.CanExtrudeEdges(adapter->tSpline->getTSpline(), s_id_list); + result = tool.CanExtrudeEdges(adapter->tSpline->getShape(), s_id_list); if (result) { - tool.ExtrudeEdges(adapter->tSpline->getTSpline(), s_id_list, s_id_list_new); + tool.ExtrudeEdges(adapter->tSpline->getShape(), s_id_list, s_id_list_new); s_info_list_.resize(s_id_list_new.size()); for (int i = 0; i < s_id_list_new.size(); i++) @@ -145,17 +145,17 @@ bool acamcad::SGLOperate_ExtrudeEdgeV::DoOperate(AdapterObject* adapter) { for (int i = 0; i < s_id_list.size(); i++) { - std::vector faces = AMCAX::SubD::MeshTool::EdgeFaceIndexs(adapter->mesh->mesh(), + std::vector faces = AMCAX::SubD::MeshTool::EdgeFaceIndexs(adapter->mesh->getShape(), s_id_list[i]); - AMCAX::Vector3 nor = AMCAX::SubD::MeshTool::FaceNormal(adapter->mesh->mesh(), faces[0]); + AMCAX::Vector3 nor = AMCAX::SubD::MeshTool::FaceNormal(adapter->mesh->getShape(), faces[0]); dire_ += nor.Coord(); } dire_.Normalize(); - result = AMCAX::SubD::MeshExtrude::ExtrudeEdge(adapter->mesh->mesh(), + result = AMCAX::SubD::MeshExtrude::ExtrudeEdge(adapter->mesh->getShape(), s_id_list, s_id_list_new); @@ -175,12 +175,12 @@ bool acamcad::SGLOperate_ExtrudeEdgeV::DoOperate(AdapterObject* adapter) for (int i = 0; i < s_id_list.size(); i++) { - std::vector faces = AMCAX::TMS::TMSplineTool::EdgeFaceIndexs(adapter->tSpline->getTSpline(), + std::vector faces = AMCAX::TMS::TMSplineTool::EdgeFaceIndexs(adapter->tSpline->getShape(), s_id_list[i]); //AMCAX::TMS::TMSplineTool::ControlFaceNormal(adapter->tSpline->getTSpline(), faces[0]); - AMCAX::Vector3 nor = AMCAX::TMS::TMSplineTool::ControlFaceNormal(adapter->tSpline->getTSpline(), faces[0]); + AMCAX::Vector3 nor = AMCAX::TMS::TMSplineTool::ControlFaceNormal(adapter->tSpline->getShape(), faces[0]); dire_ += nor.Coord(); } @@ -188,11 +188,11 @@ bool acamcad::SGLOperate_ExtrudeEdgeV::DoOperate(AdapterObject* adapter) dire_.Normalize(); AMCAX::TMS::TMSplineExtrude tool; - result = tool.CanExtrudeEdges(adapter->tSpline->getTSpline(), s_id_list); + result = tool.CanExtrudeEdges(adapter->tSpline->getShape(), s_id_list); if (result) { - tool.ExtrudeEdges(adapter->tSpline->getTSpline(), s_id_list, s_id_list_new); + tool.ExtrudeEdges(adapter->tSpline->getShape(), s_id_list, s_id_list_new); s_info_list_.resize(s_id_list_new.size()); for (int i = 0; i < s_id_list_new.size(); i++) diff --git a/Src/Operation/SGLOperate_ExtrudeFace.cpp b/Src/Operation/SGLOperate_ExtrudeFace.cpp index c42465dc696642e8fa5288ec8e7a99d263c59a78..6d01d37d914bee3772799da84cf0b9b7539c4fcf 100644 --- a/Src/Operation/SGLOperate_ExtrudeFace.cpp +++ b/Src/Operation/SGLOperate_ExtrudeFace.cpp @@ -36,14 +36,14 @@ bool SGLOperate_ExtrudeFace::DoOperate(AdapterObject* object) //std::vector faces = AMCAX::SubD::MeshTool::EdgeFaceIndexs(object->mesh->mesh(), // s_id_list[i]); - AMCAX::Vector3 nor = AMCAX::SubD::MeshTool::FaceNormal(object->mesh->mesh(), s_id_list[i]); + AMCAX::Vector3 nor = AMCAX::SubD::MeshTool::FaceNormal(object->mesh->getShape(), s_id_list[i]); dire_ += nor.Coord(); } dire_.Normalize(); - result = AMCAX::SubD::MeshExtrude::ExtrudeFace(object->mesh->mesh(), + result = AMCAX::SubD::MeshExtrude::ExtrudeFace(object->mesh->getShape(), s_id_list, s_id_list_new); @@ -68,7 +68,7 @@ bool SGLOperate_ExtrudeFace::DoOperate(AdapterObject* object) //AMCAX::TMS::TMSplineTool::ControlFaceNormal(adapter->tSpline->getTSpline(), faces[0]); - AMCAX::Vector3 nor = AMCAX::TMS::TMSplineTool::ControlFaceNormal(object->tSpline->getTSpline(), s_id_list[i]); + AMCAX::Vector3 nor = AMCAX::TMS::TMSplineTool::ControlFaceNormal(object->tSpline->getShape(), s_id_list[i]); dire_ += nor.Coord(); } @@ -76,11 +76,11 @@ bool SGLOperate_ExtrudeFace::DoOperate(AdapterObject* object) dire_.Normalize(); AMCAX::TMS::TMSplineExtrude tool; - result = tool.CanExtrudeFaces(object->tSpline->getTSpline(), s_id_list); + result = tool.CanExtrudeFaces(object->tSpline->getShape(), s_id_list); if (result) { - tool.ExtrudeFaces(object->tSpline->getTSpline(), s_id_list, s_id_list_new); + tool.ExtrudeFaces(object->tSpline->getShape(), s_id_list, s_id_list_new); s_info_list_.resize(s_id_list_new.size()); for (int i = 0; i < s_id_list_new.size(); i++) diff --git a/Src/Operation/SGLOperate_FillHole.cpp b/Src/Operation/SGLOperate_FillHole.cpp index e03114c3b1c106df6ba47061a3500d0ba2eb92d9..6425b547266821964905c3d682d0a6fb99c55681 100644 --- a/Src/Operation/SGLOperate_FillHole.cpp +++ b/Src/Operation/SGLOperate_FillHole.cpp @@ -25,11 +25,11 @@ namespace acamcad break; AMCAX::TMS::TMSplineFillHole tool; - result = tool.CanFillSingleHole(adapter->tSpline->getTSpline(), ids[0]); + result = tool.CanFillSingleHole(adapter->tSpline->getShape(), ids[0]); if (result) { - tool.FillSingleHole(adapter->tSpline->getTSpline(), ids[0]); + tool.FillSingleHole(adapter->tSpline->getShape(), ids[0]); adapter->updateDraw(); } @@ -37,7 +37,7 @@ namespace acamcad break; case acamcad::SelectModel::OBJECT_MODEL: { - result = AMCAX::TMS::TMSplineFillHole().FillAllHoles(adapter->tSpline->getTSpline()); + result = AMCAX::TMS::TMSplineFillHole().FillAllHoles(adapter->tSpline->getShape()); adapter->updateDraw(); } diff --git a/Src/Operation/SGLOperate_Repair.cpp b/Src/Operation/SGLOperate_Repair.cpp index f502a042e0421d261f16897f9b4dfc546f06e0f8..46c7896b55d1b4bb2647506dd1b0440415cf1b95 100644 --- a/Src/Operation/SGLOperate_Repair.cpp +++ b/Src/Operation/SGLOperate_Repair.cpp @@ -15,7 +15,7 @@ namespace acamcad adapter->dataType() != DataType::TSPLINEU_TYPE) return false; - return AMCAX::TMS::TMSplineRepair().RepairTSpline(adapter->tSpline->getTSpline()); + return AMCAX::TMS::TMSplineRepair().RepairTSpline(adapter->tSpline->getShape()); } } diff --git a/Src/Operation/SGLOperate_ReverseNormal.cpp b/Src/Operation/SGLOperate_ReverseNormal.cpp index 9bd2f4d1c325d2c2cffed5ac063181912fe5fed2..6ac612fa466a23f31b2cc82406e66368e94cd468 100644 --- a/Src/Operation/SGLOperate_ReverseNormal.cpp +++ b/Src/Operation/SGLOperate_ReverseNormal.cpp @@ -20,13 +20,13 @@ bool acamcad::SGLOperate_ReverseNormal::DoOperate(AdapterObject* adapter) case DataType::MESH_TYPE: { result = true; - AMCAX::SubD::MeshTool::ReverseMeshNormal(adapter->mesh->mesh()); + AMCAX::SubD::MeshTool::ReverseMeshNormal(adapter->mesh->getShape()); } break; case DataType::TSPLINEU_TYPE: { result = true; - AMCAX::TMS::TMSplineTool::ReverseTMSNormal(adapter->tSpline->getTSpline()); + AMCAX::TMS::TMSplineTool::ReverseTMSNormal(adapter->tSpline->getShape()); } break; default: diff --git a/Src/Operation/SGLOperate_Separate.cpp b/Src/Operation/SGLOperate_Separate.cpp index 9c66507cdc6c919dcee7fd4d27818d9c12a09a9e..7ac0196828eff23cd030b78071fc7839b329dbd2 100644 --- a/Src/Operation/SGLOperate_Separate.cpp +++ b/Src/Operation/SGLOperate_Separate.cpp @@ -26,7 +26,7 @@ bool SGLOperate_Separate::DoOperate(AdapterObject* adapter) std::vector s_id_list = getSelectSubList(s_info_list_); //polymesh::meshSeparateEdges(mesh_object->mesh(), s_id_list); - result = AMCAX::SubD::MeshSeparate::SeparateEdges(adapter->mesh->mesh(), s_id_list); + result = AMCAX::SubD::MeshSeparate::SeparateEdges(adapter->mesh->getShape(), s_id_list); } break; case DataType::TSPLINEU_TYPE: @@ -34,7 +34,7 @@ bool SGLOperate_Separate::DoOperate(AdapterObject* adapter) std::vector s_id_list = getSelectSubList(s_info_list_); //result = true; - result = AMCAX::TMS::TMSplineSeparate().SeparateEdges(adapter->tSpline->getTSpline(), s_id_list); + result = AMCAX::TMS::TMSplineSeparate().SeparateEdges(adapter->tSpline->getShape(), s_id_list); } break; diff --git a/Src/Operation/SGLOperate_SplitFace.cpp b/Src/Operation/SGLOperate_SplitFace.cpp index 7db8c83de9f64c810747fc040ce4b02506f5d3a0..b660472ea5c9df1bc4f10c489acf886fee4671e4 100644 --- a/Src/Operation/SGLOperate_SplitFace.cpp +++ b/Src/Operation/SGLOperate_SplitFace.cpp @@ -17,10 +17,10 @@ namespace acamcad AMCAX::TMS::TMSplineSplit tool; - bool result = tool.CanSplitFacesCross(adapter->tSpline->getTSpline(), ids); + bool result = tool.CanSplitFacesCross(adapter->tSpline->getShape(), ids); if (result) { - tool.SplitFacesCross(adapter->tSpline->getTSpline(), ids); + tool.SplitFacesCross(adapter->tSpline->getShape(), ids); adapter->updateDraw(); } diff --git a/Src/Operation/SGLOperate_SplitFaceByEdge.cpp b/Src/Operation/SGLOperate_SplitFaceByEdge.cpp index 4080dcb5bdab099723f9e6151bcc77f10e4a0e3d..f2be7886e2329123bde057cbcc768c10c1b80edd 100644 --- a/Src/Operation/SGLOperate_SplitFaceByEdge.cpp +++ b/Src/Operation/SGLOperate_SplitFaceByEdge.cpp @@ -50,7 +50,7 @@ bool acamcad::SGLOperate_SplitFaceByEdge::DoOperate(AdapterObject* adapter) edge_t.push_back(AMCAX::Point3(re_points_[i])); } - result = AMCAX::SubD::MeshSplit::SplitFaceWithEdge(adapter->mesh->mesh(), edge_id, edge_t); + result = AMCAX::SubD::MeshSplit::SplitFaceWithEdge(adapter->mesh->getShape(), edge_id, edge_t); } break; case DataType::TSPLINEU_TYPE: @@ -63,10 +63,10 @@ bool acamcad::SGLOperate_SplitFaceByEdge::DoOperate(AdapterObject* adapter) } AMCAX::TMS::TMSplineSplit tool; - result = tool.CanSplitFaceWithEdge(adapter->tSpline->getTSpline(), edge_id, edge_t); + result = tool.CanSplitFaceWithEdge(adapter->tSpline->getShape(), edge_id, edge_t); if (result) { - tool.SplitFaceWithEdge(adapter->tSpline->getTSpline(), edge_id, edge_t); + tool.SplitFaceWithEdge(adapter->tSpline->getShape(), edge_id, edge_t); adapter->updateDraw(); } diff --git a/Src/Operation/SGLOperate_SplitFaceVertex.cpp b/Src/Operation/SGLOperate_SplitFaceVertex.cpp index 2b5c9f7b0b32ce6d34d9a0180152dd7676b75304..440519770fa09af4414375a10acf53a1c10aa336 100644 --- a/Src/Operation/SGLOperate_SplitFaceVertex.cpp +++ b/Src/Operation/SGLOperate_SplitFaceVertex.cpp @@ -34,7 +34,7 @@ namespace acamcad { std::vector edges = getSelectSubList(s_info_list_); - result = AMCAX::SubD::MeshSplit::SplitFaceWithVertex(adapter->mesh->mesh(), edges); + result = AMCAX::SubD::MeshSplit::SplitFaceWithVertex(adapter->mesh->getShape(), edges); } break; case DataType::TSPLINEU_TYPE: @@ -43,10 +43,10 @@ namespace acamcad AMCAX::TMS::TMSplineSplit tool; - result = tool.CanSplitFaceWithVertex(adapter->tSpline->getTSpline(), edges); + result = tool.CanSplitFaceWithVertex(adapter->tSpline->getShape(), edges); if (result) { - tool.SplitFaceWithVertex(adapter->tSpline->getTSpline(), edges); + tool.SplitFaceWithVertex(adapter->tSpline->getShape(), edges); adapter->updateDraw(); } } diff --git a/Src/Operation/SGLOperate_SplitLoop.cpp b/Src/Operation/SGLOperate_SplitLoop.cpp index 4d084f02da0d8643ddca768879f96e3e6e9e8f0f..94bdda3cb908d86452b2f5ac89906d766b7cbc18 100644 --- a/Src/Operation/SGLOperate_SplitLoop.cpp +++ b/Src/Operation/SGLOperate_SplitLoop.cpp @@ -26,31 +26,31 @@ bool acamcad::SGLOperate_SplitLoop::DoOperate(AdapterObject* adapter) case DataType::MESH_TYPE: { int p0, p1; - AMCAX::SubD::MeshTool::EdgeVertexIndexs(adapter->mesh->mesh(), s_info_list_[0].object_subselect_id_, p0, p1); + AMCAX::SubD::MeshTool::EdgeVertexIndexs(adapter->mesh->getShape(), s_info_list_[0].object_subselect_id_, p0, p1); - AMCAX::Point3 point0 = AMCAX::SubD::MeshTool::Position(adapter->mesh->mesh(), p0), - point1 = AMCAX::SubD::MeshTool::Position(adapter->mesh->mesh(), p1), + AMCAX::Point3 point0 = AMCAX::SubD::MeshTool::Position(adapter->mesh->getShape(), p0), + point1 = AMCAX::SubD::MeshTool::Position(adapter->mesh->getShape(), p1), pointT(re_points_[0]); double t = point0.Distance(pointT) / point0.Distance(point1); - result = AMCAX::SubD::MeshSplit::SplitLoop(adapter->mesh->mesh(), s_info_list_[0].object_subselect_id_, 1 - t); + result = AMCAX::SubD::MeshSplit::SplitLoop(adapter->mesh->getShape(), s_info_list_[0].object_subselect_id_, 1 - t); } break; case DataType::TSPLINEU_TYPE: { int p0, p1; - AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(adapter->tSpline->getTSpline(), s_info_list_[0].object_subselect_id_, + AMCAX::TMS::TMSplineTool::EdgeVertexIndexs(adapter->tSpline->getShape(), s_info_list_[0].object_subselect_id_, p0, p1); - AMCAX::Point3 point0 = AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getTSpline(), p0), - point1 = AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getTSpline(), p1), + AMCAX::Point3 point0 = AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getShape(), p0), + point1 = AMCAX::TMS::TMSplineTool::ControlPosition(adapter->tSpline->getShape(), p1), pointT(re_points_[0]); double t = point0.Distance(pointT) / point0.Distance(point1); AMCAX::TMS::TMSplineSplit tool; - result = tool.CanSplitFaceLoop(adapter->tSpline->getTSpline(), s_info_list_[0].object_subselect_id_, t); + result = tool.CanSplitFaceLoop(adapter->tSpline->getShape(), s_info_list_[0].object_subselect_id_, t); if (result) { - tool.SplitFaceLoop(adapter->tSpline->getTSpline(), s_info_list_[0].object_subselect_id_, t); + tool.SplitFaceLoop(adapter->tSpline->getShape(), s_info_list_[0].object_subselect_id_, t); adapter->updateDraw(); } //result = AMCAX::TMS::TMSplineSplit().SplitFaceLoop(adapter->tSpline->getTSpline(), 1 - t); diff --git a/Src/Operation/SGLOperate_Thicken.cpp b/Src/Operation/SGLOperate_Thicken.cpp index 13239d1a8f9efe3b0162c6f3d7e241a8b6387a9f..9aed0b5acf33d6f0abe93224d85b0e4e8142dc87 100644 --- a/Src/Operation/SGLOperate_Thicken.cpp +++ b/Src/Operation/SGLOperate_Thicken.cpp @@ -21,11 +21,11 @@ namespace acamcad case DataType::MESH_TYPE: //AMCAX::SubD::MeshTool::MeshTriangles(adapter->mesh->mesh()); //result = true; - result = AMCAX::SubD::MeshOffset::ThickenMesh(adapter->mesh->mesh(), 0.2); + result = AMCAX::SubD::MeshOffset::ThickenMesh(adapter->mesh->getShape(), 0.2); break; case DataType::TSPLINEU_TYPE: { - result = AMCAX::TMS::TMSplineThicken().ThickenTMSplins(adapter->tSpline->getTSpline(), 0.2); + result = AMCAX::TMS::TMSplineThicken().ThickenTMSplins(adapter->tSpline->getShape(), 0.2); } break; diff --git a/Src/Operation/SGLOperate_TriangulateMesh.cpp b/Src/Operation/SGLOperate_TriangulateMesh.cpp index 3a6d70feb94ea0f14292512cc93cba7427cc4aab..82ea674bf0e25c152d1d12b6220da8c69ff3e79d 100644 --- a/Src/Operation/SGLOperate_TriangulateMesh.cpp +++ b/Src/Operation/SGLOperate_TriangulateMesh.cpp @@ -19,7 +19,7 @@ namespace acamcad switch (adapter->dataType()) { case DataType::MESH_TYPE: - AMCAX::SubD::MeshTool::MeshTriangles(adapter->mesh->mesh()); + AMCAX::SubD::MeshTool::MeshTriangles(adapter->mesh->getShape()); result = true; break; default: diff --git a/Src/Operation/SGLOperate_Uniform.cpp b/Src/Operation/SGLOperate_Uniform.cpp index 13baf1ecab1dc0d66239ce088abb9ae8ecad5a5a..de573dee5abf57b28881809c442d904ea3551cb7 100644 --- a/Src/Operation/SGLOperate_Uniform.cpp +++ b/Src/Operation/SGLOperate_Uniform.cpp @@ -17,7 +17,7 @@ namespace acamcad AMCAX::TMS::TMSplineRepair repair; - bool result = repair.UniformTSpline(object->tSpline->getTSpline()); + bool result = repair.UniformTSpline(object->tSpline->getShape()); if (result) { diff --git a/Src/Operation/SGLOperate_WeldEdge.cpp b/Src/Operation/SGLOperate_WeldEdge.cpp index c51b546735d94a2791a05961a15c846d743b4705..c432c6d3e53336aac5f93ef6fa299afcb37794a3 100644 --- a/Src/Operation/SGLOperate_WeldEdge.cpp +++ b/Src/Operation/SGLOperate_WeldEdge.cpp @@ -22,10 +22,10 @@ bool SGLOperate_WeldEdge::DoOperate(AdapterObject* adapter) { std::vector s_id_list = getSelectSubList(s_info_list_); - result = AMCAX::SubD::MeshWeld::FilterVoidWeldEdges(adapter->mesh->mesh(), s_id_list); + result = AMCAX::SubD::MeshWeld::FilterVoidWeldEdges(adapter->mesh->getShape(), s_id_list); if (result) { - AMCAX::SubD::MeshWeld::MeshWeldEdges(adapter->mesh->mesh(), s_id_list); + AMCAX::SubD::MeshWeld::MeshWeldEdges(adapter->mesh->getShape(), s_id_list); } } break; @@ -34,10 +34,10 @@ bool SGLOperate_WeldEdge::DoOperate(AdapterObject* adapter) std::vector s_id_list = getSelectSubList(s_info_list_); AMCAX::TMS::TMSplineWeld tool; - result = tool.FilterVoidWeldEdges(adapter->tSpline->getTSpline(), s_id_list); + result = tool.FilterVoidWeldEdges(adapter->tSpline->getShape(), s_id_list); if (result) { - tool.WeldEdges(adapter->tSpline->getTSpline(), s_id_list); + tool.WeldEdges(adapter->tSpline->getShape(), s_id_list); adapter->updateDraw(); } diff --git a/Src/Record/Record.cpp b/Src/Record/Record.cpp deleted file mode 100644 index c53649ddfee66da8375ab7d5e12d2d0b1ba0e0e3..0000000000000000000000000000000000000000 --- a/Src/Record/Record.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. -* All rights reserved. -* -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this -* software under the terms of either license. -* -* For more information about the AGPLv3.0 license, please visit: -* https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please -* contact Hefei Jiushao Intelligent Technology Co., Ltd. -* =================================================================== -* Author: -*/ -#include "Record.h" - -#include -#include -#include -#include -#include - -namespace acamcad -{ - Record::Step::Step(int stepNum, int object_id, std::string action, std::string lable) :stepNum(stepNum),object_id(object_id),action(action), lable(lable) - { - } - Record::Record() { - - } - Record::~Record() { - - } - - bool startsWith(const std::string& base, const std::string& sub) - { - return base.rfind(sub, 0) == 0; - } - - std::vector parsexyz(const std::string base) { - std::vector xyz; - int nPos = 0; - std::string str = base; - nPos = str.find("("); - str = str.substr(nPos + 1); - nPos = str.find(")"); - str = str.substr(0,nPos); - nPos = str.find(","); - while (-1 != nPos) - { - std::string temp = str.substr(0,nPos); - double d = 0; - d = std::stold(temp); - xyz.emplace_back(d); - str = str.substr(nPos + 1); - nPos = str.find(","); - } - xyz.emplace_back(std::stold(str)); - return xyz; - } - - void Record::recordCylinder(const int object_id, const AMCAX::Coord3 & b_center, const AMCAX::Coord3 & axis, const AMCAX::Coord3 & xaxis,double radius, double height, double angle,const std::string& label) { - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_PRIM_API_MAKE_CYLINDER, label)); - object_num++; - } - void Record::recordCone(const int object_id, const AMCAX::Coord3 & b_center, const AMCAX::Coord3 & axis, const AMCAX::Coord3 & xaxis,double radiusR, double radiusr, double height,double angle, const std::string& label) { - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_PRIM_API_MAKE_CONE, label)); - object_num++; - } - void Record::recordCube(const int object_id, const AMCAX::Coord3 & first, const AMCAX::Coord3 & second, const std::string& label) { - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1 , object_id, BREP_PRIM_API_MAKE_CUBE, label)); - object_num++; - } - void Record::recordTorus(const int object_id, const AMCAX::Coord3 & center, const AMCAX::Coord3 & axis, const AMCAX::Coord3 & xaxis, double radius0, double radius1, double angle, const std::string& label) { - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_PRIM_API_MAKE_TORUS, label)); - object_num++; - } - void Record::recordSphere(const int object_id, const AMCAX::Coord3 & center, double radius,double angle1, double angle2, double angle3,const std::string& label) { - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_PRIM_API_MAKE_SPHERE, label)); - object_num++; - } - void Record::recordPlane(const int object_id, const AMCAX::Coord3 & first, const AMCAX::Coord3 & second, const std::string& label) { - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_PRIM_API_MAKE_PLANE, label)); - object_num++; - - } - void Record::recordPolygon(const int object_id, const std::vector& points, const std::string& label) { - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_BUILDER_API_MAKE_POLYGON, label)); - } - void Record::recordPrism(const int object_id, const std::vector& points, const AMCAX::Coord3& prism_axis, const std::string& label) { - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_PRIM_API_MAKE_PRISM, label)); - object_num++; - } - int Record::getStepByObject(int id) { - int object_id = id; - int stepId = 0; - for (int i = steps.size() - 1; i >= 0; i--) - { - if (steps[i].object_id == object_id) - { - stepId = steps[i].stepNum; - break; - } - } - return stepId; - } - void Record::recordMove(const int object_id, const AMCAX::Coord3d & vec){ - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_BUILDER_API_TRANSFORM, "move")); - } - - void Record::recorRotation(const int object_id, const AMCAX::Coord3 & center, const AMCAX::Coord3 & axis,const double angle ) { - if (object_id <= 0) return; - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_BUILDER_API_ROTATION, "rotation")); - } - - void Record::recordDelete(BaseObject* obj) { - steps.push_back(Record::Step(steps.size() + 1, obj->id(), BREP_ALGO_API_DELETE, "delete")); - } - void Record::recordFuse(const int object_id, std::vector persistent_id_list) { - if (object_id <= 0) return; - - std::vector stepList; - for (int id : persistent_id_list) - { - stepList.emplace_back(getStepByObject(id)); - } - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_ALGO_API_FUSE, "fuse")); - object_num++; - } - void Record::recordCommon(const int object_id, std::vector persistent_id_list) { - if (object_id <= 0) return; - - std::vector stepList; - for (int id : persistent_id_list) - { - stepList.emplace_back(getStepByObject(id)); - } - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_ALGO_API_COMMON, "common")); - object_num++; - } - void Record::recordCut(const int object_id, std::vector persistent_id_list) { - if (object_id <= 0) return; - - std::vector stepList; - for (int id : persistent_id_list) - { - stepList.emplace_back(getStepByObject(id)); - } - - steps.push_back(Record::Step(steps.size() + 1, object_id, BREP_ALGO_API_CUT, "cut")); - object_num++; - } -} \ No newline at end of file diff --git a/Src/Record/Record.h b/Src/Record/Record.h deleted file mode 100644 index d9c587fbb315f9734ffec56ddf7163aaf73e7a49..0000000000000000000000000000000000000000 --- a/Src/Record/Record.h +++ /dev/null @@ -1,98 +0,0 @@ -/* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. -* All rights reserved. -* -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this -* software under the terms of either license. -* -* For more information about the AGPLv3.0 license, please visit: -* https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please -* contact Hefei Jiushao Intelligent Technology Co., Ltd. -* =================================================================== -* Author: -*/ -#pragma once -#include -#include - -#include -#include -#include "../Object/BaseObject.h" -#define BREP_PRIM_API_MAKE_PLANE "BRepPrimAPI_MakePlane" -#define BREP_PRIM_API_MAKE_CUBE "BRepPrimAPI_MakeBox" -#define BREP_PRIM_API_MAKE_SPHERE "BRepPrimAPI_MakeSphere" -#define BREP_PRIM_API_MAKE_CYLINDER "BRepPrimAPI_MakeCylinder" -#define BREP_PRIM_API_MAKE_CONE "BRepPrimAPI_MakeCone" -#define BREP_PRIM_API_MAKE_TORUS "BRepPrimAPI_MakeTorus" -#define BREP_PRIM_API_MAKE_PRISM "BRepPrimAPI_MakePrism" -#define BREP_PRIM_API_MAKE_ROUNDEDPRISM "BRepPrimAPI_MakeRoundedPrism" -#define BREP_PRIM_API_MAKE_WEDGE "BRepPrimAPI_MakeWedge" -#define BREP_BUILDER_API_MAKE_EDGE "BRepBuilderAPI_MakeEdge" -#define BREP_BUILDER_API_MAKE_WIRE "BRepBuilderAPI_MakeWire" -#define BREP_BUILDER_API_MAKE_FACE "BRepBuilderAPI_MakeFace" -#define BREP_BUILDER_API_MAKE_POLYGON "BRepBuilderAPI_MakePolygon" -#define BREP_BUILDER_API_TRANSFORM "BRepBuilderAPI_Transform" -#define BREP_BUILDER_API_ROTATION "BRepBuilderAPI_Rotation" -#define BREP_ALGO_API_DELETE "BRepAlgoAPI_Delete" -#define BREP_ALGO_API_FUSE "BRepAlgoAPI_Fuse" -#define BREP_ALGO_API_COMMON "BRepAlgoAPI_Common" -#define BREP_ALGO_API_CUT "BRepAlgoAPI_Cut" -#define JSON_KEYWORD_ACTION "action" -#define JSON_KEYWORD_STEP "step" -#define JSON_KEYWORD_ID "id" -#define JSON_KEYWORD_DESCRIPTION "description" -#define JSON_KEYWORD_ARGS "args" -#define JSON_KEYWORD_LABEL "lable" -#define BREP_BUILDER_API_TRANSFORM_SHAPE_PREFIX_L "shape" -#define BREP_BUILDER_API_TRANSFORM_SHAPE_PREFIX_U "Shape" -#define BREP_BUILDER_API_TRANSFORM_SHAPES_PREFIX_L "shapes" -#define BREP_BUILDER_API_TRANSFORM_SHAPES_PREFIX_U "Shapes" - -#define JSON_KEYWORD_SHAPE "shape" -#define JSON_KEYWORD_VECTOR "vec" -#define JSON_KEYWORD_VECTOR_X "vector_x" -#define JSON_KEYWORD_VECTOR_Y "vector_y" -#define JSON_KEYWORD_VECTOR_Z "vector_z" - -namespace acamcad -{ - - class Record - { - public: - struct Step - { - int stepNum; - int object_id; - std::string action; - std::string lable; - Step(int stepNum, int object_id,std::string action, std::string lable); - }; - private: - std::vector steps; - int object_num = 0; - public: - void recordCylinder(const int id, const AMCAX::Coord3 & b_center, const AMCAX::Coord3 & axis, const AMCAX::Coord3 & xaxis, double radius, double height, double angle, const std::string& label); - void recordCone(const int id, const AMCAX::Coord3 & b_center, const AMCAX::Coord3 & axis, const AMCAX::Coord3 & xaxis, double radiusR, double radiusr, double height, double angle, const std::string& label); - void recordCube(const int id, const AMCAX::Coord3 & first, const AMCAX::Coord3 & second, const std::string& label); - void recordTorus(const int id, const AMCAX::Coord3 & center, const AMCAX::Coord3 & axis, const AMCAX::Coord3 & xaxis, double radius0, double radius1, double angle, const std::string& label); - void recordSphere(const int id, const AMCAX::Coord3 & center, double radius,double angle1, double angle2, double angle3,const std::string& label); - void recordPlane(const int id, const AMCAX::Coord3 & first, const AMCAX::Coord3 & second, const std::string& label); - void recordPolygon(const int id, const std::vector& points, const std::string& label); - void recordPrism(const int id, const std::vector& points, const AMCAX::Coord3& prism_axis, const std::string& label); - int getStepByObject(int id); - void recordMove(const int id, const AMCAX::Coord3d & vec); - void recorRotation(const int object_id, const AMCAX::Coord3 & center, const AMCAX::Coord3 & axis, const double angle); - void recordDelete(BaseObject* obj); - void recordFuse(const int object_id, std::vector persistent_id_list); - void recordCommon(const int object_id, std::vector persistent_id_list); - void recordCut(const int object_id, std::vector persistent_id_list); - - public: - Record(); - ~Record(); - - }; -} \ No newline at end of file diff --git a/Src/Windows/ACAMWindow.cpp b/Src/Windows/ACAMWindow.cpp deleted file mode 100644 index d0dbfc0d6375c5e93df03473f80642dbd5172e61..0000000000000000000000000000000000000000 --- a/Src/Windows/ACAMWindow.cpp +++ /dev/null @@ -1,580 +0,0 @@ -/* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. -* All rights reserved. -* -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this -* software under the terms of either license. -* -* For more information about the AGPLv3.0 license, please visit: -* https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please -* contact Hefei Jiushao Intelligent Technology Co., Ltd. -* =================================================================== -* Author: -*/ -#include "ACAMWindow.h" -#include -#include -#include -#include -#include -#include -#include - -#include "RenderViewer.h" -#include "../Core/DataManager.h" -#include "../Core/ACAMCore.h" -#include "OperateToolbar.h" -#include "../Core/UndoRedoHandler.h" - -using namespace acamcad; - - -ACAMWindow::ACAMWindow(QWidget* parent) - : QMainWindow(parent) -{ - ui.setupUi(this); - - size_t icon_size = 36; - - this->setIconSize(QSize(icon_size, icon_size)); - this->setWindowIcon(QIcon(":images/images/logo.ico")); - - initCoreAndViewer(); - - createAction(); - createMenus(); - createToolBars(); - - OperateToolBar* tab_widget = new OperateToolBar(render_viewer_, this); - connect(tab_widget, &OperateToolBar::currentChanged, this, &ACAMWindow::slotCurrentChanged); - - render_viewer_->selectCheckChanged(); - - QVBoxLayout* layout_main = new QVBoxLayout; - - size_t bar_height = tab_widget->tabBar()->height(); - - tab_widget->setMaximumHeight(icon_size + bar_height + 2); - - layout_main->addWidget(tab_widget, 0, Qt::AlignTop); - layout_main->addWidget(render_viewer_, 10); - - this->centralWidget()->setLayout(layout_main); - - this->resize(1280, 750); - - slotCurrentChanged(1); - - //instance = this; - - //ShowMessge("Selet dd", acamcad::DataType::BREP_TYPE | acamcad::DataType::TSPLINEU_TYPE); - - ///statusBar()->showMessage("XXXXXXXXXXXXXXXXXXX!!!"); - -} - -ACAMWindow::~ACAMWindow() -{ - delete render_viewer_; - delete dataManager_; - //delete undoRedoHandler_; - delete core_; -} - -void ACAMWindow::initCoreAndViewer() -{ - dataManager_ = new DataManager(); - - //undoRedoHandler_ = new UndoRedoHandler(dataManager_, 50); - - core_ = new acamcad::AMCore(); - core_->setDataManager(dataManager_); - //core_->setUndoRedoHandler(undoRedoHandler_); - - render_viewer_ = new acamcad::RenderViewer(this); - render_viewer_->setDataManger(dataManager_); - render_viewer_->setCore(core_); -} - -void ACAMWindow::createAction() -{ - // menu actions - createAction_File(); - createAction_Edit(); - createAction_Help(); - - // left side toolbar actions - createAction_LeftTools(); -} - -void ACAMWindow::createAction_File() -{ - openAction_ = new QAction(tr("Open")); - openAction_->setStatusTip(tr("Open a file")); - connect(openAction_, &QAction::triggered, core_, &AMCore::loadObjects); - - saveAction_ = new QAction(tr("Save")); - saveAction_->setStatusTip(tr("Save to file")); - connect(saveAction_, &QAction::triggered, core_, QOverload<>::of(&AMCore::saveObjects)); - - exportAction_ = new QAction(tr("Export Selected")); - exportAction_->setStatusTip(tr("Export selected models")); - connect(exportAction_, &QAction::triggered, core_, &AMCore::saveSelectedObjects); - - // TODO: - exitAction_ = new QAction(tr("Exit")); - exitAction_->setStatusTip(tr("Exit the program")); - connect(exitAction_, &QAction::triggered, this, &ACAMWindow::close); - // check if unsaved... -} - -void ACAMWindow::createAction_Edit() -{ - undoAction_ = new QAction(tr("Undo")); - undoAction_->setStatusTip(tr("Undo")); - connect(undoAction_, &QAction::triggered, render_viewer_, &RenderViewer::slotUndo); - - redoAction_ = new QAction(tr("Redo")); - redoAction_->setStatusTip(tr("Redo")); - connect(redoAction_, &QAction::triggered, render_viewer_, &RenderViewer::slotRedo); - - - //undoAction_->setEnabled(false); -} - -void ACAMWindow::createAction_Help() -{ - aboutAction_ = new QAction(tr("About")); - aboutAction_->setStatusTip("About us"); - // TODO connect -} - - -void ACAMWindow::createAction_LeftTools() -{ - chamferAction_ = new QAction("Chamfer Edges"); - chamferAction_->setStatusTip(tr("(Support Brep)select an edge to chamfer")); - chamferAction_->setIcon(QIcon(":images/images/Chamfer.png")); - connect(chamferAction_, &QAction::triggered, this, &ACAMWindow::slotChamfer); - - deleteFaceAction_ = new QAction("Delete Face"); - deleteFaceAction_->setStatusTip(tr("(Support Mesh)Delete a face from mesh, the define is face, Enter sure")); - deleteFaceAction_->setIcon(QIcon(":images/images/DeleteFace.png")); - connect(deleteFaceAction_, &QAction::triggered, this, &ACAMWindow::slotDeleteElement); - - embedAction_ = new QAction("Embed face"); - embedAction_->setStatusTip( - tr("(Support Mesh)Embed a face to mesh, select connected boundary vert or connected boundary edge, enter sure")); - embedAction_->setIcon(QIcon(":images/images/EmbedFace.png")); - connect(embedAction_, &QAction::triggered, this, &ACAMWindow::slotEmbedFace); - - - separateAction_ = new QAction("Separate"); - deleteFaceAction_->setStatusTip(tr("(Support Mesh)Sperate a face from mesh , select vertex or edge, Enter sure")); - separateAction_->setIcon(QIcon(":images/images/Separate.png")); - connect(separateAction_, &QAction::triggered, this, &ACAMWindow::slotSeparateEdge); - - weldAction_ = new QAction("Weld"); - deleteFaceAction_->setStatusTip(tr("(Support Mesh)Weld a edge to mesh , select edge, Enter sure")); - weldAction_->setIcon(QIcon(":images/images/Weld.png")); - connect(weldAction_, &QAction::triggered, this, &ACAMWindow::slotWeldEdge); - - - - splitLoopAction_ = new QAction("Split loop"); - splitLoopAction_->setStatusTip(tr("Split a mesh loop")); - splitLoopAction_->setIcon(QIcon(":images/images/SplitLoop.png")); - connect(splitLoopAction_, &QAction::triggered, this, &ACAMWindow::slotSplitLoop); - - //splitEdgeAction_ = new QAction("Split edge"); - //splitEdgeAction_->setStatusTip(tr("Split a edge")); - //splitEdgeAction_->setIcon(QIcon(":images/images/SplitEdge.png")); - //connect(splitEdgeAction_, &QAction::triggered, this, &ACAMWindow::slotSplitEdge); - splitFaceAction_ = new QAction("split face"); - splitFaceAction_->setStatusTip(tr("split face cross")); - splitFaceAction_->setIcon(QIcon(":images/images/SplitFace1.png")); - connect(splitFaceAction_, &QAction::triggered, this, &ACAMWindow::slotSplitFace); - - - splitFaceEdgeAction_ = new QAction("Split Face by Edge"); - splitFaceEdgeAction_->setStatusTip(tr("Split Face by select some edge")); - splitFaceEdgeAction_->setIcon(QIcon(":images/images/SplitEdgeOnly.png")); - connect(splitFaceEdgeAction_, &QAction::triggered, this, &ACAMWindow::slotSplitFaceByEdge); - - - splitFaceVertAction_ = new QAction("Cut face"); - splitFaceVertAction_->setStatusTip(tr("Cat the face by select vert")); - splitFaceVertAction_->setIcon(QIcon(":images/images/SplitFace.png")); - connect(splitFaceVertAction_, &QAction::triggered, this, &ACAMWindow::slotSplitFaceByVert); - - //collapseEdgeAction_ = new QAction("combine Vert"); - //collapseEdgeAction_->setStatusTip(tr("Combine ywo Vertex")); - //collapseEdgeAction_->setIcon(QIcon(":images/images/CollapseEdge.png")); - //connect(collapseEdgeAction_, &QAction::triggered, this, &ACAMWindow::slotCollapseEdge); - - //swapEdgeAction_ = new QAction("Swap Edge"); - //swapEdgeAction_->setStatusTip(tr("Swap a mesh edge")); - //swapEdgeAction_->setIcon(QIcon(":images/images/SwapEdge.png")); - //connect(swapEdgeAction_, &QAction::triggered, this, &ACAMWindow::slotSwapMeshEdge); - - - bridgeEdgeAction_ = new QAction("Bridge edge"); - bridgeEdgeAction_->setStatusTip(tr("Bridge to edge, select two not connected boudnary edge, enter sure")); - bridgeEdgeAction_->setIcon(QIcon(":images/images/BridgeEdge.png")); - connect(bridgeEdgeAction_, &QAction::triggered, this, &ACAMWindow::slotBridgeEdge); - - extrudeEdgeVAction_ = new QAction("Extrude edge Vertical"); - extrudeEdgeVAction_->setStatusTip(tr("Select some edge, use Ctrl+left to extrude edge, direction vartical face")); - extrudeEdgeVAction_->setIcon(QIcon(":images/images/ExtrudeEdgeVertical.png")); - connect(extrudeEdgeVAction_, &QAction::triggered, this, &ACAMWindow::slotExtrudeEdgeV); - - extrudeEdgeHAction_ = new QAction("Extrude edge Horizontal"); - extrudeEdgeHAction_->setStatusTip(tr("Select some edge, use Ctrl+left to extrude edge, direction horizontal face")); - extrudeEdgeHAction_->setIcon(QIcon(":images/images/ExtrudeEdgeHorizontal.png")); - connect(extrudeEdgeHAction_, &QAction::triggered, this, &ACAMWindow::slotExtrudeEdgeH); - - //chamferAction_->setVisible(false); - - thickenAction_ = new QAction("Thicken Mesh"); - thickenAction_->setStatusTip(tr("Thicken a mesh")); - thickenAction_->setIcon(QIcon(":images/images/Thicken.png")); - connect(thickenAction_, &QAction::triggered, this, &ACAMWindow::slotThicken); - - normalAction_ = new QAction("Reverse Mesh Normal"); - normalAction_->setStatusTip(tr("Reverse Mesh Normal")); - normalAction_->setIcon(QIcon(":images/images/ReverseNormal.png")); - connect(normalAction_, &QAction::triggered, this, &ACAMWindow::slotMeshReverse); - - fillHoleAction_ = new QAction("FillHole"); - fillHoleAction_->setStatusTip(tr("Try to FillHole")); - fillHoleAction_->setIcon(QIcon(":images/images/FillHole.png")); - connect(fillHoleAction_, &QAction::triggered, this, &ACAMWindow::slotFillHole); - - filletAction_ = new QAction("Fillet Edges"); - filletAction_->setStatusTip(tr("(Support Brep)select an edge to fillet")); - filletAction_->setIcon(QIcon(":images/images/fillet.png")); - connect(filletAction_, &QAction::triggered, this, &ACAMWindow::slotFillet); - - extrudAction_ = new QAction("Extrude Face"); - extrudAction_->setStatusTip(tr("(Support Brep)select a face to extrude(Support Mesh)")); - extrudAction_->setIcon(QIcon(":images/images/ExtrudeFace.png")); - connect(extrudAction_, &QAction::triggered, render_viewer_, &RenderViewer::slotBRepExtrude); - - extrudActionMesh_ = new QAction("Extrude Face Mesh"); - extrudActionMesh_->setStatusTip(tr("(Support Brep)select a face to extrude(Support Mesh)")); - extrudActionMesh_->setIcon(QIcon(":images/images/ExtrudeFace.png")); - connect(extrudActionMesh_, &QAction::triggered, this, &ACAMWindow::slotExtrudeFace); - - bool_fuseAction_ = new QAction("Fuse Object"); - bool_fuseAction_->setStatusTip(tr("(Support Brep)Fuse more than one Object to a one Object")); - bool_fuseAction_->setIcon(QIcon(":images/images/BoolFuse.png")); - connect(bool_fuseAction_, SIGNAL(triggered()), this, SLOT(slotBoolFuseObject())); - - bool_cutAction_ = new QAction("Cut Object"); - bool_cutAction_->setStatusTip(tr("(Support Brep)one object Cut other more than one Object")); - bool_cutAction_->setIcon(QIcon(":images/images/BoolCut.png")); - connect(bool_cutAction_, SIGNAL(triggered()), this, SLOT(slotBoolCutObject())); - - bool_CommonAction_ = new QAction("Common Object"); - bool_CommonAction_->setStatusTip(tr("(Support Brep)find more than one Object' common Object")); - bool_CommonAction_->setIcon(QIcon(":images/images/BoolCommon.png")); - connect(bool_CommonAction_, SIGNAL(triggered()), this, SLOT(slotBoolCommonObject())); - - - duplicateAction_ = new QAction("duplicate"); -} - - - -//================================================================ - -void ACAMWindow::createMenus() -{ - fileMenu_ = menuBar()->addMenu(tr("&File")); - fileMenu_->addAction(openAction_); - fileMenu_->addAction(saveAction_); - fileMenu_->addAction(exportAction_); - fileMenu_->addSeparator(); - fileMenu_->addAction(exitAction_); - - editMenu_ = menuBar()->addMenu(tr("Edit")); - editMenu_->addAction(undoAction_); - editMenu_->addAction(redoAction_); - - helpMenu_ = menuBar()->addMenu(tr("Help")); - helpMenu_->addAction(aboutAction_); -} - -void ACAMWindow::createToolBars() -{ - //============================================================= - QToolBar* editToolBar_ = new QToolBar(this); - editToolBar_->setObjectName(QString::fromUtf8("Edit")); - this->addToolBar(Qt::LeftToolBarArea, editToolBar_); - - - - editToolBar_->addAction(deleteFaceAction_); - list_actions_.push_back(deleteFaceAction_); - editToolBar_->addAction(embedAction_); - list_actions_.push_back(embedAction_); - editToolBar_->addAction(separateAction_); - list_actions_.push_back(separateAction_); - editToolBar_->addAction(weldAction_); - list_actions_.push_back(weldAction_); - - editToolBar_->addSeparator(); - - //===================================== - editToolBar_->addAction(splitLoopAction_); - list_actions_.push_back(splitLoopAction_); - editToolBar_->addAction(splitFaceAction_); - list_actions_.push_back(splitFaceAction_); - //editToolBar_->addAction(splitEdgeAction_); - editToolBar_->addAction(splitFaceEdgeAction_); - list_actions_.push_back(splitFaceEdgeAction_); - editToolBar_->addAction(splitFaceVertAction_); - list_actions_.push_back(splitFaceVertAction_); - //editToolBar_->addAction(collapseEdgeAction_); - //editToolBar_->addAction(swapEdgeAction_); - - editToolBar_->addAction(chamferAction_); - list_actions_.push_back(chamferAction_); - editToolBar_->addAction(filletAction_); - list_actions_.push_back(filletAction_); - editToolBar_->addAction(bridgeEdgeAction_); - list_actions_.push_back(bridgeEdgeAction_); - editToolBar_->addAction(extrudeEdgeVAction_); - list_actions_.push_back(extrudeEdgeVAction_); - editToolBar_->addAction(extrudeEdgeHAction_); - list_actions_.push_back(extrudeEdgeHAction_); - editToolBar_->addAction(extrudAction_); - list_actions_.push_back(extrudAction_); - editToolBar_->addAction(extrudActionMesh_); - list_actions_.push_back(extrudActionMesh_); - - editToolBar_->addSeparator(); - - editToolBar_->addAction(thickenAction_); - list_actions_.push_back(thickenAction_); - editToolBar_->addAction(normalAction_); - list_actions_.push_back(normalAction_); - editToolBar_->addAction(fillHoleAction_); - list_actions_.push_back(fillHoleAction_); - - - editToolBar_->addAction(bool_fuseAction_); - list_actions_.push_back(bool_fuseAction_); - editToolBar_->addAction(bool_cutAction_); - list_actions_.push_back(bool_cutAction_); - editToolBar_->addAction(bool_CommonAction_); - list_actions_.push_back(bool_CommonAction_); -} - -//================================================================ -#include -#include -#include -#include -#include -#include - -//================================================================ - -void ACAMWindow::slotBoolFuseObject() -{ - BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshFuse); -} - -void ACAMWindow::slotBoolCutObject() -{ - BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshCut); -} - -void ACAMWindow::slotBoolCommonObject() -{ - BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshCommon); -} - -void ACAMWindow::slotChamfer() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshChamferEdge); -} - -void ACAMWindow::slotEmbedFace() -{ - BaseObjectOperation(SelectModel::VERTEX_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshEmbedFace); -} - -void ACAMWindow::slotDeleteElement() -{ - BaseObjectOperation(SelectModel::FACE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshDeleteElement); -} - -void ACAMWindow::slotSeparateEdge() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshSeparate); -} - -void ACAMWindow::slotWeldEdge() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshWeldEdge); -} - -void ACAMWindow::slotFillet() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshFilletEdge); -} - - -void ACAMWindow::slotSplitEdge() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::PICK_OPERATION_MOVE, MeshOperationType::MeshSplitEdge); -} - -void ACAMWindow::slotSplitLoop() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::PICK_OPERATION_MOVE, MeshOperationType::MeshSplitLoop); -} - -void ACAMWindow::slotSplitFace() -{ - BaseObjectOperation(SelectModel::FACE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshSplitFace); -} - -void ACAMWindow::slotSplitFaceByEdge() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::PICK_OPERATION_MOVE_MULTI, MeshOperationType::MeshSplitFaceByEdge); -} - - -void ACAMWindow::slotSplitFaceByVert() -{ - BaseObjectOperation(SelectModel::VERTEX_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshSplitFaceVertex); -} - - -void ACAMWindow::slotSwapMeshEdge() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::PICK_OPERATION_SINGLE, MeshOperationType::MeshSwapEdge); -} - -void ACAMWindow::slotCombineTwoFace() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::PICK_OPERATION_SINGLE, MeshOperationType::MeshCombineFace); -} - -void ACAMWindow::slotCollapseEdge() -{ - BaseObjectOperation(SelectModel::VERTEX_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshCollapseEdge); -} - -void ACAMWindow::slotBridgeEdge() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshBridgeEdge); -} - -void ACAMWindow::slotExtrudeEdgeV() -{ - BaseObjectOperationSelect(SelectModel::EDGE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeEdgeVertical); -} - -void ACAMWindow::slotExtrudeEdgeH() -{ - BaseObjectOperationSelect(SelectModel::EDGE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeEdgeHorizontal); -} - - -void ACAMWindow::slotThicken() -{ - BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshThicken); -} - -void ACAMWindow::slotMeshReverse() -{ - BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshReverseNormal); -} - -void ACAMWindow::slotFillHole() -{ - BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshFillHole); -} - -//========================================================================== - - -void ACAMWindow::BaseObjectOperation(const SelectModel& s_model, const OperationType& op_type, const MeshOperationType mesh_operate_type) -{ - render_viewer_->clearSelected(); - render_viewer_->setSelectModel(s_model); - render_viewer_->setOperateType(op_type); - - core_->setMeshOperationType(mesh_operate_type); - render_viewer_->selectCheckChanged(); - render_viewer_->update(); -} - -void ACAMWindow::BaseObjectOperationSelect(const SelectModel& s_model, const OperationType& op_type, const MeshOperationType mesh_operate_type) -{ - if (render_viewer_->getSelectModel() != s_model) - render_viewer_->clearSelected(); - - render_viewer_->setSelectModel(s_model); - render_viewer_->setOperateType(op_type); - - core_->setMeshOperationType(mesh_operate_type); - - render_viewer_->selectCheckChanged(); - render_viewer_->update(); -} -void ACAMWindow::slotExtrudeFace() -{ - BaseObjectOperationSelect(SelectModel::FACE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeFaceNew); -} - -void ACAMWindow::slotCurrentChanged(int index) -{ - std::vector> array = { - { - //true,true,true,true,true, - //true,true,true,true,true, - //true,true,true,true,true, - //true,true,true,true,true, - //true - }, - //BRep - { - false,false,false,false,false, - false,false,false,true,true, - false,false,false,true,false, - false,false,false,true,true, - true - }, - //Mesh - { - true,true,true,true,true , - false,true,true,false,false, - true,true,true,false,true, - true,true,true,false,false, - false - }, - //TSpline - { - true,true,true,true ,true, - true,true,true,true,false, - true,true,true,false,true, - true,true,true,false,false, - false - }, - }; - - for (int i = 0; i < array[index].size(); i++) - { - list_actions_[i]->setVisible(array[index][i]); - } - - - ///std::cout << "ACAMWindow::slotCurrentChanged!!" << list_actions_.size() << std::endl; -} \ No newline at end of file diff --git a/Src/Windows/ACAMWindow.h b/Src/Windows/ACAMWindow.h deleted file mode 100644 index 1d00c7f2c9263153bf51394b7022b1e45fffca81..0000000000000000000000000000000000000000 --- a/Src/Windows/ACAMWindow.h +++ /dev/null @@ -1,176 +0,0 @@ -/* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. -* All rights reserved. -* -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this -* software under the terms of either license. -* -* For more information about the AGPLv3.0 license, please visit: -* https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please -* contact Hefei Jiushao Intelligent Technology Co., Ltd. -* =================================================================== -* Author: -*/ -#pragma once - -#include -#include "ui_ACAMWindow.h" -#include - -#include "../Core/CoreDefine.h" - - -namespace acamcad { - class RenderViewer; - class DataManager; - class BackupManager; - class AMCore; - class OperateToolBar; - class UndoRedoHandler; -} - -class ACAMWindow : public QMainWindow -{ - Q_OBJECT - -public: - ACAMWindow(QWidget* parent = Q_NULLPTR); - ~ACAMWindow(); - - //static void ShowMessge(const QString& text, int type); - -private: - void initCoreAndViewer(); - - void createMenus(); - void createToolBars(); - - void createAction(); - - void createAction_File(); - void createAction_Edit(); - void createAction_Help(); - - void createAction_LeftTools(); - //void createAction_Topology(); - - //void refresh_LeftTools(); - -private: // menu entries - // file menu - QAction* openAction_; - QAction* saveAction_; - QAction* exportAction_; - QAction* exitAction_; - // file menu - - // edit menu - QAction* undoAction_; - QAction* redoAction_; - // edit menu - - // help menu - QAction* aboutAction_; - // help menu - - // menu bar - QMenu* fileMenu_; - QMenu* editMenu_; - QMenu* helpMenu_; - // menu bar - -private: - - QAction* duplicateAction_; - -private: - - -private: - - QAction* chamferAction_; - QAction* embedAction_; - QAction* separateAction_; - QAction* weldAction_; - QAction* deleteFaceAction_; - QAction* filletAction_; - QAction* extrudAction_; - QAction* extrudActionMesh_; - - QAction* splitFaceEdgeAction_; - QAction* splitEdgeAction_; - QAction* splitLoopAction_; - QAction* splitFaceAction_; - QAction* splitFaceVertAction_; - QAction* collapseEdgeAction_; - QAction* swapEdgeAction_; - - QAction* bridgeEdgeAction_; - QAction* extrudeEdgeVAction_; - QAction* extrudeEdgeHAction_; - QAction* thickenAction_; - QAction* normalAction_; - QAction* fillHoleAction_; - - QAction* bool_fuseAction_; - QAction* bool_CommonAction_; - QAction* bool_cutAction_; - -private: - QToolBar* editToolBar_; - - -private slots: - void slotChamfer(); - void slotFillet(); - void slotEmbedFace(); - void slotDeleteElement(); - void slotSeparateEdge(); - void slotWeldEdge(); - - - void slotSplitEdge(); - void slotSplitLoop(); - void slotSplitFace(); - void slotSplitFaceByEdge(); - - - void slotBoolFuseObject(); - void slotBoolCutObject(); - void slotBoolCommonObject(); - void slotSplitFaceByVert(); - void slotSwapMeshEdge(); - void slotCombineTwoFace(); - void slotCollapseEdge(); - - void slotBridgeEdge(); - void slotExtrudeEdgeV(); - void slotExtrudeEdgeH(); - void slotThicken(); - void slotMeshReverse(); - void slotFillHole(); - void slotExtrudeFace(); - - // - void slotCurrentChanged(int index); - - -private: - void BaseObjectOperation(const acamcad::SelectModel& s_model, - const acamcad::OperationType& op_type, const acamcad::MeshOperationType mesh_operate_type); - void BaseObjectOperationSelect(const acamcad::SelectModel& s_model, - const acamcad::OperationType& op_type, const acamcad::MeshOperationType mesh_operate_type); - - std::vector list_actions_; - -private: - Ui::ACAMWindow ui; - - acamcad::RenderViewer* render_viewer_; - - acamcad::DataManager* dataManager_; - acamcad::AMCore* core_; - //acamcad::UndoRedoHandler* undoRedoHandler_; -}; diff --git a/Src/Windows/ACAMWindow.ui b/Src/Windows/ACAMWindow.ui deleted file mode 100644 index cf2be451feb3fc31e98dfd0e30ae689f61433507..0000000000000000000000000000000000000000 --- a/Src/Windows/ACAMWindow.ui +++ /dev/null @@ -1,47 +0,0 @@ - - - ACAMWindow - - - - 0 - 0 - 1004 - 687 - - - - ACAMWindow - - - - - - 0 - 0 - 1004 - 26 - - - - - - - edit mesh - - - - - cube - - - - - cylinder - - - - - - - diff --git a/Src/Windows/DialogAuto.cpp b/Src/Windows/DialogAuto.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a207fc4d7e35c715903831f3dd5a549b9fcdc5e3 --- /dev/null +++ b/Src/Windows/DialogAuto.cpp @@ -0,0 +1,429 @@ +#include "DialogAuto.h" +#include +#include +#include +#include +#include +#include "QLineEditUnit.h" +#include +#include +#include "IntValidator.h" + +#define WIDTH_LINEEDIT_3 80 +#define WIDTH_LINEEDIT_2 80 +#define WIDTH_LINEEDIT_1 182 + +namespace acamcad { + + DialogAuto::DialogAuto(QWidget* parent) : + QDialog(parent) + { + } + + void DialogAuto::SetData(QString title, std::vector value) + { + setWindowTitle(title); + setMinimumWidth(400); + QVBoxLayout* layout = new QVBoxLayout(this); + setLayout(layout); + + vec_ = value; + Refresh(layout); + } + + std::vector& DialogAuto::GetData() + { + return vec_; + } + + void DialogAuto::Refresh(QVBoxLayout* layout) + { + for (int i = 0; i < vec_.size(); i++) + { + addDataUI(vec_[i], layout, i); + } + + layout->addSpacing(40); + layout->addStretch(); + + QHBoxLayout* layout2 = new QHBoxLayout; + layout->addLayout(layout2); + //layout->addStretch(); + layout2->addStretch(); + QPushButton* button = new QPushButton(this); + button->setText(tr("确定")); + button->setStyleSheet("QPushButton{" + "color: white;" + "background-color: #464681;" + "border-radius: 4px;" + "}"); + button->setFixedSize(QSize(72, 24)); + connect(button, &QPushButton::clicked, this, &DialogAuto::accept); + layout2->addWidget(button); + + button = new QPushButton(this); + button->setFixedSize(QSize(72, 24)); + button->setText(tr("取消")); + connect(button, &QPushButton::clicked, this, &DialogAuto::reject); + layout2->addWidget(button); + + + } + + void DialogAuto::addDataUI(DialogAuto::Data& data, QVBoxLayout* layout, int index) + { + QHBoxLayout* layout2 = new QHBoxLayout; + + + //layout->addStretch(); + + switch (data.type) + { + case DataType::NONE: + { + + QLabel* label = new QLabel(this); + label->setText("▼" + data.name); + QFont font; + font.setPointSizeF(12); + font.setBold(true); + + label->setFont(font); + + label->setStyleSheet("color: #464681"); + + layout2->addWidget(label); + + layout->addSpacing(10); + layout->addLayout(layout2); + layout->addSpacing(10); + //layout->addSpacing(10); + + break; + } + case DataType::BOOL: + { + QCheckBox* box = new QCheckBox(this); + box->setText(data.name); + layout2->addWidget(box); + box->setChecked(data.value.b); + + ui_.push_back(box); + + layout2->addStretch(); + layout->addLayout(layout2); + } + break; + case DataType::INT: + { + QLabel* label = new QLabel(this); + label->setText(data.name); + layout2->addWidget(label); + + layout2->addStretch(); + + QLineEdit* line = new QLineEdit(this); + + ///QRegExp reg("^[1-9][0-9]*$"); + ///line->setInputMask("9"); + //line->setValidator(new QRegExpValidator(reg, this)); + line->setValidator(new IntValidator(1, INT_MAX, this)); + line->setText(QString::number(data.value.i)); + line->setFixedWidth(WIDTH_LINEEDIT_1); + layout2->addWidget(line); + + //DataUI d = { line }; + ui_.push_back(DataUI({ line })); + layout->addLayout(layout2); + } + break; + case DataType::DOUBLE: + { + QLabel* label = new QLabel(this); + label->setText(data.name); + layout2->addWidget(label); + + layout2->addStretch(); + + QLineEditUnit* line = new QLineEditUnit(this); + line->setValidator(new QDoubleValidator(this)); + line->setFixedWidth(WIDTH_LINEEDIT_1); + line->SetUnitText(data.value.unit); + line->setText(QString::number(data.value.d, 'f', 2)); + layout2->addWidget(line); + + //DataUI d = { line }; + ui_.push_back(DataUI({ line })); + layout->addLayout(layout2); + } + break; + case DataType::COORD2: + { + QLabel* label = new QLabel(this); + label->setText(data.name); + layout2->addWidget(label); + + layout2->addStretch(); + + label = new QLabel(this); + label->setText("X"); + layout2->addWidget(label); + + std::vector vec; + + QLineEdit* line = new QLineEdit(this); + line->setValidator(new QDoubleValidator(this)); + line->setText(QString::number(data.value.c2.X(), 'f', 2)); + line->setFixedWidth(WIDTH_LINEEDIT_2); + layout2->addWidget(line); + vec.push_back(line); + + label = new QLabel(this); + label->setText("Y"); + layout2->addWidget(label); + + line = new QLineEdit(this); + line->setText(QString::number(data.value.c2.Y(), 'f', 2)); + line->setFixedWidth(WIDTH_LINEEDIT_2); + line->setValidator(new QDoubleValidator(this)); + layout2->addWidget(line); + //ui_.push_back(DataUI({ line })); + vec.push_back(line); + + ui_.push_back(vec); + layout->addLayout(layout2); + } + break; + case DataType::COORD3: + { + QLabel* label = new QLabel(this); + label->setText(data.name); + layout2->addWidget(label); + + layout2->addStretch(); + + + std::vector vec; + label = new QLabel(this); + label->setText("X"); + + layout2->addWidget(label); + + QLineEdit* line = new QLineEdit(this); + line->setFixedWidth(WIDTH_LINEEDIT_3); + line->setText(QString::number(data.value.c3.X(), 'f', 2)); + line->setValidator(new QDoubleValidator(this)); + layout2->addWidget(line); + vec.push_back(line); + + label = new QLabel(this); + label->setText("Y"); + layout2->addWidget(label); + + line = new QLineEdit(this); + line->setFixedWidth(WIDTH_LINEEDIT_3); + line->setText(QString::number(data.value.c3.Y(), 'f', 2)); + line->setValidator(new QDoubleValidator(this)); + layout2->addWidget(line); + vec.push_back(line); + + label = new QLabel(this); + label->setText("Z"); + layout2->addWidget(label); + + line = new QLineEdit(this); + line->setFixedWidth(WIDTH_LINEEDIT_3); + line->setText(QString::number(data.value.c3.Z(), 'f', 2)); + line->setValidator(new QDoubleValidator(this)); + layout2->addWidget(line); + vec.push_back(line); + + ui_.push_back(vec); + + layout->addLayout(layout2); + } + break; + case DataType::COORD3VEC: + { + QLabel* label = new QLabel(this); + label->setText("▼" + data.name); + QFont font; + font.setPointSizeF(12); + font.setBold(true); + + label->setFont(font); + + label->setStyleSheet("color: #464681"); + + layout2->addWidget(label); + layout2->addStretch(); + + layout->addSpacing(10); + layout->addLayout(layout2); + layout->addSpacing(10); + + QPushButton* button = new QPushButton(this); + button->setText(tr("+")); + button->setFixedSize(QSize(30, 30)); + connect(button, &QPushButton::clicked, this, [this, layout, index]() { + + saveData(); + vec_[index].value.c3s.push_back({ 0.0,0.0,0.0 }); + + auto c = children(); + + for each (auto iter in c) + { + iter->deleteLater(); + } + delete this->layout(); + + ui_.clear(); + + QVBoxLayout* layout = new QVBoxLayout(this); + setLayout(layout); + + Refresh(layout); + }); + layout2->addWidget(button); + + button = new QPushButton(this); + button->setText("-"); + button->setFixedSize(QSize(30, 30)); + connect(button, &QPushButton::clicked, this, [this, layout, index]() { + + auto& data = vec_[index].value.c3s; + + if (data.empty()) + return; + + saveData(); + data.erase(data.begin() + (data.size() - 1)); + + auto c = children(); + + for each (auto iter in c) + { + iter->deleteLater(); + } + delete this->layout(); + + ui_.clear(); + + QVBoxLayout* layout = new QVBoxLayout(this); + setLayout(layout); + + Refresh(layout); + //ui_.erase() + }); + layout2->addWidget(button); + + int pos = layout2->children().size(); + std::vector vec; + std::vector lay; + for (int i = 0; i < data.value.c3s.size(); i++) + { + QHBoxLayout* layout3 = new QHBoxLayout(this); + layout->addLayout(layout3); + lay.push_back(layout3); + + QLabel* label = new QLabel(this); + label->setText(data.value.unit.arg(i + 1)); + layout3->addWidget(label); + layout3->addStretch(); + + QLineEdit* line = new QLineEdit(this); + line->setFixedWidth(WIDTH_LINEEDIT_3); + line->setText(QString::number(data.value.c3s[i].X(), 'f', 2)); + line->setValidator(new QDoubleValidator(this)); + layout3->addWidget(line); + vec.push_back(line); + + line = new QLineEdit(this); + line->setFixedWidth(WIDTH_LINEEDIT_3); + line->setText(QString::number(data.value.c3s[i].Y(), 'f', 2)); + line->setValidator(new QDoubleValidator(this)); + layout3->addWidget(line); + vec.push_back(line); + + line = new QLineEdit(this); + line->setFixedWidth(WIDTH_LINEEDIT_3); + line->setText(QString::number(data.value.c3s[i].Z(), 'f', 2)); + line->setValidator(new QDoubleValidator(this)); + layout3->addWidget(line); + vec.push_back(line); + } + + ui_.push_back({ vec,lay,pos }); + } + break; + default: + break; + } + + + //layout->addSpacing(10); + } + + void DialogAuto::accept() + { + + saveData(); + + QDialog::accept(); + } + + void DialogAuto::saveData() + { + for (int i = 0, j = 0; i < vec_.size(); i++) + { + //vec_[i]; + auto& data = vec_[i]; + + switch (data.type) + { + case DataType::BOOL: + data.value.b = ui_[j].box->isChecked(); + j++; + break; + case DataType::INT: + data.value.i = ui_[j].line[0]->text().toInt(); + j++; + break; + case DataType::DOUBLE: + data.value.d = ui_[j].line[0]->text().toDouble(); + j++; + break; + case DataType::COORD2: + data.value.c2.SetCoord(ui_[j].line[0]->text().toDouble(), ui_[j].line[1]->text().toDouble()); + j++; + break; + case DataType::COORD3: + data.value.c3.SetCoord( + ui_[j].line[0]->text().toDouble(), + ui_[j].line[1]->text().toDouble(), + ui_[j].line[2]->text().toDouble()); + j++; + break; + case DataType::COORD3VEC: + { + data.value.c3s.clear(); + int size = ui_[j].line.size() / 3; + for (int k = 0; k < size; k++) + { + data.value.c3s.push_back(AMCAX::Coord3( + ui_[j].line[3 * k]->text().toDouble(), + ui_[j].line[3 * k + 1]->text().toDouble(), + ui_[j].line[3 * k + 2]->text().toDouble() + )); + } + j++; + } + break; + default: + break; + } + } + } + +} diff --git a/Src/Windows/DialogAuto.h b/Src/Windows/DialogAuto.h new file mode 100644 index 0000000000000000000000000000000000000000..8ff88a9ae54a10ffb60408caaae2ee1be8ef991c --- /dev/null +++ b/Src/Windows/DialogAuto.h @@ -0,0 +1,146 @@ +/* =================================================================== +* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. +* All rights reserved. +* +* This software is licensed under the GNU Affero General Public License +* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this +* software under the terms of either license. +* +* For more information about the AGPLv3.0 license, please visit: +* https://www.gnu.org/licenses/agpl-3.0.html +* For licensing inquiries or to obtain a commercial license, please +* contact Hefei Jiushao Intelligent Technology Co., Ltd. +* =================================================================== +* Author: +*/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "../Core/CoreDefine.h" +#include + +namespace acamcad { + + class DialogAuto : public QDialog { + Q_OBJECT + + public: + + enum class DataType { + NONE, + BOOL, + INT, + DOUBLE, + COORD2, + COORD3, + COORD3VEC + }; + + struct DataAuto + { + bool b; + size_t i; + double d; + QString unit; + AMCAX::Coord2 c2; + AMCAX::Coord3 c3; + + std::vector c3s; + + ///DataAuto() {} + + ///~DataAuto() {} + }; + + struct Data { + DataType type; + QString name; + DataAuto value; + + + Data(QString n) :type(DataType::NONE), name(n) {} + + Data(QString n, bool b) : type(DataType::BOOL), name(n) + { + value.b = b; + } + + Data(QString n, size_t i) :type(DataType::INT), name(n) + { + value.i = i; + } + + Data(QString n, double d, QString u) : type(DataType::DOUBLE), name(n) + { + value.d = d; + value.unit = u; + } + + Data(QString n, AMCAX::Coord2 co2) :type(DataType::COORD2), name(n) + { + value.c2 = co2; + } + + Data(QString n, AMCAX::Coord3 co3) :type(DataType::COORD3), name(n) + { + value.c3 = co3; + } + + Data(QString n, std::vector co3s, QString u) :type(DataType::COORD3VEC), name(n) + { + value.c3s = co3s; + value.unit = u; + } + + ~Data() {} + }; + + DialogAuto(QWidget* parent = nullptr); + void SetData(QString title, std::vector value); + std::vector& GetData(); + + + public slots: // public slots + void accept() override; + + protected: + + void Refresh(QVBoxLayout* layout); + //QCheckBox + + void addDataUI(Data& data, QVBoxLayout* layout, int index); + + void saveData(); + protected: + std::vector vec_; + + struct DataUI + { + QCheckBox* box; + std::vector line; + std::vector layout; + int pos; + //QLayout* layout; + + DataUI(QCheckBox* b) :box(b) {} + DataUI(std::vector l) :box(nullptr), line(l) {} + //DataUI(DataUI& data) : line(data.line) {} + //DataUI(DataUI&& data) :box(data.box), line(data.line) {} + DataUI(std::vector li, std::vector la, int p) : + line(li), layout(la), pos(p) + {} + }; + + std::vector ui_; + //QLineEdit + }; + +} \ No newline at end of file diff --git a/Src/Windows/IntValidator.cpp b/Src/Windows/IntValidator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d52207dc24c49e40ee4eba4682aeb99a6d066152 --- /dev/null +++ b/Src/Windows/IntValidator.cpp @@ -0,0 +1,22 @@ +#include "IntValidator.h" + +IntValidator::IntValidator(int bottom, int top, QObject* parent) :QIntValidator(bottom, top, parent) +{ +} + +QValidator::State IntValidator::validate(QString& input, int& pos) const +{ + const QValidator::State state = QIntValidator::validate(input, pos); + + if (state == QValidator::Intermediate) + { + int i = input.toInt(); + + if (i <= bottom()) + { + return QValidator::Invalid; + } + } + + return state; +} \ No newline at end of file diff --git a/Src/Windows/IntValidator.h b/Src/Windows/IntValidator.h new file mode 100644 index 0000000000000000000000000000000000000000..dc8da42c1f9c98e6a97f6d19a2da53e7a1f4bd05 --- /dev/null +++ b/Src/Windows/IntValidator.h @@ -0,0 +1,25 @@ +/* =================================================================== +* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. +* All rights reserved. +* +* This software is licensed under the GNU Affero General Public License +* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this +* software under the terms of either license. +* +* For more information about the AGPLv3.0 license, please visit: +* https://www.gnu.org/licenses/agpl-3.0.html +* For licensing inquiries or to obtain a commercial license, please +* contact Hefei Jiushao Intelligent Technology Co., Ltd. +* =================================================================== +* Author: Liu Wei +*/ +#pragma once + +#include + +class IntValidator : public QIntValidator +{ +public: + IntValidator(int bottom, int top, QObject* parent = nullptr); + QValidator::State validate(QString&, int&) const override; +}; diff --git a/Src/Windows/OperateToolbar.cpp b/Src/Windows/OperateToolbar.cpp deleted file mode 100644 index a116eb49a6a3ab1bf912738372ab86f68a795479..0000000000000000000000000000000000000000 --- a/Src/Windows/OperateToolbar.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. -* All rights reserved. -* -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this -* software under the terms of either license. -* -* For more information about the AGPLv3.0 license, please visit: -* https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please -* contact Hefei Jiushao Intelligent Technology Co., Ltd. -* =================================================================== -* Author: -*/ -#include "operatetoolbar.h" - -#include -#include -#include - -#include - -#include "RenderViewer.h" -#include "../Core/ACAMCore.h" - -#include "RenderViewer.h" - -using namespace acamcad; - -// action template: -// {"", QObject::tr(""), "", false, &OperateToolBar::}, -// {"Title", "tooltip", icon, checkable, slot} -const QVector OperateToolBar::tabs{ - { // Base tab - "Base", tr("View"), { - {"WireFrame", QObject::tr("Show in wirefram model"), "", &RenderViewer::slotRenderMode_Wire}, - {"Render", QObject::tr("Show in Shading model"), "", &RenderViewer::slotRenderMode_Rendering}, - {"Smooth", QObject::tr("Show in Smooth model"), "", &RenderViewer::slotRenderMode_Shading}, - {""}, // empty for separator - {"SetCenter", QObject::tr("Set secnce center in select object center"), ":images/images/SetCenter.png", &RenderViewer::slotSetScenesCenter}, - {"SetWorkPlane", QObject::tr("SetWorkPlane by Select element, if no select, set xy plane"), ":images/images/WorkPlane.png", &RenderViewer::slotSetWorkPlane}, - {""}, // empty for separator - {"Vert", QObject::tr("Select Vert model"), ":images/images/SelectVert.png", &RenderViewer::slotSelectMode_Vert, true, SelectModel::VERTEX_MODEL}, - {"Edge", QObject::tr("Select Edge model"), ":images/images/SelectEdge.png", &RenderViewer::slotSelectMode_Edge, true, SelectModel::EDGE_MODEL}, - {"Face", QObject::tr("Select Face model"), ":images/images/SelectFace.png", &RenderViewer::slotSelectMode_Face, true, SelectModel::FACE_MODEL}, - {"Object", QObject::tr("Select Object model"), ":images/images/SelectObject.png", &RenderViewer::slotSelectMode_Object, true, SelectModel::OBJECT_MODEL}, - - {""}, // empty for separator - {"Copy Object", QObject::tr("Copy an Object"), ":images/images/copy.png", &RenderViewer::slotCopyObject}, - {"Delete Object", QObject::tr("Delete an Object"), ":images/images/DeleteObject.png", &RenderViewer::slotDeleteObject}, - {"Combine Object", QObject::tr("Combine more than one Object to a one Object, final get only one object"), ":images/images/Combine.png", &RenderViewer::slotCombineObject}, - {"Mirror", QObject::tr("Select a object to mirror"), ":images/images/Mirror.png", &RenderViewer::slotMirrorObject}, - }}, - - - { // BRep tab - "BRep", tr("BRep"), { - {"Create Single Face", QObject::tr("Create a mesh has a single face, click the left mouse button in turn, enter sure"), ":images/images/SingalMeshFace.png", &RenderViewer::slotCreateSingleFaceBRep}, - {"Create a BRep Plane", QObject::tr("Create a plane brep"), ":images/images/Plane.png", &RenderViewer::slotCreatPlaneBRep}, - {"Create a BRep Polygon", QObject::tr("Create a polygon brep"), ":images/images/Polygon.png", &RenderViewer::slotCreatePolygonBRep}, - {""}, - {"Create a BRep Cube", QObject::tr("Create a cube brep"), ":images/images/Cube.png", &RenderViewer::slotCreatCubeBRep}, - {"Create a BRep Sphere", QObject::tr("Create a sphere brep"), ":images/images/UCSphere.png", &RenderViewer::slotCreateSphereBRep}, - {"Create a BRep Cylinder", QObject::tr("Create a cylinder brep"), ":images/images/Cylinder.png", &RenderViewer::slotCreateCylinderBRep}, - {"Create a BRep Cone", QObject::tr("Create a cone brep"), ":images/images/Cone.png", &RenderViewer::slotCreateConeBrep}, - {"Create a BRep Torus", QObject::tr("Create a torus brep"), ":images/images/Torus.png", &RenderViewer::slotCreateTorusBRep}, - {"Create a BRep Prism", QObject::tr("Create a prism brep"), ":images/images/Prism.png", &RenderViewer::slotCreatePrismBRep}, - }}, - - { // Mesh tab - "Mesh", tr("Mesh"), { - {"Create Single Face", QObject::tr("Create a mesh has a single face, click the left mouse button in turn, enter sure"), ":images/images/SingalMeshFace.png", &RenderViewer::slotCreateSingleFaceMesh}, - {"Create a Mesh Plane", QObject::tr("Create a plane mesh"), ":images/images/Plane.png", &RenderViewer::slotCreatPlaneMesh}, - {"Create a Mesh Cube", QObject::tr("Create a cube mesh"), ":images/images/Cube.png", &RenderViewer::slotCreatCubeMesh}, - {""}, // empty for separator - - {"Subdivide CC", QObject::tr("Subdivide a mesh using Catmull Clark"), ":images/images/SubdiveCC.png", &RenderViewer::slotMeshSubdiveCatmullClark}, - {"Loop", QObject::tr("Subdivide a mesh using Loop"), ":images/images/SubdiveLoop.png", &RenderViewer::slotMeshSubdiveLoop}, - {"Triangulated Mesh", QObject::tr("Triangulation a mesh"), ":images/images/MeshTriangulation.png", &RenderViewer::slotMeshTriangulation}, - }}, - - { // TSpline tab - "TSpline", tr("TSpline"), { - {"Create Single Face", QObject::tr("Create a mesh have a singel face, Click the left mouse button in turn, enter sure"), ":images/images/SingalMeshFace.png", &RenderViewer::slotCreateSingleFaceTSpline}, - {"Create a Plane", QObject::tr("Create a plane mesh"), ":images/images/Plane.png", &RenderViewer::slotCreatePlaneTSpline}, - {"Create a Cube", QObject::tr("Create a cube mesh"), ":images/images/Cube.png", &RenderViewer::slotCreateCubeTSpline}, - {"Create a UVSphere", QObject::tr("Create a UVSphere mesh"), ":images/images/UCSphere.png", &RenderViewer::slotCreateUVSphereTSpline}, - {"Create a QuadSphere", QObject::tr("Create a QuadSphere mesh"), ":images/images/QuadSphere.png", &RenderViewer::slotCreateQuadSphereTSpline}, - {"Create a Cylinder", QObject::tr("Create a Cylinder mesh"), ":images/images/Cylinder.png", &RenderViewer::slotCreateCylinderTSpline}, - {"Create a Cone", QObject::tr("Create a Cone mesh"), ":images/images/Cone.png", &RenderViewer::slotCreateConeTSpline}, - {"Create a Trous", QObject::tr("Create a Trous"), ":images/images/Torus.png", &RenderViewer::slotCreateTorusTSpline}, - //{""}, // empty for separator - {"Create a Circular Arcs", QObject::tr("Create a Circular Arcs"), ":images/images/Circular.png", &RenderViewer::slotCreateCircularTSpline}, - //{"Create a Ring", QObject::tr("Create a Ring by a Curve"), ":images/images/Ring.png", &RenderViewer::slotCreateRingTSpline}, - {""}, // empty for separator - {"Add Crease Edge", QObject::tr("select edges to become crease edge"), ":images/images/Crease.png", &RenderViewer::slotAddCreaseEdge}, - {"Remove Crease Edge", QObject::tr("select edges reomve it's crease status"), ":images/images/RCrease.png", &RenderViewer::slotRemoveCreaseEdge}, - //setFrepFixAction_ "Frep FixF" - //setFrepForceAction_ "Frep ForceF" - //{""}, // empty for separator - {"Make Uniform", QObject::tr("Make TSpline param uniform"), ":images/images/Uniform.png", &RenderViewer::slotTsplineUniform}, - {"Make Repair", QObject::tr("Repair TSpline which can Smooth"), ":images/images/Repair.png", &RenderViewer::slotTsplineRepair}, - }}, - - -}; - -OperateToolBar::OperateToolBar(RenderViewer* viewer, QWidget* parent) : - QTabWidget(parent) -{ - setMovable(true); - setFocusPolicy(Qt::NoFocus); - - std::for_each(tabs.begin(), tabs.end(), [&](const ToolBarTabFillOut& tab) { - auto* toolBar = new QToolBar(tab.title); - toolBars.append(toolBar); - addTab(toolBar, tab.name); - std::for_each(tab.actions.begin(), tab.actions.end(), [&](const ToolBarTabAction& ta) { - auto* action = ta.toQAction(); - if (action) { - if (ta.actionSlot) { - connect(action, &QAction::triggered, viewer, ta.actionSlot); - } - if (ta.checkable) { - viewer->addSelector(ta.selectModel, action); - } - toolBar->addAction(action); - } - else { - toolBar->addSeparator(); - } - }); - }); -} diff --git a/Src/Windows/OperateToolbar.h b/Src/Windows/OperateToolbar.h deleted file mode 100644 index ab08e1628bf7986fa7cc64cb982c3bece31b1634..0000000000000000000000000000000000000000 --- a/Src/Windows/OperateToolbar.h +++ /dev/null @@ -1,113 +0,0 @@ -/* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. -* All rights reserved. -* -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this -* software under the terms of either license. -* -* For more information about the AGPLv3.0 license, please visit: -* https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please -* contact Hefei Jiushao Intelligent Technology Co., Ltd. -* =================================================================== -* Author: -*/ -#ifndef OPERATETOOLBAR_H -#define OPERATETOOLBAR_H - -#include - -// QWidget -#include -#include -#include - -// QObject -#include -#include -#include -#include -#include - -#include "../Core/CoreDefine.h" - -namespace acamcad -{ -class RenderViewer; -class AMCore; -class OperateToolBar; - -class ToolBarTabAction { -private: - QString actionText; - QString actionStatusTip; - QString actionIcon; - -public: - void (RenderViewer::*actionSlot)(); - bool checkable; - SelectModel selectModel; - -public: - ToolBarTabAction(const QString &aText, const QString &aStatusTip = QString(""), - const QString &aIcon = QString(""), void (RenderViewer::*aSlot)() = nullptr, - bool checkable = false, SelectModel selectModel=SelectModel::VERTEX_MODEL): - actionText(aText), actionStatusTip(aStatusTip), - actionIcon(aIcon), actionSlot(aSlot), checkable(checkable), - selectModel(selectModel) {} - -public: - QAction *toQAction() const { - auto *action = actionText.length() ? new QAction(actionText) : nullptr; - if (action) { - if (actionStatusTip.length()) { - action->setStatusTip(actionStatusTip); - } - if (actionIcon.length()) { - action->setIcon(QIcon(actionIcon)); - } - action->setCheckable(checkable); - } - return action; - } -}; // ToolBarTabAction - -class ToolBarTabFillOut -{ -public: - const QString name; - const QString title; - - const QVector actions; - -public: - ToolBarTabFillOut(const QString &name, const QString &title, const QVector &actions): - name(name), title(title), actions(actions) {} -}; - - - -// class Toolbar : public QTabWidget -// { -// Q_OBJECT - - - -// }; - -class OperateToolBar : public QTabWidget -{ - Q_OBJECT -public: - OperateToolBar(RenderViewer *viewer, QWidget *parent=nullptr); - -private: - QVector toolBars; - -private: - const static QVector tabs; - -}; -} -#endif // OPERATETOOLBAR_H diff --git a/Src/Windows/OperateViewer.h b/Src/Windows/OperateViewer.h index 0c838b075a4c0e4038b79d52175b4ced984378da..8e830c324bd8620095fe1d1301ebb09f1ec8064e 100644 --- a/Src/Windows/OperateViewer.h +++ b/Src/Windows/OperateViewer.h @@ -20,8 +20,7 @@ #include "../Object/ObjectDefine.h" #include "AxisOperate.h" #include "../Core/CoreDefine.h" -#include "ParamDialog.h" -#include "../record/Record.h" +//#include "ParamDialog.h" namespace acamcad { diff --git a/Src/Windows/ParamDialog.cpp b/Src/Windows/ParamDialog.cpp deleted file mode 100644 index 7f2840456efba379e993cc4c2ec8027043a96a10..0000000000000000000000000000000000000000 --- a/Src/Windows/ParamDialog.cpp +++ /dev/null @@ -1,683 +0,0 @@ -/* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. -* All rights reserved. -* -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this -* software under the terms of either license. -* -* For more information about the AGPLv3.0 license, please visit: -* https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please -* contact Hefei Jiushao Intelligent Technology Co., Ltd. -* =================================================================== -* Author: -*/ -#include "ParamDialog.h" - -#include - -#include -#include -#include -#include -#include - -#include - -using namespace acamcad; - -void clearLayout(QLayout *layout) -{ - if (layout) - { - QLayoutItem *item; - while ((item = layout->takeAt(0))) - { - if (item->layout()) - { - ::clearLayout(item->layout()); - delete item->layout(); - } - if (item->widget()) - { - delete item->widget(); - } - delete item; - } - } -} - -int ShortLineEdit::counter = 0; - -void ShortLineEdit::setSuffix(QLabel *label) -{ - suffix = label; - suffix->setParent(this); - connect(this, &ShortLineEdit::textChanged, this, &ShortLineEdit::updateSuffixPos); -} - -void ShortLineEdit::updateSuffixPos() -{ - if (suffix) { - auto left = rect().right() - suffix->width(); - left = left < cursorRect().left() + 3 ? 0 : left; - auto top = rect().center().y() - suffix->height() / 2; - suffix->move(left, top); - } -} - -void ParamGBoxTitleButton::mousePressEvent(QMouseEvent *me) -{ - btns = me->buttons(); - QLabel::mousePressEvent(me); -} - -void ParamGBoxTitleButton::mouseReleaseEvent(QMouseEvent *me) -{ - if (btns && !(btns & ~Qt::LeftButton)) // only left button was pressed - { - auto pos = me->localPos(); - if (pos.x() < width() / 2 + 1) - { - emit negative(); - } else - { - emit positive(); - } - } - QLabel::mouseReleaseEvent(me); -} - -void ParamGBoxTitleToggle::mousePressEvent(QMouseEvent *me) -{ - btns = me->buttons(); - QLabel::mousePressEvent(me); -} - -void ParamGBoxTitleToggle::mouseReleaseEvent(QMouseEvent *me) -{ - if (btns && !(btns & ~Qt::LeftButton)) // only left button was pressed - { - emit clicked(); - } - QLabel::mouseReleaseEvent(me); -} - -void ParamGBoxTitle::setCheckable(bool checkable) -{ - button->setHidden(!checkable); - // setFixedWidth(label->width() + (checkable ? button->width() : 0) + 6); -} - -ParamGBoxTitle::ParamGBoxTitle(const QString &title, QWidget *parent): - QWidget(parent), title(title), countable(false) -{ - setStyleSheet("font-size: 0.6875em; background-color: #F0F0F0; " - "padding: 0em; margin: 0em; border: 0em;" - "padding-left: 0.25em; padding-right: 0.25em;"); - - auto *hbox = new QHBoxLayout(this); - setLayout(hbox); - label = new ParamGBoxTitleToggle(title, this); - button = new ParamGBoxTitleButton("( - | + )", this); - button->setStyleSheet("font-family: monospace;"); - - hbox->addWidget(label, 0); - hbox->addStretch(1); - hbox->addWidget(button, 0); - // setMinimumWidth(label->width()); - - setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - - connect(button, &ParamGBoxTitleButton::positive, this, &ParamGBoxTitle::onPositive); - connect(button, &ParamGBoxTitleButton::negative, this, &ParamGBoxTitle::onNegative); - connect(label, &ParamGBoxTitleToggle::clicked, this, &ParamGBoxTitle::onClicked); -} - -void ParamGBoxTitle::setCountable() -{ - countable = true; - label->setText(title + " (1)"); -} - -void ParamGBoxTitle::setCount(const int count) -{ - if (countable) - { - label->setText(title + " (" + QString::number(count) + ")"); - } -} - -ParamGBox::ParamGBox(const QString &title, - const ParamDialog::ParamType &type, - const QVector &row, - const ParamDialog::DataUnit &defaultValue, - const bool checkable, - const bool checked, - QWidget *parent) : - QWidget(parent), templateRow(row), templateDU(defaultValue), - type(type), checkable(checkable), checked(checked) -{ - basel = new QVBoxLayout(this); - setLayout(basel); - - hlinetop = new QFrame(this); - hlinetop->setFrameStyle(QFrame::HLine | QFrame::Sunken); - - twidget = new ParamGBoxTitle(title, this); - twidget->setCheckable(checkable); - basel->addWidget(twidget); - - hlinetop->stackUnder(twidget); - - setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); - addRow(row, defaultValue); - - setChecked(checked); - - connect(twidget, &ParamGBoxTitle::negative, this, &ParamGBox::onNegative); - connect(twidget, &ParamGBoxTitle::positive, this, &ParamGBox::onPositive); - connect(twidget, &ParamGBoxTitle::clicked, this, &ParamGBox::toggle); -} - -void ParamGBox::addRow(const QVector &row, - const ParamDialog::DataUnit &defaultValue) -{ - auto *hbox = new QHBoxLayout; - for (auto i = 0; i < row.size(); ++i) - { - if (row[i].first.size()) - { // only add a name label if it has a name - auto *name = new QLabel(row[i].first); - hbox->addWidget(name, 0); - } - - auto *input = createLineEdit(row[i].second); - auto defText = defaultValue.toString(type, i); - - input->setPlaceholderText(defText); - input->setText(defText); - input->setEnabled(checked); - - hbox->addWidget(input, 0); - } - basel->addLayout(hbox); -} - -void ParamGBox::updateTitlePos() -{ - hlinetop->setFixedWidth(width()); - hlinetop->move(0, twidget->height() / 2 - 1); - // twidget->update(); -} - -void ParamGBox::setCheckable(bool checkable) -{ - this->checkable = checkable; - twidget->setCheckable(checkable); -} - -void ParamGBox::setChecked(bool checked) -{ - for (auto * input: inputs) - { - input->setEnabled(checked); - } -} - -void ParamGBox::setCountable() -{ - twidget->setCountable(); -} - -bool ParamGBox::isCheckable() const -{ - return checkable; -} - -bool ParamGBox::isChecked() const -{ - return checked; -} - -void ParamGBox::resizeEvent(QResizeEvent *re) -{ - updateTitlePos(); - QWidget::resizeEvent(re); -} - -void ParamGBox::showEvent(QShowEvent *se) -{ - updateTitlePos(); - QWidget::showEvent(se); -} - -void ParamGBox::onNegative() -{ - if (inputs.size() > templateRow.size()) - { - auto *lastrow = basel->takeAt(basel->count() - 1)->layout(); - while (lastrow->count()) - { - auto w = lastrow->takeAt(0)->widget(); - lastrow->removeWidget(w); - w->deleteLater(); - } - basel->removeItem(lastrow); - lastrow->deleteLater(); - for (auto i = 0; i < templateRow.size(); ++i) - { - inputs.pop_back(); - } - static_cast(parent())->adjustSize(); - twidget->setCount(inputs.size() / templateRow.size()); - } -} - -void ParamGBox::onPositive() -{ - addRow(templateRow, templateDU); - twidget->setCount(inputs.size() / templateRow.size()); -} - -void ParamGBox::toggle() -{ - setChecked((checked = !checked)); -} - -const QMap> ParamDialog::TypeName { - { ParamDialog::TYPE_MPOINT, QList{tr("MPoint_X:"), tr("MPoint_Y:"), tr("MPoint_Z:")} }, - { ParamDialog::TYPE_MPOINT3, QList{tr("MPoint_X:"), tr("MPoint_Y:"), tr("MPoint_Z:")} }, - { ParamDialog::TYPE_MVEC, QList{tr("MVec_X:"), tr("MVec_Y:"), tr("MVec_Z:")} }, - { ParamDialog::TYPE_MVEC3, QList{tr("MVec_X:"), tr("MVec_Y:"), tr("MVec_Z:")} }, -}; - -void ParamDialog::closeEvent(QCloseEvent *ce) -{ - ::clearLayout(inputLayout); - gboxes.clear(); - tunables.clear(); - QDialog::closeEvent(ce); -} - -ParamDialog::ParamDialog(QWidget *parent): QDialog(parent) { - setWindowTitle(tr("Parameters")); - - setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); - auto *bLayout = new QVBoxLayout; - setLayout(bLayout); - - inputLayout = new QVBoxLayout; - bLayout->addLayout(inputLayout); - - auto *confirm = new QPushButton(tr("&Confirm")); - auto *cancel = new QPushButton(tr("C&ancel")); - - connect(confirm, &QPushButton::clicked, this, &ParamDialog::accept); - connect(cancel, &QPushButton::clicked, this, &ParamDialog::reject); - - auto *butLayout = new QHBoxLayout; - bLayout->addStretch(1); - bLayout->addLayout(butLayout); - - butLayout->addStretch(1); - butLayout->addWidget(confirm, 0); - butLayout->addStretch(0); - butLayout->addWidget(cancel, 0); - butLayout->addStretch(1); - -} - -void ParamDialog::addElement(const RequestUnit ¶m) { - // Stores default value in the response package and update them later; - // Make sure correspounding amount of "input" widgets are created and - // are added to the "inputs" QVector; - response.append(std::make_pair(param.type, param.def)); - - switch (param.type) { - case TYPE_MPOINT: - case TYPE_MPOINT3: { - auto gBox = new ParamGBox(param.title, TYPE_MPOINT3, - QVector { - {TypeName[TYPE_MPOINT3][0], PDEditAttr(INFINITY, INFINITY)}, - {TypeName[TYPE_MPOINT3][1], PDEditAttr(INFINITY, INFINITY)}, - {TypeName[TYPE_MPOINT3][2], PDEditAttr(INFINITY, INFINITY)}, - }, param.def); - inputLayout->addWidget(gBox, 0); - gboxes.push_back(gBox); - } - break; - case TYPE_MPOINT3_2: { - auto gBox = new ParamGBox(param.title, TYPE_MPOINT3_2, - QVector { - {TypeName[TYPE_MPOINT3][0], PDEditAttr(INFINITY, INFINITY)}, - {TypeName[TYPE_MPOINT3][1], PDEditAttr(INFINITY, INFINITY)}, - }, param.def); - inputLayout->addWidget(gBox, 0); - gboxes.push_back(gBox); - } - break; - case TYPE_ARR_MPOINT3: { - auto gBox = new ParamGBox(param.title, param.type, - QVector { - {TypeName[TYPE_MPOINT3][0], PDEditAttr(INFINITY, INFINITY)}, - {TypeName[TYPE_MPOINT3][1], PDEditAttr(INFINITY, INFINITY)}, - {TypeName[TYPE_MPOINT3][2], PDEditAttr(INFINITY, INFINITY)}, - }, param.def, true, true); - gBox->setCountable(); - inputLayout->addWidget(gBox, 0); - gboxes.push_back(gBox); - } - break; - case TYPE_MVEC: - case TYPE_MVEC3: { - auto gBox = new ParamGBox(param.title, TYPE_MVEC3, - QVector { - {TypeName[TYPE_MVEC3][0], PDEditAttr(INFINITY, INFINITY)}, - {TypeName[TYPE_MVEC3][1], PDEditAttr(INFINITY, INFINITY)}, - {TypeName[TYPE_MVEC3][2], PDEditAttr(INFINITY, INFINITY)}, - }, param.def); - inputLayout->addWidget(gBox, 0); - gboxes.push_back(gBox); - } - break; - case TYPE_MVEC3_2: { - auto gBox = new ParamGBox(param.title, param, - QVector { - {TypeName[TYPE_MVEC3][0], PDEditAttr(INFINITY, INFINITY)}, - {TypeName[TYPE_MVEC3][1], PDEditAttr(INFINITY, INFINITY)}, - }, param.def); - inputLayout->addWidget(gBox, 0); - gboxes.push_back(gBox); - } - break; - case TYPE_DOUBLE: { - auto gBox = new ParamGBox(param.title, param, - QVector { - {"", PDEditAttr(param.def.d.bottom, param.def.d.top)}, - }, param.def); - inputLayout->addWidget(gBox, 0); - gboxes.push_back(gBox); - } - break; - case TYPE_DOUBLE_PI: { - auto gBox = new ParamGBox(param.title, param, - QVector { - {"", PDEditAttr(param.def.d.bottom, param.def.d.top, " Pi Rad ")}, - }, param.def); - inputLayout->addWidget(gBox, 0); - gboxes.push_back(gBox); - } - break; - case TYPE_BOOL: { - // Create all the widgets but only add to the layout - // when exec() the dialog - auto *cBox = new QCheckBox(param.title, this); - // inputs.append(cBox); - cBox->setCheckable(true); - cBox->setChecked(param.def.b); - - tunables.append(cBox); - } - break; - case TYPE_SIZE_T: { - auto gBox = new ParamGBox(param.title, TYPE_SIZE_T, - QVector { - {"", PDEditAttr(param.def.s.hasBottom, param.def.s.bottom, - param.def.s.hasTop, param.def.s.top)}, - }, param.def); - inputLayout->addWidget(gBox, 0); - gboxes.push_back(gBox); - } - break; - case TYPE_STRING: { - auto gBox = new ParamGBox(param.title, param, - QVector { - {"", PDEditAttr()} - }, param.def); - inputLayout->addWidget(gBox, 0); - gboxes.push_back(gBox); - } - break; - default: - break; - } -} - -void ParamDialog::accept() -{ - generateResponse(); - emit dataReady(response); - QDialog::accept(); -} - -void ParamDialog::reject() -{ - // Doing nothing - QDialog::reject(); -} - -void ParamDialog::open() -{ - resize(1, 1); - QDialog::open(); -} - -ShortLineEdit * ParamGBox::createLineEdit(QValidator *validator) { - auto *input = new ShortLineEdit; - if (validator) { input->setValidator(validator); } - - inputs.push_back(input); - input->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum)); - - return input; -} - -ShortLineEdit * ParamGBox::createDoubleLineEdit(const double bottom, - const double top) { - auto *v = new QDoubleValidator; - if (!std::isinf(bottom)) { - v->setBottom(bottom); - } - if (!std::isinf(top)) { - v->setTop(top); - } - return createLineEdit(v); -} - -ShortLineEdit * ParamGBox::createDoubleLineEdit(const double bottom, - const double top, - const QString &suffix) { - auto *edit = createDoubleLineEdit(bottom, top); - QLabel* label = new QLabel(suffix); - label->setStyleSheet("font-size:1em;color:red;"); - label->setAutoFillBackground(true); - edit->setSuffix(label); - - return edit; -} - -ShortLineEdit * ParamGBox::createIntLineEdit(const bool hasBottom, const int bottom, - const bool hasTop, const int top) { - auto *v = new QIntValidator; - if (hasBottom) { - v->setBottom(bottom); - } - if (hasTop) { - v->setTop(top); - } - return createLineEdit(v); -} - -ShortLineEdit * ParamGBox::createLineEdit(const PDEditAttr &unit) -{ - switch (unit.type) - { - case PDEditAttr::TYPE_SIZE_T: - { - return createIntLineEdit(unit.hasBottom, unit.bottom.s, - unit.hasTop, unit.top.s); - } break; - case PDEditAttr::TYPE_DOUBLE: - { - if (unit.extra.size()) - { - return createDoubleLineEdit(unit.bottom.d, - unit.top.d, - unit.extra); - } else - { - return createDoubleLineEdit(unit.bottom.d, - unit.top.d); - } - } break; - case PDEditAttr::TYPE_STRING: - { - return createLineEdit(nullptr); - } - default:; - } - return nullptr; -} - - -void ParamDialog::addElements(const QVector ¶mList) { - ::clearLayout(inputLayout); // just in case - tunables.clear(); // just in case - response.clear(); // just in case - gboxes.clear(); // just in case - - std::for_each(paramList.begin(), paramList.end(), [&](const RequestUnit ¶m) { - addElement(param); - }); - - if (tunables.size()) { - // create groupBox for all the tunables - auto *tunableBox = new QGroupBox(tr("Tunables")); - auto *tunableLayout = new QVBoxLayout(); - tunableBox->setLayout(tunableLayout); - inputLayout->addWidget(tunableBox); - //do { - // auto *cBox = tunables.takeFirst(); - // tunableLayout->addWidget(cBox); - //} while (tunables.size()); - for (auto* cBox : tunables) { - tunableLayout->addWidget(cBox); - } - } -} - -void ParamDialog::generateResponse() { - const auto getOne = [](const QLineEdit *input) -> QString { - int _ = 0; // Required argument for validators but is not actually used - if (input) - { - auto text = input->text(); - auto *vor = input->validator(); - auto valid = vor ? - vor->validate(text, _) == QValidator::Acceptable : - true; - if (valid) { return text; } - /*else { return input->placeholderText(); } // assume the placeholder is also the default value */ - } - return QString(""); - }; - const auto getOneDouble = [&getOne](const QLineEdit *input) -> double { - return getOne(input).toDouble(); - }; - const auto getOneSizeT = [&getOne](const QLineEdit *input) -> size_t { - return getOne(input).toULongLong(); - }; - const auto getThreeDouble = [&getOneDouble](const std::vector &inputs3) { - double v[3]{.0}; - for (auto i = 0; i < 3 && i < inputs3.size(); ++i) - { - v[i] = getOneDouble(inputs3[i]); - } - return std::make_tuple(v[0], v[1], v[2]); - }; - std::for_each(response.begin(), response.end(), [&, this](auto &rspu) { - switch (rspu.first) - { - case TYPE_MPOINT: [[fallthrough]]; - case TYPE_MPOINT3: { - double x, y, z; - std::tie(x, y, z) = getThreeDouble(gboxes.takeFirst()->getInputs()); - auto &rspdu = rspu.second.point; - if (!std::isnan(x)) rspdu[0] = x; - if (!std::isnan(y)) rspdu[1] = y; - if (!std::isnan(z)) rspdu[2] = z; - } - break; - case TYPE_MPOINT3_2: { - double x, y, _; - std::tie(x, y, _) = getThreeDouble(gboxes.takeFirst()->getInputs()); - auto &rspdu = rspu.second.point; - if (!std::isnan(x)) rspdu[0] = x; - if (!std::isnan(y)) rspdu[1] = y; - } - break; - case TYPE_ARR_MPOINT3: { - auto inputs = gboxes.takeFirst()->getInputs(); - std::vector results; - for (int i = 0; i < inputs.size(); i += 3) - { - double x, y, z; - std::tie(x, y, z) = getThreeDouble({inputs[i], inputs[i + 1], inputs[i + 2]}); - if (!std::isnan(x) && !std::isnan(y)) - { - results.push_back({x, y, z}); - } - } - rspu.second.pointarr = results; - } - break; - case TYPE_MVEC: [[fallthrough]]; - case TYPE_MVEC3: { - double x, y, z; - std::tie(x, y, z) = getThreeDouble(gboxes.takeFirst()->getInputs()); - auto &rspdu = rspu.second.vec; - if (!std::isnan(x)) rspdu[0] = x; - if (!std::isnan(y)) rspdu[1] = y; - if (!std::isnan(z)) rspdu[2] = z; - } - break; - case TYPE_MVEC3_2: { - double x, y, _; - std::tie(x, y, _) = getThreeDouble(gboxes.takeFirst()->getInputs()); - auto &rspdu = rspu.second.vec; - if (!std::isnan(x)) rspdu[0] = x; - if (!std::isnan(y)) rspdu[1] = y; - } - break; - case TYPE_BOOL: { - auto *input = tunables.takeFirst(); - auto value = input->isChecked(); - auto &rspdu = rspu.second.b; - rspdu = value; - } - break; - case TYPE_DOUBLE: [[fallthrough]]; - case TYPE_DOUBLE_PI: { - auto value = getOneDouble(gboxes.takeFirst()->getInputs()[0]); - auto &rspdu = rspu.second.d; - if (!std::isnan(value)) rspdu = value; - } - break; - case TYPE_SIZE_T: { - auto value = getOneSizeT(gboxes.takeFirst()->getInputs()[0]); - auto &rspdu = rspu.second.s; - rspdu = value; - } - break; - case TYPE_STRING: { - auto text = getOne(gboxes.takeFirst()->getInputs()[0]); - rspu.second.str = text; - } - break; - default: - break; - } - }); -} diff --git a/Src/Windows/ParamDialog.h b/Src/Windows/ParamDialog.h deleted file mode 100644 index 437372b5492a9299971291803cf8e6dd368fea27..0000000000000000000000000000000000000000 --- a/Src/Windows/ParamDialog.h +++ /dev/null @@ -1,469 +0,0 @@ -/* =================================================================== -* Copyright (C) 2023 Hefei Jiushao Intelligent Technology Co., Ltd. -* All rights reserved. -* -* This software is licensed under the GNU Affero General Public License -* v3.0 (AGPLv3.0) or a commercial license. You may choose to use this -* software under the terms of either license. -* -* For more information about the AGPLv3.0 license, please visit: -* https://www.gnu.org/licenses/agpl-3.0.html -* For licensing inquiries or to obtain a commercial license, please -* contact Hefei Jiushao Intelligent Technology Co., Ltd. -* =================================================================== -* Author: -*/ -#ifndef PARAMETERWIDGET_H -#define PARAMETERWIDGET_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "../Core/CoreDefine.h" - -#include -#include - -namespace acamcad { - - void clearLayout(QLayout* layout); - - // Overriding the default size hint so that it won't be as wide at default - // not declaring as nested class because it's an Q_Object - class ShortLineEdit : public QLineEdit - { - Q_OBJECT - friend class ParamGBox; - private: - static int counter; // Debuging purpose making sure there's no leakage - QSize sizeHint() const override { return QSize(72, QLineEdit::sizeHint().height()); } - ShortLineEdit() : QLineEdit(), suffix(nullptr) { ++counter; } - ~ShortLineEdit() { --counter; }; - void setSuffix(QLabel* l); - protected: - void resizeEvent(QResizeEvent* re) override { updateSuffixPos(); QLineEdit::resizeEvent(re); } - void showEvent(QShowEvent* se) override { updateSuffixPos(); QLineEdit::showEvent(se); } - private: - QLabel* suffix; - private slots: - void updateSuffixPos(); - - }; - - struct PDEditAttr { - enum LType { - TYPE_NONE, - TYPE_SIZE_T, - TYPE_DOUBLE, - TYPE_STRING, - } type; - bool hasBottom; - bool hasTop; - union { - size_t s; - double d; - operator size_t() const { return s; } - operator double() const { return d; } - operator size_t& () { return s; } - operator double& () { return d; } - } bottom; - union { - size_t s; - double d; - operator size_t() const { return s; } - operator double() const { return d; } - operator size_t& () { return s; } - operator double& () { return d; } - } top; - QString extra; - - PDEditAttr() : type(TYPE_STRING), hasBottom(false), hasTop(false) { } - PDEditAttr(const double b, const double t) : - type(TYPE_DOUBLE), hasBottom(std::isinf(b)), hasTop(std::isinf(t)) - { - bottom.d = b; - top.d = t; - } - PDEditAttr(const double b, const double t, const QString e) : - type(TYPE_DOUBLE), hasBottom(std::isinf(b)), hasTop(std::isinf(t)), extra(e) - { - bottom.d = b; - top.d = t; - } - PDEditAttr(const bool isbottom, const size_t l) : - type(TYPE_SIZE_T), hasBottom(isbottom), hasTop(!isbottom) - { - bottom.s = l; - top.s = l; - } - PDEditAttr(const bool hasBottom, const size_t bottom, - const bool hasTop, const size_t top) : - type(TYPE_SIZE_T), hasBottom(hasBottom), hasTop(hasTop) - { - this->bottom.s = bottom; - this->top.s = top; - } - }; - - // typedef std::pair PDRowUnit; - typedef std::pair PDRowUnit; - - - class ParamGBox; - - class ParamDialog : public QDialog - { - Q_OBJECT - public: // type define - enum ParamType - { - /* TYPE_MPOINT2, */ TYPE_MPOINT, TYPE_MPOINT3, - TYPE_MPOINT3_2, // /* TYPE_POINT3_2 is MPoint3 but only shows 2 Params in the dialog */ - /* TYPE_MVEC2, */ TYPE_MVEC, TYPE_MVEC3, - TYPE_MVEC3_2, // /* TYPE_MVEC3_2 is MVec3 but only shows 2 Params in the dialog */ - /* TYPE_FLOAT, */ TYPE_DOUBLE, TYPE_DOUBLE_PI, TYPE_BOOL, - /* TYPE_INT8, TYPE_INT16, TYPE_INT32, TYPE_INT64, TYPE_INT128, */ - /* TYPE_UINT8, TYPE_UINT16, TYPE_UINT32, TYPE_UINT64, TYPE_UINT128, */ - TYPE_SIZE_T, TYPE_STRING, - // TYPE_ARR_MPOINT3, - TYPE_ARR_MPOINT3, - }; // ParamType - - /* - capsule data so that different types of values can be stored in - a single container. - */ - struct DataUnit - { - struct Size_t - { // defining Size_t so that DataUnit can handle top and bottom too - size_t value; - size_t bottom, top; - bool hasBottom, hasTop; - - Size_t() : value(0), hasBottom(false), hasTop(false) {} - Size_t(const size_t& s, - bool hasBottom, size_t bottom, - bool hasTop = false, bool top = 0) : - value(s), bottom(bottom), top(top), - hasBottom(hasBottom), hasTop(hasTop) { } - - operator size_t() const { return value; } - size_t& operator=(const size_t& a) { return value = a; } - } s; // Size_t - struct Double - { // define Double so that DataUnit can handle top and bottom too - double value; - double bottom, top; - - Double() : value(.0), bottom(-INFINITY), top(INFINITY) {}; - Double(const double& d, - double bottom, double top) : - value(d), bottom(bottom), top(top) { } - - operator double() const { return value; } - double& operator=(const double& a) { return value = a; } - } d; // Double - bool b; - AMCAX::Coord3 point; - AMCAX::Coord3 vec; - QString str; - std::vector pointarr; - // float f; - /* int8_t i8; int16_t i16; int32_t i32; int64_t i64; */ // int - /* uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; */ // uint - - // no need to apply The Rule of Five - DataUnit(const size_t& s, - bool hasBottom = true, size_t bottom = 1, - bool hasTop = false, size_t top = 0) : - s(s, hasBottom, bottom, hasTop, top) { } - DataUnit(const double& d, const double& bottom = INFINITY, const double& top = INFINITY) : - d(d, bottom, top) { } - DataUnit(const bool& b) : b(b) { } - DataUnit(const AMCAX::Point3& p) - { - point.SetX(p.X()); - point.SetY(p.Y()); - point.SetZ(p.Z()); - } - DataUnit(const AMCAX::Coord3& value, ParamType type) { - if (type == ParamType::TYPE_MPOINT3) { - point = value; - } - else if (type == ParamType::TYPE_MVEC3) { - vec = value; - } - } - DataUnit(const QString& str) : str(str) { } - DataUnit(const std::vector& pointarr) = delete; - - DataUnit() { }; - DataUnit(const DataUnit& rd) : - s(rd.s, rd.s.hasBottom, rd.s.bottom, rd.s.hasTop, rd.s.top), - d(rd.d, rd.d.bottom, rd.d.top), - point(rd.point), - vec(rd.vec), - str(rd.str), - pointarr(rd.pointarr) { } - DataUnit(DataUnit&&) = delete; - ~DataUnit() { }; // required by std::pair - - DataUnit& operator=(DataUnit&&) = delete; - DataUnit& operator=(const DataUnit&) = delete; - - void updatePoint(const AMCAX::Coord3d& otherp) { point = otherp; } - - QString toString(const ParamType t, const int i) const { - switch (t) { - case TYPE_MPOINT: - case TYPE_MPOINT3: - case TYPE_MPOINT3_2: - case TYPE_ARR_MPOINT3: { - return QString::number(point[i], 'f', 2); - } - break; - case TYPE_MVEC: - case TYPE_MVEC3: - case TYPE_MVEC3_2: { - return QString::number(vec[i], 'f', 2); - } - break; - case TYPE_DOUBLE: - case TYPE_DOUBLE_PI: { - return QString::number(d, 'g', 12); - } - break; - case TYPE_SIZE_T: { - return QString::number(s); - } - break; - case TYPE_STRING: { - return str; - } - break; - default: - break; - } - return QString(""); - } - }; // DataUnit - - // ParameterType, title, default value - struct RequestUnit - { - ParamType type; - QString title; - DataUnit def; - - RequestUnit() { } - RequestUnit(const ParamType& type, const QString& title, const DataUnit& def) : - type(type), title(title), def(def) { } - RequestUnit(const std::tuple& oth) : - type(std::get<0>(oth)), title(std::get<1>(oth)), def(std::get<2>(oth)) {} - RequestUnit(const RequestUnit& oth) : - type(oth.type), title(oth.title), def(oth.def) {} - - operator ParamType() const { return type; } - operator QString() const { return title; } - operator DataUnit() const { return def; } - }; - - // redundant ParameterType, payload, isValid - typedef QList> Response; - - private: // type define - union InputWidget - { - QLineEdit* edit; - QCheckBox* check; - - InputWidget() = delete; - InputWidget(QLineEdit* edit) { this->edit = edit; } - InputWidget(QCheckBox* check) { this->check = check; } - ~InputWidget() { edit = nullptr; check = nullptr; } - }; // InputWidget - - public: // constructors - ParamDialog(QWidget* parent = nullptr); - - public: // public methods - void addElements(const QVector& param); - - public slots: // public slots - void accept() override; - void reject() override; - - private slots: // private slots - - private: // private methods - void addElement(const RequestUnit& param); - void generateResponse(); - QSize sizeHint() const override { return QSize(72, QDialog::sizeHint().height()); } - - public: // public members - void open() override; - - private: // private members - QVBoxLayout* inputLayout; - Response response; - // QList inputs; - QList tunables; - QList gboxes; - - public: // public static methods - - private: // private static methods - // static - - private: // private constant members - const static QMap> TypeName; - - signals: // - void dataReady(const ParamDialog::Response& data); - - protected: - void closeEvent(QCloseEvent* ce) override; - - }; - - // not declaring as nested class because it's an Q_Object - class ParamGBoxTitleButton : public QLabel - { - Q_OBJECT - friend class ParamGBoxTitle; - private: - ParamGBoxTitleButton(const QString& text, QWidget* parent = nullptr) : - QLabel(text, parent) {} - ~ParamGBoxTitleButton() {} - Qt::MouseButtons btns; - protected: - void mousePressEvent(QMouseEvent* me) override; - void mouseReleaseEvent(QMouseEvent* me) override; - signals: - void negative(); - void positive(); - }; // ParamGBoxTitleButton - - // not declaring as nested class because it's an Q_Object - class ParamGBoxTitleToggle : public QLabel - { - Q_OBJECT - friend class ParamGBoxTitle; - private: - ParamGBoxTitleToggle(const QString& text, QWidget* parent = nullptr) : - QLabel(text, parent) {} - ~ParamGBoxTitleToggle() {} - Qt::MouseButtons btns; - protected: - void mousePressEvent(QMouseEvent* me) override; - void mouseReleaseEvent(QMouseEvent* me) override; - signals: - void clicked(); - }; // ParamGBoxTitleToggle - - // not declaring as nested class because it's an Q_Object - class ParamGBoxTitle : public QWidget - { - Q_OBJECT - friend class ParamGBox; - private: - ParamGBoxTitle(const QString& title, QWidget* parent = nullptr); - ~ParamGBoxTitle() { }; - void setCheckable(bool cable); - void setCountable(); - void setCount(const int count); - const QString title; - bool countable; - ParamGBoxTitleToggle* label; - ParamGBoxTitleButton* button; - - signals: - void negative(); - void positive(); - void clicked(); - - private slots: - void onNegative() { emit negative(); } - void onPositive() { emit positive(); } - void onClicked() { emit clicked(); } - }; // ParamGBoxTitle - - // not declaring as nested class because it's an Q_Object - class ParamGBox : public QWidget - { - Q_OBJECT - friend class ParamDialog; - private: - ParamGBox(const QString& title, - const ParamDialog::ParamType& type, - const QVector& row, - const ParamDialog::DataUnit& defaultValue, - const bool checkable = false, - const bool checked = true, - QWidget* parent = nullptr); - ~ParamGBox() {}; - private: - void updateTitlePos(); - void setCheckable(bool checkable); - void setChecked(bool checked); - void setCountable(); - bool isCheckable() const; - bool isChecked() const; - - void addRow(const QVector& row, const ParamDialog::DataUnit& defaultValue); - - ShortLineEdit* createLineEdit(QValidator* validator); - // INFINITY if has no bottom/top limit - ShortLineEdit* createDoubleLineEdit(const double bottom, const double top); - ShortLineEdit* createDoubleLineEdit(const double bottom, const double top, const QString& suffix); - ShortLineEdit* createIntLineEdit(const bool hasBottom, const int bottom, - const bool hasTop = false, const int top = 0); - ShortLineEdit* createLineEdit(const PDEditAttr& attr); - - protected: - void resizeEvent(QResizeEvent* re) override; - void showEvent(QShowEvent* se) override; - private: - std::vector inputs; - - ParamDialog::ParamType type; - QVector templateRow; - ParamDialog::DataUnit templateDU; - - QVBoxLayout* basel; - ParamGBoxTitle* twidget; - QFrame* hlinetop; - - bool checkable; - bool checked; - - private: - std::vector getInputs() { return inputs; } - - private slots: - void onNegative(); - void onPositive(); - void toggle(); - }; - -} - -#endif // PARAMETERWIDGET_H diff --git a/Src/Windows/QLineEditUnit.cpp b/Src/Windows/QLineEditUnit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d7ccc392bc29a25159a05b53891cf4201b29760 --- /dev/null +++ b/Src/Windows/QLineEditUnit.cpp @@ -0,0 +1,49 @@ +#include "QLineEditUnit.h" +#include +#include +QLineEditUnit::QLineEditUnit(QWidget* parent) : QLineEdit(parent) +{ + m_unitButton = new QPushButton(this); + //λťƽޱ߿ + m_unitButton->setStyleSheet("border-style:solid;color: black;"); + m_unitButton->setFlat(true); + //λ벼 + QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight); + layout->addStretch(); + layout->setSpacing(0); + //λťMargins + //ұ13pxλ(ť) + layout->setContentsMargins(0, 0, 13, 0); + layout->addWidget(m_unitButton); + this->setLayout(layout); +} + +QLineEditUnit::~QLineEditUnit() +{ +} + +QString QLineEditUnit::UnitText() const +{ + return this->m_unitButton->text(); +} + +void QLineEditUnit::SetUnitText(const QString& unitText) +{ + if (unitText.isEmpty() || this->m_unitButton->text() == unitText) + { + return; + } + + + m_unitButton->setFont(this->font()); + this->m_unitButton->setText(unitText); + // this->m_unitButton->setVisible(true); + //ֵĿ + int width = this->fontMetrics().width(m_unitButton->text()); + m_unitButton->setFixedWidth(width); + //λͷ˾ʾֹһڰť + QBoxLayout* layout = (QBoxLayout*)this->layout(); + QMargins margins = layout->contentsMargins(); + setTextMargins(margins.left(), margins.top(), width + margins.right(), margins.bottom()); +} + diff --git a/Src/Windows/QLineEditUnit.h b/Src/Windows/QLineEditUnit.h new file mode 100644 index 0000000000000000000000000000000000000000..5725c6524075d171b86d5a4821b1e5bbd48c93cd --- /dev/null +++ b/Src/Windows/QLineEditUnit.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include + +class QLineEditUnit : public QLineEdit +{ + Q_OBJECT +public: + QLineEditUnit(QWidget* parent = Q_NULLPTR); + ~QLineEditUnit(); + //ȡ༭λ + QString UnitText()const; + + //ñ༭λ + void SetUnitText(const QString& unitText); + +private: + QPushButton* m_unitButton = nullptr;//λıť +}; \ No newline at end of file diff --git a/Src/Windows/RenderViewer.cpp b/Src/Windows/RenderViewer.cpp index 08799d2738813706ece9e779e020294a25ff01d2..5c6734c2a0a902d1424d029f46d31fcb2f5c352a 100644 --- a/Src/Windows/RenderViewer.cpp +++ b/Src/Windows/RenderViewer.cpp @@ -24,13 +24,14 @@ #include "../Core/DataManager.h" #include "SelectTool.h" #include "../Utils/MathUtils.h" +#include "DialogAuto.h" using namespace acamcad; using namespace qglviewer; RenderViewer::RenderViewer(QWidget* parent, Qt::WindowFlags flags) : QGLViewer(parent, flags), selectBuffer_(nullptr), draw_model_(DrawModel::RENDERING_MODEL), - dataManager_(nullptr), coreCommand_(nullptr) + dataManager_(nullptr), coreCommand_(nullptr), listen_(nullptr) { work_plane_ = new PlaneObject(); is_draw_grid_ = true; @@ -38,6 +39,7 @@ RenderViewer::RenderViewer(QWidget* parent, Qt::WindowFlags flags) : is_pick_move_select_ = false; o_axis_ = nullptr; + selectTool_ = new SelectTool(); setSelectBufferSize(2000); setSelectRegionWidthDefault(); @@ -46,11 +48,9 @@ RenderViewer::RenderViewer(QWidget* parent, Qt::WindowFlags flags) : setSelectModel(SelectModel::OBJECT_MODEL); setOperateType(OperationType::NO_OPERATION); - selectTool_ = new SelectTool(); - dialog = new ParamDialog(parent); + // TODO: connect - connect(dialog, &ParamDialog::finished, this, &RenderViewer::disconnectDialog); // Possibly a bug in Qt5 MSVC, which prevents the "new" style connect() from working. //connect(this, &RenderViewer::selectedModelChanged, this, &RenderViewer::selectCheckChanged); connect(this, SIGNAL(selectedModelChanged()), this, SLOT(selectCheckChanged())); @@ -58,9 +58,9 @@ RenderViewer::RenderViewer(QWidget* parent, Qt::WindowFlags flags) : glu_cylinder_ = gluNewQuadric(); - initBRep(); - initMesh(); - initTSpline(); + //initBRep(); + //initMesh(); + //initTSpline(); } RenderViewer::~RenderViewer() @@ -621,6 +621,11 @@ std::vector getSelectObjectList(const std::vector& s_ return so_id_list; } +void RenderViewer::setListen(RenderViewerListen* listen) +{ + listen_ = listen; +} + void RenderViewer::mouseReleaseEvent(QMouseEvent* e) { if (is_frame_selection_) @@ -798,15 +803,6 @@ void RenderViewer::keyPressEvent(QKeyEvent* e) { beginEnterClickOperation(); } - // control , with undo / redo - else if (modifiers == Qt::ControlModifier && key == Qt::Key_Z) - { - slotUndo(); - } - else if (modifiers == Qt::ControlModifier && key == Qt::Key_Y) - { - slotRedo(); - } //开关工作平面显示 else if (modifiers == Qt::NoModifier && key == Qt::Key_G) { changeGridIsDrawn(); @@ -909,8 +905,8 @@ void RenderViewer::keyPressEvent(QKeyEvent* e) //保存操作 else if (modifiers == Qt::ControlModifier && key == Qt::Key_S) { - QString f = "AMCAD_work.obj"; - coreCommand_->saveObjects(f); + //QString f = "AMCAD_work.obj"; + coreCommand_->saveObjects(); } else { @@ -1340,7 +1336,8 @@ AMCAX::Coord3 RenderViewer::mouseMoveTrans(const QPoint& e_point, const AMCAX::C void RenderViewer::clearSelected() { - dataManager_->clearSelected(); + if (dataManager_) + dataManager_->clearSelected(); select_info_list_.clear(); selectTool_->clearSelected(); } @@ -1389,131 +1386,172 @@ void RenderViewer::slotCreateSingleFaceMesh() } void RenderViewer::slotCreatPlaneMesh() { - dialog->addElements(objectFillOuts[OPERATE_MESH_PLANE]); - dialog->setWindowTitle(tr("Create a plane mesh")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatPlaneMeshAccepted); - dialog->open(); -} -void RenderViewer::slotCreatPlaneMeshAccepted(const ParamDialog::Response& data) { + //dialog->addElements(objectFillOuts[OPERATE_MESH_PLANE]); + //dialog->setWindowTitle(tr("Create a plane mesh")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatPlaneMeshAccepted); + //dialog->open(); + DialogAuto dialog(this); - MPoint3 p0 = MPoint3(data[0].second.point.X(), data[0].second.point.Y(), data[0].second.point.Z()); - MPoint3 p1 = MPoint3(data[1].second.point.X(), data[1].second.point.Y(), data[1].second.point.Z()); + dialog.SetData(tr("平面"), + { + {tr("位置与形状")}, + {tr("第一点"),AMCAX::Coord2(0.0,0.0)}, + {tr("对角点"),AMCAX::Coord2(1.0,1.0)}, + {tr("X向分段"),(size_t)3}, + {tr("Y向分段"),(size_t)3}, + }); - size_t u_num = data[2].second.s; - size_t v_num = data[3].second.s; - coreCommand_->createPlaneMeshObject(p0, p1, u_num, v_num); - update(); + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createPlaneMeshObject(data[1].value.c2, data[2].value.c2, data[3].value.i, data[4].value.i); + } - objectFillOuts[OPERATE_MESH_PLANE][0].def.point = data[0].second.point; - objectFillOuts[OPERATE_MESH_PLANE][1].def.point = data[1].second.point; - objectFillOuts[OPERATE_MESH_PLANE][2].def.s = u_num; - objectFillOuts[OPERATE_MESH_PLANE][3].def.s = v_num; } +//void RenderViewer::slotCreatPlaneMeshAccepted(const ParamDialog::Response& data) { +// +// MPoint3 p0 = MPoint3(data[0].second.point.X(), data[0].second.point.Y(), data[0].second.point.Z()); +// MPoint3 p1 = MPoint3(data[1].second.point.X(), data[1].second.point.Y(), data[1].second.point.Z()); +// +// size_t u_num = data[2].second.s; +// size_t v_num = data[3].second.s; +// coreCommand_->createPlaneMeshObject(p0, p1, u_num, v_num); +// update(); +// +// objectFillOuts[OPERATE_MESH_PLANE][0].def.point = data[0].second.point; +// objectFillOuts[OPERATE_MESH_PLANE][1].def.point = data[1].second.point; +// objectFillOuts[OPERATE_MESH_PLANE][2].def.s = u_num; +// objectFillOuts[OPERATE_MESH_PLANE][3].def.s = v_num; +//} void RenderViewer::slotCreatCubeMesh() { - dialog->addElements(objectFillOuts[OPERATE_MESH_CUBE]); - dialog->setWindowTitle(tr("Create a cube mesh")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatCubeMeshAccepted); - dialog->open(); -} + //dialog->addElements(objectFillOuts[OPERATE_MESH_CUBE]); + //dialog->setWindowTitle(tr("Create a cube mesh")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatCubeMeshAccepted); + //dialog->open(); -void RenderViewer::slotCreatCubeMeshAccepted(const ParamDialog::Response& data) { + DialogAuto dialog(this); - AMCAX::Coord3 c_co = data[0].second.point; - AMCAX::Coord3 s_co = data[1].second.point; + dialog.SetData(tr("立方体"), + { + {tr("位置与形状")}, + {tr("第一点"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("对角点"),AMCAX::Coord3(1.0,1.0,1.0)}, + {tr("X向分段"),(size_t)2}, + {tr("Y向分段"),(size_t)2}, + {tr("Z向分段"),(size_t)2}, + }); - MPoint3 c(c_co.X(), c_co.Y(), c_co.Z()); - MPoint3 s(s_co.X(), s_co.Y(), s_co.Z()); - size_t x_num = data[2].second.s; - size_t y_num = data[3].second.s; - size_t z_num = data[4].second.s; - coreCommand_->createCubeMeshObject(c, s, x_num, y_num, z_num); - update(); - objectFillOuts[OPERATE_MESH_CUBE][0].def.point = c_co; - objectFillOuts[OPERATE_MESH_CUBE][1].def.point = s_co; - objectFillOuts[OPERATE_MESH_CUBE][2].def.s = x_num; - objectFillOuts[OPERATE_MESH_CUBE][3].def.s = y_num; - objectFillOuts[OPERATE_MESH_CUBE][4].def.s = z_num; -} + if (dialog.exec()) + { + auto& data = dialog.GetData(); -void RenderViewer::initTSpline() -{ - objectFillOuts[OPERATE_TSPLINE_PLANE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("p0p"), ParamDialog::DataUnit{MPoint3(0.0, 0.0, 0.0)}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("p1p"), ParamDialog::DataUnit{MPoint3(2.0, 2.0, 0.0)}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("u_num"), ParamDialog::DataUnit{3Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("v_num"), ParamDialog::DataUnit{3Ui64}), - }; - objectFillOuts[OPERATE_TSPLINE_CUBE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("first"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("second"), ParamDialog::DataUnit{MPoint3{1.0, 1.0, 1.0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("x_num"), ParamDialog::DataUnit{1Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("y_num"), ParamDialog::DataUnit{1Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("z_num"), ParamDialog::DataUnit{1Ui64}), - }; - objectFillOuts[OPERATE_TSPLINE_UVSPHERE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("rf_num"), ParamDialog::DataUnit{8Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("vf_num"), ParamDialog::DataUnit{8Ui64}), - }; - objectFillOuts[OPERATE_TSPLINE_QUADSPHERE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("subtime"), ParamDialog::DataUnit{2Ui64}), - }; - objectFillOuts[OPERATE_TSPLINE_CYLINDER] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("b_center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("axis"), ParamDialog::DataUnit{MPoint3{.0, .0, 1.0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("height"), ParamDialog::DataUnit{2.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("rf_num"), ParamDialog::DataUnit{8Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("vf_num"), ParamDialog::DataUnit{4Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_BOOL, tr("top"), ParamDialog::DataUnit{true}), - ParamDialog::RequestUnit(ParamDialog::TYPE_BOOL, tr("bottom"), ParamDialog::DataUnit{true}), - }; - objectFillOuts[OPERATE_TSPLINE_CONE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("b_center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("axis"), ParamDialog::DataUnit{MPoint3{.0, .0, 1.0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("height"), ParamDialog::DataUnit{2.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("rf_num"), ParamDialog::DataUnit{8Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("vf_num"), ParamDialog::DataUnit{4Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_BOOL, tr("bottom"), ParamDialog::DataUnit{true}), - }; - objectFillOuts[OPERATE_TSPLINE_TORUS] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius0"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius1"), ParamDialog::DataUnit{0.5}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("rf_num"), ParamDialog::DataUnit{8Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("vf_num"), ParamDialog::DataUnit{4Ui64}), - }; - objectFillOuts[OPERATE_TSPLINE_CIRCULAR] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{2.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle"), ParamDialog::DataUnit{0.5}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("c_num"), ParamDialog::DataUnit{4Ui64}), - }; + coreCommand_->createCubeMeshObject(data[1].value.c3, data[2].value.c3, + data[3].value.i, data[4].value.i, data[5].value.i); + } } -void RenderViewer::initMesh() -{ - objectFillOuts[OPERATE_MESH_PLANE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("p0"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("p1"), ParamDialog::DataUnit{MPoint3{2.0, 2.0, 0.0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("u_num"), ParamDialog::DataUnit{2Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("v_num"), ParamDialog::DataUnit{2Ui64}), - }; - - objectFillOuts[OPERATE_MESH_CUBE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("first"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("second"), ParamDialog::DataUnit{MPoint3{1.0, 1.0, 1.0}}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("x_num"), ParamDialog::DataUnit{2Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("y_num"), ParamDialog::DataUnit{2Ui64}), - ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("z_num"), ParamDialog::DataUnit{2Ui64}), - }; -} +//void RenderViewer::slotCreatCubeMeshAccepted(const ParamDialog::Response& data) { +// +// AMCAX::Coord3 c_co = data[0].second.point; +// AMCAX::Coord3 s_co = data[1].second.point; +// +// MPoint3 c(c_co.X(), c_co.Y(), c_co.Z()); +// MPoint3 s(s_co.X(), s_co.Y(), s_co.Z()); +// size_t x_num = data[2].second.s; +// size_t y_num = data[3].second.s; +// size_t z_num = data[4].second.s; +// coreCommand_->createCubeMeshObject(c, s, x_num, y_num, z_num); +// update(); +// +// objectFillOuts[OPERATE_MESH_CUBE][0].def.point = c_co; +// objectFillOuts[OPERATE_MESH_CUBE][1].def.point = s_co; +// objectFillOuts[OPERATE_MESH_CUBE][2].def.s = x_num; +// objectFillOuts[OPERATE_MESH_CUBE][3].def.s = y_num; +// objectFillOuts[OPERATE_MESH_CUBE][4].def.s = z_num; +//} + +//void RenderViewer::initTSpline() +//{ +// objectFillOuts[OPERATE_TSPLINE_PLANE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("p0p"), ParamDialog::DataUnit{MPoint3(0.0, 0.0, 0.0)}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("p1p"), ParamDialog::DataUnit{MPoint3(2.0, 2.0, 0.0)}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("u_num"), ParamDialog::DataUnit{3Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("v_num"), ParamDialog::DataUnit{3Ui64}), +// }; +// objectFillOuts[OPERATE_TSPLINE_CUBE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("first"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("second"), ParamDialog::DataUnit{MPoint3{1.0, 1.0, 1.0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("x_num"), ParamDialog::DataUnit{1Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("y_num"), ParamDialog::DataUnit{1Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("z_num"), ParamDialog::DataUnit{1Ui64}), +// }; +// objectFillOuts[OPERATE_TSPLINE_UVSPHERE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("rf_num"), ParamDialog::DataUnit{8Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("vf_num"), ParamDialog::DataUnit{8Ui64}), +// }; +// objectFillOuts[OPERATE_TSPLINE_QUADSPHERE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("subtime"), ParamDialog::DataUnit{2Ui64}), +// }; +// objectFillOuts[OPERATE_TSPLINE_CYLINDER] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("b_center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("axis"), ParamDialog::DataUnit{MPoint3{.0, .0, 1.0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("height"), ParamDialog::DataUnit{2.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("rf_num"), ParamDialog::DataUnit{8Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("vf_num"), ParamDialog::DataUnit{4Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_BOOL, tr("top"), ParamDialog::DataUnit{true}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_BOOL, tr("bottom"), ParamDialog::DataUnit{true}), +// }; +// objectFillOuts[OPERATE_TSPLINE_CONE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("b_center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("axis"), ParamDialog::DataUnit{MPoint3{.0, .0, 1.0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("height"), ParamDialog::DataUnit{2.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("rf_num"), ParamDialog::DataUnit{8Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("vf_num"), ParamDialog::DataUnit{4Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_BOOL, tr("bottom"), ParamDialog::DataUnit{true}), +// }; +// objectFillOuts[OPERATE_TSPLINE_TORUS] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius0"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius1"), ParamDialog::DataUnit{0.5}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("rf_num"), ParamDialog::DataUnit{8Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("vf_num"), ParamDialog::DataUnit{4Ui64}), +// }; +// objectFillOuts[OPERATE_TSPLINE_CIRCULAR] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{2.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle"), ParamDialog::DataUnit{0.5}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("c_num"), ParamDialog::DataUnit{4Ui64}), +// }; +//} +// +//void RenderViewer::initMesh() +//{ +// objectFillOuts[OPERATE_MESH_PLANE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("p0"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("p1"), ParamDialog::DataUnit{MPoint3{2.0, 2.0, 0.0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("u_num"), ParamDialog::DataUnit{2Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("v_num"), ParamDialog::DataUnit{2Ui64}), +// }; +// +// objectFillOuts[OPERATE_MESH_CUBE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("first"), ParamDialog::DataUnit{MPoint3{.0, .0, .0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("second"), ParamDialog::DataUnit{MPoint3{1.0, 1.0, 1.0}}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("x_num"), ParamDialog::DataUnit{2Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("y_num"), ParamDialog::DataUnit{2Ui64}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_SIZE_T, tr("z_num"), ParamDialog::DataUnit{2Ui64}), +// }; +//} void RenderViewer::slotMeshSubdiveCatmullClark() { MeshObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, @@ -1559,194 +1597,350 @@ void RenderViewer::slotCreateSingleFaceTSpline() { } void RenderViewer::slotCreatePlaneTSpline() { - dialog->addElements(objectFillOuts[OPERATE_TSPLINE_PLANE]); - dialog->setWindowTitle(tr("Create a Plane TSpline")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatePlaneTSplineAccepted); - dialog->open(); -} + // @lw + //dialog->addElements(objectFillOuts[OPERATE_TSPLINE_PLANE]); -void RenderViewer::slotCreatePlaneTSplineAccepted(const ParamDialog::Response& data) { - MPoint3 p0(data[0].second.point); - MPoint3 p1(data[1].second.point); - size_t u_num = data[2].second.s; - size_t v_num = data[3].second.s; - coreCommand_->createPlaneObject(p0, p1, u_num, v_num); - update(); + //dialog->setWindowTitle(tr("Create a Plane TSpline")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatePlaneTSplineAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("平面"), + { + {tr("位置与形状")}, + {tr("第一点"),AMCAX::Coord2(0.0,0.0)}, + {tr("对角点"),AMCAX::Coord2(1.0,1.0)}, + {tr("X向分段"),(size_t)3}, + {tr("Y向分段"),(size_t)3}, + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); - // updating the value stored in objectFillOuts so that the next time the dialog launches - // the data will remain the same as the user used the previous time - objectFillOuts[OPERATE_TSPLINE_PLANE][0].def.point = p0.Coord(); - objectFillOuts[OPERATE_TSPLINE_PLANE][1].def.point = p1.Coord(); - objectFillOuts[OPERATE_TSPLINE_PLANE][2].def.s = u_num; - objectFillOuts[OPERATE_TSPLINE_PLANE][3].def.s = v_num; + coreCommand_->createPlaneObject(data[1].value.c2, data[2].value.c2, data[3].value.i, data[4].value.i); + } } + void RenderViewer::slotCreateCubeTSpline() { - dialog->addElements(objectFillOuts[OPERATE_TSPLINE_CUBE]); - dialog->setWindowTitle(tr("Create a Cube TSpline")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateCubeTSplineAccepted); - dialog->open(); -} + //dialog->addElements(objectFillOuts[OPERATE_TSPLINE_CUBE]); + //dialog->setWindowTitle(tr("Create a Cube TSpline")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateCubeTSplineAccepted); + //dialog->open(); -void RenderViewer::slotCreateCubeTSplineAccepted(const ParamDialog::Response& data) { - MPoint3 c(data[0].second.point); - MPoint3 s(data[1].second.point); - size_t x_num = data[2].second.s; - size_t y_num = data[3].second.s; - size_t z_num = data[4].second.s; - coreCommand_->createCubeObject(c, s, x_num, y_num, z_num); - update(); + DialogAuto dialog(this); + + dialog.SetData(tr("立方体"), + { + {tr("位置与形状")}, + {tr("第一点"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("对角点"),AMCAX::Coord3(1.0,1.0,1.0)}, + {tr("X向分段"),(size_t)2}, + {tr("Y向分段"),(size_t)2}, + {tr("Z向分段"),(size_t)2}, + }); + + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createCubeObject(data[1].value.c3, data[2].value.c3, + data[3].value.i, data[4].value.i, data[5].value.i); + } - // updating the value stored in objectFillOuts so that the next time the dialog launches - // the data will remain the same as the user used the previous time - objectFillOuts[OPERATE_TSPLINE_CUBE][0].def.point = c.Coord(); - objectFillOuts[OPERATE_TSPLINE_CUBE][1].def.point = s.Coord(); - objectFillOuts[OPERATE_TSPLINE_CUBE][2].def.s = x_num; - objectFillOuts[OPERATE_TSPLINE_CUBE][3].def.s = y_num; - objectFillOuts[OPERATE_TSPLINE_CUBE][4].def.s = z_num; } +//void RenderViewer::slotCreateCubeTSplineAccepted(const ParamDialog::Response& data) { +// MPoint3 c(data[0].second.point); +// MPoint3 s(data[1].second.point); +// size_t x_num = data[2].second.s; +// size_t y_num = data[3].second.s; +// size_t z_num = data[4].second.s; +// coreCommand_->createCubeObject(c, s, x_num, y_num, z_num); +// update(); +// +// // updating the value stored in objectFillOuts so that the next time the dialog launches +// // the data will remain the same as the user used the previous time +// objectFillOuts[OPERATE_TSPLINE_CUBE][0].def.point = c.Coord(); +// objectFillOuts[OPERATE_TSPLINE_CUBE][1].def.point = s.Coord(); +// objectFillOuts[OPERATE_TSPLINE_CUBE][2].def.s = x_num; +// objectFillOuts[OPERATE_TSPLINE_CUBE][3].def.s = y_num; +// objectFillOuts[OPERATE_TSPLINE_CUBE][4].def.s = z_num; +//} + void RenderViewer::slotCreateUVSphereTSpline() { - dialog->addElements(objectFillOuts[OPERATE_TSPLINE_UVSPHERE]); - dialog->setWindowTitle(tr("Create a UVSphere TSpline")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateUVSphereTSplineAccepted); - dialog->open(); -} + DialogAuto dialog(this); -void RenderViewer::slotCreateUVSphereTSplineAccepted(const ParamDialog::Response& data) { - MPoint3 c(data[0].second.point); - double radius = data[1].second.d; - size_t rf_num = data[2].second.s; - size_t vf_num = data[3].second.s; - coreCommand_->createUVSphereObject(c, radius, rf_num, vf_num); - update(); + dialog.SetData(tr("球体"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("形状")}, + {tr("半径"),1.0,""}, + {tr("径向分段"),(size_t)8}, + {tr("纬度分段"),(size_t)8}, + }); + + + if (dialog.exec()) + { + auto& data = dialog.GetData(); - objectFillOuts[OPERATE_TSPLINE_UVSPHERE][0].def.point = c.Coord(); - objectFillOuts[OPERATE_TSPLINE_UVSPHERE][1].def.d = radius; - objectFillOuts[OPERATE_TSPLINE_UVSPHERE][2].def.s = rf_num; - objectFillOuts[OPERATE_TSPLINE_UVSPHERE][3].def.s = vf_num; + coreCommand_->createUVSphereObject(MPoint3(data[1].value.c3), data[3].value.d, + data[4].value.i, data[5].value.i); + } } +//void RenderViewer::slotCreateUVSphereTSplineAccepted(const ParamDialog::Response& data) { +// MPoint3 c(data[0].second.point); +// double radius = data[1].second.d; +// size_t rf_num = data[2].second.s; +// size_t vf_num = data[3].second.s; +// coreCommand_->createUVSphereObject(c, radius, rf_num, vf_num); +// update(); +// +// objectFillOuts[OPERATE_TSPLINE_UVSPHERE][0].def.point = c.Coord(); +// objectFillOuts[OPERATE_TSPLINE_UVSPHERE][1].def.d = radius; +// objectFillOuts[OPERATE_TSPLINE_UVSPHERE][2].def.s = rf_num; +// objectFillOuts[OPERATE_TSPLINE_UVSPHERE][3].def.s = vf_num; +//} + void RenderViewer::slotCreateQuadSphereTSpline() { - dialog->addElements(objectFillOuts[OPERATE_TSPLINE_QUADSPHERE]); - dialog->setWindowTitle(tr("Create a Quad Sphere TSpline")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateQuadSphereTSplineAccepted); - dialog->open(); -} -void RenderViewer::slotCreateQuadSphereTSplineAccepted(const ParamDialog::Response& data) { - MPoint3 c(data[0].second.point); - double radius = data[1].second.d; - size_t subtime = data[2].second.s; - coreCommand_->createSubdSphereObject(c, radius, subtime); - update(); + //dialog->addElements(objectFillOuts[OPERATE_TSPLINE_QUADSPHERE]); + //dialog->setWindowTitle(tr("Create a Quad Sphere TSpline")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateQuadSphereTSplineAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("球体"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("形状")}, + {tr("半径"),1.0,""}, + {tr("细分次数"),(size_t)2}, + }); + + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createSubdSphereObject(MPoint3(data[1].value.c3), data[3].value.d, + data[4].value.i); + } - objectFillOuts[OPERATE_TSPLINE_QUADSPHERE][0].def.point = c.Coord(); - objectFillOuts[OPERATE_TSPLINE_QUADSPHERE][1].def.d = radius; - objectFillOuts[OPERATE_TSPLINE_QUADSPHERE][2].def.s = subtime; } +//void RenderViewer::slotCreateQuadSphereTSplineAccepted(const ParamDialog::Response& data) { +// MPoint3 c(data[0].second.point); +// double radius = data[1].second.d; +// size_t subtime = data[2].second.s; +// coreCommand_->createSubdSphereObject(c, radius, subtime); +// update(); +// +// objectFillOuts[OPERATE_TSPLINE_QUADSPHERE][0].def.point = c.Coord(); +// objectFillOuts[OPERATE_TSPLINE_QUADSPHERE][1].def.d = radius; +// objectFillOuts[OPERATE_TSPLINE_QUADSPHERE][2].def.s = subtime; +//} void RenderViewer::slotCreateCylinderTSpline() { - dialog->addElements(objectFillOuts[OPERATE_TSPLINE_CYLINDER]); - dialog->setWindowTitle(tr("Create a Cylinder TSpline")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateCylinderTSplineAccepted); - dialog->open(); -} -void RenderViewer::slotCreateCylinderTSplineAccepted(const ParamDialog::Response& data) { - MPoint3 c(data[0].second.point); - MVector3 v(data[1].second.point); - double radius = data[2].second.d; - double height = data[3].second.d; - size_t rf_num = data[4].second.s; - size_t vf_num = data[5].second.s; - bool top = data[6].second.b; - bool bottom = data[7].second.b; - coreCommand_->createCylinderObject(c, v, radius, height, rf_num, vf_num, top, bottom); - update(); + //dialog->addElements(objectFillOuts[OPERATE_TSPLINE_CYLINDER]); + //dialog->setWindowTitle(tr("Create a Cylinder TSpline")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateCylinderTSplineAccepted); + //dialog->open(); + DialogAuto dialog(this); - objectFillOuts[OPERATE_TSPLINE_CYLINDER][0].def.point = c.Coord(); - objectFillOuts[OPERATE_TSPLINE_CYLINDER][1].def.vec = v.Coord(); - objectFillOuts[OPERATE_TSPLINE_CYLINDER][2].def.d = radius; - objectFillOuts[OPERATE_TSPLINE_CYLINDER][3].def.d = height; - objectFillOuts[OPERATE_TSPLINE_CYLINDER][4].def.s = rf_num; - objectFillOuts[OPERATE_TSPLINE_CYLINDER][5].def.s = vf_num; - objectFillOuts[OPERATE_TSPLINE_CYLINDER][6].def.b = top; - objectFillOuts[OPERATE_TSPLINE_CYLINDER][7].def.b = bottom; + dialog.SetData(tr("圆柱体"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("轴向"),AMCAX::Coord3(0.0,0.0,1.0)}, + {tr("形状")}, + {tr("半径"),1.0,""}, + {tr("高度"),2.0,""}, + {tr("径向分段"),(size_t)8}, + {tr("高度分段"),(size_t)4}, + {tr("顶部封盖"),true}, + {tr("底部封盖"),true}, + }); + + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createCylinderObject(MPoint3(data[1].value.c3), MVector3(data[2].value.c3), + data[4].value.d, data[5].value.d, data[6].value.i, data[7].value.i, data[8].value.b, + data[9].value.b); + } } +//void RenderViewer::slotCreateCylinderTSplineAccepted(const ParamDialog::Response& data) { +// MPoint3 c(data[0].second.point); +// MVector3 v(data[1].second.point); +// double radius = data[2].second.d; +// double height = data[3].second.d; +// size_t rf_num = data[4].second.s; +// size_t vf_num = data[5].second.s; +// bool top = data[6].second.b; +// bool bottom = data[7].second.b; +// coreCommand_->createCylinderObject(c, v, radius, height, rf_num, vf_num, top, bottom); +// update(); +// +// objectFillOuts[OPERATE_TSPLINE_CYLINDER][0].def.point = c.Coord(); +// objectFillOuts[OPERATE_TSPLINE_CYLINDER][1].def.vec = v.Coord(); +// objectFillOuts[OPERATE_TSPLINE_CYLINDER][2].def.d = radius; +// objectFillOuts[OPERATE_TSPLINE_CYLINDER][3].def.d = height; +// objectFillOuts[OPERATE_TSPLINE_CYLINDER][4].def.s = rf_num; +// objectFillOuts[OPERATE_TSPLINE_CYLINDER][5].def.s = vf_num; +// objectFillOuts[OPERATE_TSPLINE_CYLINDER][6].def.b = top; +// objectFillOuts[OPERATE_TSPLINE_CYLINDER][7].def.b = bottom; +//} + void RenderViewer::slotCreateConeTSpline() { - dialog->addElements(objectFillOuts[OPERATE_TSPLINE_CONE]); - dialog->setWindowTitle(tr("Create a Cone TSpline")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateConeTSplineAccepted); - dialog->open(); -} -void RenderViewer::slotCreateConeTSplineAccepted(const ParamDialog::Response& data) { - MPoint3 c(data[0].second.point); - MVector3 v(data[1].second.point); - double radius = data[2].second.d; - double height = data[3].second.d; - size_t rf_num = data[4].second.s; - size_t vf_num = data[5].second.s; - bool bottom = data[6].second.b; - coreCommand_->createConeObject(c, v, radius, height, rf_num, vf_num, bottom); - update(); + //dialog->addElements(objectFillOuts[OPERATE_TSPLINE_CONE]); + //dialog->setWindowTitle(tr("Create a Cone TSpline")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateConeTSplineAccepted); + //dialog->open(); + + DialogAuto dialog(this); + + dialog.SetData(tr("圆锥"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("轴向"),AMCAX::Coord3(0.0,0.0,1.0)}, + {tr("形状")}, + {tr("半径"),1.0,""}, + {tr("高度"),2.0,""}, + {tr("径向分段"),(size_t)8}, + {tr("高度分段"),(size_t)4}, + {tr("底部封盖"),true}, + }); + + + if (dialog.exec()) + { + auto& data = dialog.GetData(); - objectFillOuts[OPERATE_TSPLINE_CONE][0].def.point = c.Coord(); - objectFillOuts[OPERATE_TSPLINE_CONE][1].def.point = v.Coord(); - objectFillOuts[OPERATE_TSPLINE_CONE][2].def.d = radius; - objectFillOuts[OPERATE_TSPLINE_CONE][3].def.d = height; - objectFillOuts[OPERATE_TSPLINE_CONE][4].def.s = rf_num; - objectFillOuts[OPERATE_TSPLINE_CONE][5].def.s = vf_num; - objectFillOuts[OPERATE_TSPLINE_CONE][6].def.b = bottom; + coreCommand_->createConeObject(MPoint3(data[1].value.c3), MVector3(data[2].value.c3), + data[4].value.d, data[5].value.d, data[6].value.i, data[7].value.i, data[8].value.b); + } } +//void RenderViewer::slotCreateConeTSplineAccepted(const ParamDialog::Response& data) { +// MPoint3 c(data[0].second.point); +// MVector3 v(data[1].second.point); +// double radius = data[2].second.d; +// double height = data[3].second.d; +// size_t rf_num = data[4].second.s; +// size_t vf_num = data[5].second.s; +// bool bottom = data[6].second.b; +// coreCommand_->createConeObject(c, v, radius, height, rf_num, vf_num, bottom); +// update(); +// +// objectFillOuts[OPERATE_TSPLINE_CONE][0].def.point = c.Coord(); +// objectFillOuts[OPERATE_TSPLINE_CONE][1].def.point = v.Coord(); +// objectFillOuts[OPERATE_TSPLINE_CONE][2].def.d = radius; +// objectFillOuts[OPERATE_TSPLINE_CONE][3].def.d = height; +// objectFillOuts[OPERATE_TSPLINE_CONE][4].def.s = rf_num; +// objectFillOuts[OPERATE_TSPLINE_CONE][5].def.s = vf_num; +// objectFillOuts[OPERATE_TSPLINE_CONE][6].def.b = bottom; +//} void RenderViewer::slotCreateTorusTSpline() { - dialog->addElements(objectFillOuts[OPERATE_TSPLINE_TORUS]); - dialog->setWindowTitle(tr("Create a Torus TSpline")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateTorusTSplineAccepted); - dialog->open(); -} -void RenderViewer::slotCreateTorusTSplineAccepted(const ParamDialog::Response& data) { - MPoint3 c(data[0].second.point); - double radius0 = data[1].second.d; - double radius1 = data[2].second.d; - size_t rf_num = data[3].second.s; - size_t vf_num = data[4].second.s; - coreCommand_->creatTourObject(c, radius0, radius1, rf_num, vf_num); - update(); + //dialog->addElements(objectFillOuts[OPERATE_TSPLINE_TORUS]); + //dialog->setWindowTitle(tr("Create a Torus TSpline")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateTorusTSplineAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("圆环"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("形状")}, + {tr("主环半径"),2.0,""}, + {tr("管道半径"),1.0,""}, + {tr("主环分段"),(size_t)8}, + {tr("管道分段"),(size_t)16}, + }); + + + if (dialog.exec()) + { + auto& data = dialog.GetData(); - objectFillOuts[OPERATE_TSPLINE_TORUS][0].def.point = c.Coord(); - objectFillOuts[OPERATE_TSPLINE_TORUS][1].def.d = radius0; - objectFillOuts[OPERATE_TSPLINE_TORUS][2].def.d = radius1; - objectFillOuts[OPERATE_TSPLINE_TORUS][3].def.s = rf_num; - objectFillOuts[OPERATE_TSPLINE_TORUS][4].def.s = vf_num; + coreCommand_->creatTourObject(MPoint3(data[1].value.c3), data[3].value.d, + data[4].value.d, data[5].value.i, data[6].value.i); + } } +//void RenderViewer::slotCreateTorusTSplineAccepted(const ParamDialog::Response& data) { +// //MPoint3 c(data[0].second.point); +// //double radius0 = data[1].second.d; +// //double radius1 = data[2].second.d; +// //size_t rf_num = data[3].second.s; +// //size_t vf_num = data[4].second.s; +// //coreCommand_->creatTourObject(c, radius0, radius1, rf_num, vf_num); +// //update(); +// +// //objectFillOuts[OPERATE_TSPLINE_TORUS][0].def.point = c.Coord(); +// //objectFillOuts[OPERATE_TSPLINE_TORUS][1].def.d = radius0; +// //objectFillOuts[OPERATE_TSPLINE_TORUS][2].def.d = radius1; +// //objectFillOuts[OPERATE_TSPLINE_TORUS][3].def.s = rf_num; +// //objectFillOuts[OPERATE_TSPLINE_TORUS][4].def.s = vf_num; +//} void RenderViewer::slotCreateCircularTSpline() { - dialog->addElements(objectFillOuts[OPERATE_TSPLINE_CIRCULAR]); - dialog->setWindowTitle(tr("Create a Circular TSpline")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateCircularTSplineAccepted); - dialog->open(); -} -void RenderViewer::slotCreateCircularTSplineAccepted(const ParamDialog::Response& data) { - MPoint3 c(data[0].second.point); - // //double angle = M_PI * 2; - // //size_t seg = 8; - // double angle = M_PI / 2; - // //size_t seg = angle / (M_PI / 2) + 1; - // size_t seg = 4; - double radius = data[1].second.d; - double angle = data[2].second.d; - size_t c_num = data[3].second.s; - coreCommand_->createCircular(c, radius, angle * M_PI, c_num); - update(); + //dialog->addElements(objectFillOuts[OPERATE_TSPLINE_CIRCULAR]); + //dialog->setWindowTitle(tr("Create a Circular TSpline")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateCircularTSplineAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("扇形"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("形状")}, + {tr("半径"),2.0,""}, + {tr("弧度"),1.0,"π"}, + {tr("分段"),(size_t)8}, + }); + + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createCircular(MPoint3(data[1].value.c3), data[3].value.d, + data[4].value.d * M_PI, data[5].value.i); + } - objectFillOuts[OPERATE_TSPLINE_CIRCULAR][0].def.point = c.Coord(); - objectFillOuts[OPERATE_TSPLINE_CIRCULAR][1].def.d = radius; - objectFillOuts[OPERATE_TSPLINE_CIRCULAR][2].def.d = angle; - objectFillOuts[OPERATE_TSPLINE_CIRCULAR][3].def.s = c_num; } +//void RenderViewer::slotCreateCircularTSplineAccepted(const ParamDialog::Response& data) { +// //MPoint3 c(data[0].second.point); +// //// //double angle = M_PI * 2; +// //// //size_t seg = 8; +// //// double angle = M_PI / 2; +// //// //size_t seg = angle / (M_PI / 2) + 1; +// //// size_t seg = 4; +// //double radius = data[1].second.d; +// //double angle = data[2].second.d; +// //size_t c_num = data[3].second.s; +// //coreCommand_->createCircular(c, radius, angle * M_PI, c_num); +// //update(); +// +// //objectFillOuts[OPERATE_TSPLINE_CIRCULAR][0].def.point = c.Coord(); +// //objectFillOuts[OPERATE_TSPLINE_CIRCULAR][1].def.d = radius; +// //objectFillOuts[OPERATE_TSPLINE_CIRCULAR][2].def.d = angle; +// //objectFillOuts[OPERATE_TSPLINE_CIRCULAR][3].def.s = c_num; +//} void RenderViewer::slotAddCreaseEdge() { @@ -1772,4 +1966,19 @@ void RenderViewer::slotBRepExtrudeMesh() { BaseObjectOperation(SelectModel::FACE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeFaceNew); +} + +void RenderViewer::setSelectModel(SelectModel model) { + if (select_model_ != model) + { + if (coreCommand_) + coreCommand_->setMeshOperationType(acamcad::MeshOperationType::NO_operotion); + setOperateType(OperationType::NO_OPERATION); + clearSelected(); + } + + select_model_ = model; + if (listen_) + listen_->refresh_select(model); + } \ No newline at end of file diff --git a/Src/Windows/RenderViewer.h b/Src/Windows/RenderViewer.h index 6f1bbddcfb5e31cfeaa680acd03f78c13c1cb51b..77765a6681cb4bc2e4cb669a63f5f718243e57c9 100644 --- a/Src/Windows/RenderViewer.h +++ b/Src/Windows/RenderViewer.h @@ -20,8 +20,7 @@ #include "../Object/ObjectDefine.h" #include "AxisOperate.h" #include "../Core/CoreDefine.h" -#include "ParamDialog.h" -#include "../record/Record.h" +//#include "ParamDialog.h" namespace acamcad { @@ -29,6 +28,14 @@ namespace acamcad class AMCore; class SelectTool; + class RenderViewerListen + { + + public: + virtual void refresh_view(DrawModel model) = 0; + virtual void refresh_select(SelectModel model) = 0; + }; + class RenderViewer : public QGLViewer { Q_OBJECT @@ -42,31 +49,6 @@ namespace acamcad AMCAX::Coord3 mouseMoveTrans(const QPoint& e_point, const AMCAX::Coord3& o_center); }; - enum OperateType { - - OPERATE_BREP_PLANE, - OPERATE_BREP_POLYGON, - OPERATE_BREP_CUBE, - OPERATE_BREP_SPHERE, - OPERATE_BREP_CYLINDER, - OPERATE_BREP_CONE, - OPERATE_BREP_TORUS, - OPERATE_BREP_PRISM, - OPERATE_BREP_EXTRUDE, - - OPERATE_MESH_PLANE, - OPERATE_MESH_CUBE, - - OPERATE_TSPLINE_PLANE, - OPERATE_TSPLINE_CUBE, - OPERATE_TSPLINE_UVSPHERE, - OPERATE_TSPLINE_QUADSPHERE, - OPERATE_TSPLINE_CYLINDER, - OPERATE_TSPLINE_CONE, - OPERATE_TSPLINE_TORUS, - OPERATE_TSPLINE_CIRCULAR, - }; - public: RenderViewer(QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); virtual ~RenderViewer(); @@ -87,9 +69,9 @@ namespace acamcad virtual void keyReleaseEvent(QKeyEvent*); private: - void initTSpline(); - void initMesh(); - void initBRep(); + //void initTSpline(); + //void initMesh(); + //void initBRep(); private: void beginPickOperationSingle(); @@ -152,7 +134,7 @@ namespace acamcad Qt::MouseButton click_buttom; public: - void setSelectModel(SelectModel model) { select_model_ = model; } + void setSelectModel(SelectModel model); SelectModel getSelectModel() { return select_model_; } void clearSelected(); @@ -179,7 +161,14 @@ namespace acamcad /*! @name Draw Method */ public: - void setDrawModel(DrawModel model) { draw_model_ = model; } + void setDrawModel(DrawModel model) { + draw_model_ = model; + + if (listen_) + listen_->refresh_view(model); + + } + DrawModel getDrawModel() { return draw_model_; } private: @@ -220,12 +209,12 @@ namespace acamcad void setOperateType(OperationType op_type) { operate_type_ = op_type; } OperationType operateType() { return operate_type_; } void setOperationVector(const AMCAX::Coord3d& vector) { operation_vector_ = vector; } - void setRecord(Record* record) { record = record; } + //void setRecord(Record* record) { record = record; } private: OperationType operate_type_; QVector selectActions; - QMap> objectFillOuts; + //QMap> objectFillOuts; private: //相机起点,选择射线方向 @@ -249,15 +238,18 @@ namespace acamcad public: void setDataManger(DataManager* dataManager); void setCore(AMCore* core); + void setListen(RenderViewerListen* listen); private: DataManager* dataManager_; AMCore* coreCommand_; SelectTool* selectTool_; - ParamDialog* dialog; + //ParamDialog* dialog; + + RenderViewerListen* listen_; //acamcad::Record record; - private slots: + public slots: void selectCheckChanged(); @@ -272,7 +264,7 @@ namespace acamcad void TSplineObjectOperation(const SelectModel& s_model, const OperationType& op_type, const MeshOperationType mesh_operate_type); - void disconnectDialog(); + //void disconnectDialog(); public slots: void slotUndo(); @@ -281,7 +273,7 @@ namespace acamcad friend class OperateToolBar; friend class ACAMWindow; - private slots: // base tab + public slots: // base tab void slotRenderMode_Wire(); void slotRenderMode_Rendering(); void slotRenderMode_Shading(); @@ -301,47 +293,47 @@ namespace acamcad void slotSetWorkPlane(); - private slots: // BRep tab + // BRep tab void slotCreateSingleFaceBRep(); int getLastPersistentId(); void slotCreatPlaneBRep(); - void slotCreatPlaneBRepAccepted(const ParamDialog::Response& data); + //void slotCreatPlaneBRepAccepted(const ParamDialog::Response& data); void slotCreatCubeBRep(); - void slotCreatCubeBRepAccepted(const ParamDialog::Response& data); + //void slotCreatCubeBRepAccepted(const ParamDialog::Response& data); void slotCreateSphereBRep(); - void slotCreateSphereBRepAccepted(const ParamDialog::Response& data); + //void slotCreateSphereBRepAccepted(const ParamDialog::Response& data); void slotCreateCylinderBRep(); - void slotCreateCylinderBRepAccepted(const ParamDialog::Response& data); + //void slotCreateCylinderBRepAccepted(const ParamDialog::Response& data); void slotCreateConeBrep(); - void slotCreateConeBrepAccepted(const ParamDialog::Response& data); + //void slotCreateConeBrepAccepted(const ParamDialog::Response& data); void slotCreateTorusBRep(); - void slotCreateTorusBRepAccepted(const ParamDialog::Response& data); + //void slotCreateTorusBRepAccepted(const ParamDialog::Response& data); void slotCreatePrismBRep(); - void slotCreatePrismBRepAccepted(const ParamDialog::Response& data); + //void slotCreatePrismBRepAccepted(const ParamDialog::Response& data); void slotBRepExtrude(); - void slotBRepExtrudeAccepted(const ParamDialog::Response& data); + //void slotBRepExtrudeAccepted(const ParamDialog::Response& data); void slotCreatePolygonBRep(); - void slotCreatePolygonBRepAccepted(const ParamDialog::Response& data); + //void slotCreatePolygonBRepAccepted(const ParamDialog::Response& data); // Mesh Tab void slotCreateSingleFaceMesh(); void slotCreatPlaneMesh(); - void slotCreatPlaneMeshAccepted(const ParamDialog::Response& data); + //void slotCreatPlaneMeshAccepted(const ParamDialog::Response& data); void slotCreatCubeMesh(); - void slotCreatCubeMeshAccepted(const ParamDialog::Response& data); + //void slotCreatCubeMeshAccepted(const ParamDialog::Response& data); void slotMeshSubdiveCatmullClark(); @@ -354,28 +346,28 @@ namespace acamcad void slotCreateSingleFaceTSpline(); void slotCreatePlaneTSpline(); - void slotCreatePlaneTSplineAccepted(const ParamDialog::Response& data); + //void slotCreatePlaneTSplineAccepted(const ParamDialog::Response& data); void slotCreateCubeTSpline(); - void slotCreateCubeTSplineAccepted(const ParamDialog::Response& data); + //void slotCreateCubeTSplineAccepted(const ParamDialog::Response& data); void slotCreateUVSphereTSpline(); - void slotCreateUVSphereTSplineAccepted(const ParamDialog::Response& data); + ///void slotCreateUVSphereTSplineAccepted(const ParamDialog::Response& data); void slotCreateQuadSphereTSpline(); - void slotCreateQuadSphereTSplineAccepted(const ParamDialog::Response& data); + ///void slotCreateQuadSphereTSplineAccepted(const ParamDialog::Response& data); void slotCreateCylinderTSpline(); - void slotCreateCylinderTSplineAccepted(const ParamDialog::Response& data); + //void slotCreateCylinderTSplineAccepted(const ParamDialog::Response& data); void slotCreateConeTSpline(); - void slotCreateConeTSplineAccepted(const ParamDialog::Response& data); + //void slotCreateConeTSplineAccepted(const ParamDialog::Response& data); void slotCreateTorusTSpline(); - void slotCreateTorusTSplineAccepted(const ParamDialog::Response& data); + // void slotCreateTorusTSplineAccepted(const ParamDialog::Response& data); void slotCreateCircularTSpline(); - void slotCreateCircularTSplineAccepted(const ParamDialog::Response& data); + // void slotCreateCircularTSplineAccepted(const ParamDialog::Response& data); void slotAddCreaseEdge(); void slotRemoveCreaseEdge(); @@ -385,4 +377,4 @@ namespace acamcad }; -} +}; diff --git a/Src/Windows/RenderViewerOps.cpp b/Src/Windows/RenderViewerOps.cpp index bcd2ec421d01c55f8c5f7741950ea00932d1dcc6..e0c93af0916f0dc37c952f059880e5698ea9def8 100644 --- a/Src/Windows/RenderViewerOps.cpp +++ b/Src/Windows/RenderViewerOps.cpp @@ -18,12 +18,12 @@ #include "../Core/CoreDefine.h" #include "../Core/ACAMCore.h" #include "RenderViewer.h" -#include "ACAMWindow.h" #include #include #include #include +#include using namespace acamcad; @@ -70,6 +70,6 @@ void RenderViewer::BRepObjectOperation(const SelectModel& s_model, } -void RenderViewer::disconnectDialog() { - disconnect(dialog, &ParamDialog::dataReady, this, nullptr); -} \ No newline at end of file +//void RenderViewer::disconnectDialog() { +// disconnect(dialog, &ParamDialog::dataReady, this, nullptr); +//} \ No newline at end of file diff --git a/Src/Windows/RenderViewerOpsBRep.cpp b/Src/Windows/RenderViewerOpsBRep.cpp index 44cfebaf7397b82da99a3cb359874b7a19d29d50..91fbd2b519c6c843028c1458e88a87176abee5be 100644 --- a/Src/Windows/RenderViewerOpsBRep.cpp +++ b/Src/Windows/RenderViewerOpsBRep.cpp @@ -15,72 +15,73 @@ */ #include "RenderViewer.h" #include "../Core/ACAMCore.h" +#include "DialogAuto.h" using namespace acamcad; -// ==================== BRep TAB NON SLOT METHODS ==================== -void RenderViewer::initBRep() -{ - objectFillOuts[OPERATE_BREP_PLANE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("plane")}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("first"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("second"), ParamDialog::DataUnit{ AMCAX::Coord3{2.0, 2.0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), - }; - objectFillOuts[OPERATE_BREP_CUBE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("cube")}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("p0"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("p1"), ParamDialog::DataUnit{ AMCAX::Coord3{1.0, 1.0, 1.0}, ParamDialog::ParamType::TYPE_MPOINT3}), - }; - objectFillOuts[OPERATE_BREP_SPHERE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("sphere")}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle1"), ParamDialog::DataUnit{2.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle2 (NOP)"), ParamDialog::DataUnit{0.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle3 (NOP)"), ParamDialog::DataUnit{0.0}), - }; - objectFillOuts[OPERATE_BREP_CYLINDER] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("cylinder")}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("b_center"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("axis"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 1.0}, ParamDialog::ParamType::TYPE_MVEC3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("X Axis direction"), ParamDialog::DataUnit{ AMCAX::Coord3{1.0, .0, .0}, ParamDialog::ParamType::TYPE_MVEC3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("height"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle"), ParamDialog::DataUnit{2.0}), - }; - objectFillOuts[OPERATE_BREP_CONE] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("cone")}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("b_center"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("axis"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 1.0}, ParamDialog::ParamType::TYPE_MVEC3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("X Axis direction"), ParamDialog::DataUnit{ AMCAX::Coord3{1.0, .0, .0}, ParamDialog::ParamType::TYPE_MVEC3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radiuR"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radiur"), ParamDialog::DataUnit{0.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("height"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle"), ParamDialog::DataUnit{2.0}), - }; - objectFillOuts[OPERATE_BREP_TORUS] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("torus")}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC3, tr("axis"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 1.0}, ParamDialog::ParamType::TYPE_MVEC3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("X Axis direction"), ParamDialog::DataUnit{ AMCAX::Coord3{1.0, .0, .0}, ParamDialog::ParamType::TYPE_MVEC3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radiu0"), ParamDialog::DataUnit{2.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radiu1"), ParamDialog::DataUnit{1.0}), - ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle"), ParamDialog::DataUnit{2.0}), - }; - objectFillOuts[OPERATE_BREP_PRISM] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("prism")}), - ParamDialog::RequestUnit(ParamDialog::TYPE_ARR_MPOINT3, tr("Point List"), ParamDialog::DataUnit{AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), - ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC3, tr("Extrude Axis"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 2.0}, ParamDialog::ParamType::TYPE_MVEC3}), - }; - objectFillOuts[OPERATE_BREP_EXTRUDE] = QVector{ - std::make_tuple(ParamDialog::TYPE_MVEC, tr("dir"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 1.0}, ParamDialog::ParamType::TYPE_MVEC3}), - }; - objectFillOuts[OPERATE_BREP_POLYGON] = QVector{ - ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("prism")}), - ParamDialog::RequestUnit(ParamDialog::TYPE_ARR_MPOINT3, tr("Point List"), ParamDialog::DataUnit{AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), - }; -} +//// ==================== BRep TAB NON SLOT METHODS ==================== +//void RenderViewer::initBRep() +//{ +// objectFillOuts[OPERATE_BREP_PLANE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("plane")}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("first"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3_2, tr("second"), ParamDialog::DataUnit{ AMCAX::Coord3{2.0, 2.0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// }; +// objectFillOuts[OPERATE_BREP_CUBE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("cube")}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("p0"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("p1"), ParamDialog::DataUnit{ AMCAX::Coord3{1.0, 1.0, 1.0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// }; +// objectFillOuts[OPERATE_BREP_SPHERE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("sphere")}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle1"), ParamDialog::DataUnit{2.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle2 (NOP)"), ParamDialog::DataUnit{0.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle3 (NOP)"), ParamDialog::DataUnit{0.0}), +// }; +// objectFillOuts[OPERATE_BREP_CYLINDER] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("cylinder")}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("b_center"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("axis"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 1.0}, ParamDialog::ParamType::TYPE_MVEC3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("X Axis direction"), ParamDialog::DataUnit{ AMCAX::Coord3{1.0, .0, .0}, ParamDialog::ParamType::TYPE_MVEC3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radius"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("height"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle"), ParamDialog::DataUnit{2.0}), +// }; +// objectFillOuts[OPERATE_BREP_CONE] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("cone")}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("b_center"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("axis"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 1.0}, ParamDialog::ParamType::TYPE_MVEC3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("X Axis direction"), ParamDialog::DataUnit{ AMCAX::Coord3{1.0, .0, .0}, ParamDialog::ParamType::TYPE_MVEC3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radiuR"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radiur"), ParamDialog::DataUnit{0.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("height"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle"), ParamDialog::DataUnit{2.0}), +// }; +// objectFillOuts[OPERATE_BREP_TORUS] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("torus")}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MPOINT3, tr("center"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC3, tr("axis"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 1.0}, ParamDialog::ParamType::TYPE_MVEC3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC, tr("X Axis direction"), ParamDialog::DataUnit{ AMCAX::Coord3{1.0, .0, .0}, ParamDialog::ParamType::TYPE_MVEC3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radiu0"), ParamDialog::DataUnit{2.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE, tr("radiu1"), ParamDialog::DataUnit{1.0}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_DOUBLE_PI, tr("angle"), ParamDialog::DataUnit{2.0}), +// }; +// objectFillOuts[OPERATE_BREP_PRISM] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("prism")}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_ARR_MPOINT3, tr("Point List"), ParamDialog::DataUnit{AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_MVEC3, tr("Extrude Axis"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 2.0}, ParamDialog::ParamType::TYPE_MVEC3}), +// }; +// objectFillOuts[OPERATE_BREP_EXTRUDE] = QVector{ +// std::make_tuple(ParamDialog::TYPE_MVEC, tr("dir"), ParamDialog::DataUnit{ AMCAX::Coord3{.0, .0, 1.0}, ParamDialog::ParamType::TYPE_MVEC3}), +// }; +// objectFillOuts[OPERATE_BREP_POLYGON] = QVector{ +// ParamDialog::RequestUnit(ParamDialog::TYPE_STRING, tr("Label (Group)"), ParamDialog::DataUnit{tr("prism")}), +// ParamDialog::RequestUnit(ParamDialog::TYPE_ARR_MPOINT3, tr("Point List"), ParamDialog::DataUnit{AMCAX::Coord3{.0, .0, .0}, ParamDialog::ParamType::TYPE_MPOINT3}), +// }; +//} // ==================== BRep TAB ==================== void RenderViewer::slotCreateSingleFaceBRep() { @@ -93,224 +94,405 @@ int RenderViewer::getLastPersistentId() { } void RenderViewer::slotCreatPlaneBRep() { - dialog->addElements(objectFillOuts[OPERATE_BREP_PLANE]); - dialog->setWindowTitle(tr("Create a plane mesh")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatPlaneBRepAccepted); - dialog->open(); -} -void RenderViewer::slotCreatPlaneBRepAccepted(const ParamDialog::Response& data) -{ - QString label(data[0].second.str); - AMCAX::Coord3 p0(data[1].second.point); - AMCAX::Coord3 p1(data[2].second.point); - coreCommand_->createPlaneBRepObject(p0, p1, label.toStdString()); - //record.recordPlane(getLastPersistentId(), p0, p1, label.toStdString()); - update(); - - objectFillOuts[OPERATE_BREP_PLANE][0].def.str = label; - objectFillOuts[OPERATE_BREP_PLANE][1].def.point = p0; - objectFillOuts[OPERATE_BREP_PLANE][2].def.point = p1; + //dialog->addElements(objectFillOuts[OPERATE_BREP_PLANE]); + //dialog->setWindowTitle(tr("Create a plane mesh")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatPlaneBRepAccepted); + //dialog->open(); + + DialogAuto dialog(this); + + dialog.SetData(tr("平面"), + { + {tr("位置与形状")}, + {tr("起始点"),AMCAX::Coord2(0.0,0.0)}, + {tr("对角点"),AMCAX::Coord2(1.0,1.0)}, + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createPlaneBRepObject(data[1].value.c2, data[2].value.c2); + } } +//void RenderViewer::slotCreatPlaneBRepAccepted(const ParamDialog::Response& data) +//{ +// QString label(data[0].second.str); +// AMCAX::Coord3 p0(data[1].second.point); +// AMCAX::Coord3 p1(data[2].second.point); +// coreCommand_->createPlaneBRepObject(p0, p1, label.toStdString()); +// //record.recordPlane(getLastPersistentId(), p0, p1, label.toStdString()); +// update(); +// +// objectFillOuts[OPERATE_BREP_PLANE][0].def.str = label; +// objectFillOuts[OPERATE_BREP_PLANE][1].def.point = p0; +// objectFillOuts[OPERATE_BREP_PLANE][2].def.point = p1; +//} void RenderViewer::slotCreatCubeBRep() { - dialog->addElements(objectFillOuts[OPERATE_BREP_CUBE]); - dialog->setWindowTitle(tr("Create a cube BRep")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatCubeBRepAccepted); - dialog->open(); -} -void RenderViewer::slotCreatCubeBRepAccepted(const ParamDialog::Response& data) { - QString label(data[0].second.str); - AMCAX::Coord3 c(data[1].second.point); - AMCAX::Coord3 s(data[2].second.point); - coreCommand_->createCubeBRepObject(c, s, label.toStdString()); - //record.recordCube(getLastPersistentId(), c, s, label.toStdString()); - update(); - - objectFillOuts[OPERATE_BREP_CUBE][0].def.str = label; - objectFillOuts[OPERATE_BREP_CUBE][1].def.point = c; - objectFillOuts[OPERATE_BREP_CUBE][2].def.point = s; + //dialog->addElements(objectFillOuts[OPERATE_BREP_CUBE]); + //dialog->setWindowTitle(tr("Create a cube BRep")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatCubeBRepAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("立方体"), + { + {tr("位置与形状")}, + {tr("起始点"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("对角点"),AMCAX::Coord3(1.0,1.0,1.0)}, + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createCubeBRepObject(data[1].value.c3, data[2].value.c3); + } } +//void RenderViewer::slotCreatCubeBRepAccepted(const ParamDialog::Response& data) { +// QString label(data[0].second.str); +// AMCAX::Coord3 c(data[1].second.point); +// AMCAX::Coord3 s(data[2].second.point); +// coreCommand_->createCubeBRepObject(c, s, label.toStdString()); +// //record.recordCube(getLastPersistentId(), c, s, label.toStdString()); +// update(); +// +// objectFillOuts[OPERATE_BREP_CUBE][0].def.str = label; +// objectFillOuts[OPERATE_BREP_CUBE][1].def.point = c; +// objectFillOuts[OPERATE_BREP_CUBE][2].def.point = s; +//} void RenderViewer::slotCreateSphereBRep() { - dialog->addElements(objectFillOuts[OPERATE_BREP_SPHERE]); - dialog->setWindowTitle(tr("Create a Sphere BRep")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateSphereBRepAccepted); - dialog->open(); -} -void RenderViewer::slotCreateSphereBRepAccepted(const ParamDialog::Response& data) { - QString label(data[0].second.str); - AMCAX::Coord3 center = data[1].second.point; - double radius = data[2].second.d; - double angle1 = data[3].second.d; - double angle2 = data[4].second.d; - double angle3 = data[5].second.d; - - coreCommand_->createSphereBRepObject(center, radius, angle1 * M_PI, angle2 * M_PI, - angle3 * M_PI, - label.toStdString()); - //record.recordSphere(getLastPersistentId(), center, radius, angle1, angle2, - // angle3, - // label.toStdString()); - update(); - - objectFillOuts[OPERATE_BREP_SPHERE][0].def.str = label; - objectFillOuts[OPERATE_BREP_SPHERE][1].def.point = center; - objectFillOuts[OPERATE_BREP_SPHERE][2].def.d = radius; - objectFillOuts[OPERATE_BREP_SPHERE][3].def.d = angle1; - objectFillOuts[OPERATE_BREP_SPHERE][4].def.d = angle2; - objectFillOuts[OPERATE_BREP_SPHERE][5].def.d = angle3; + //dialog->addElements(objectFillOuts[OPERATE_BREP_SPHERE]); + //dialog->setWindowTitle(tr("Create a Sphere BRep")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateSphereBRepAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("球体"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("形状")}, + {tr("半径"),1.0,""}, + {tr("弧度"),2.0,tr("π")}, + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createSphereBRepObject(data[1].value.c3, data[3].value.d, data[4].value.d * M_PI); + } } +//void RenderViewer::slotCreateSphereBRepAccepted(const ParamDialog::Response& data) { +// QString label(data[0].second.str); +// AMCAX::Coord3 center = data[1].second.point; +// double radius = data[2].second.d; +// double angle1 = data[3].second.d; +// double angle2 = data[4].second.d; +// double angle3 = data[5].second.d; +// +// coreCommand_->createSphereBRepObject(center, radius, angle1 * M_PI, label.toStdString()); +// //record.recordSphere(getLastPersistentId(), center, radius, angle1, angle2, +// // angle3, +// // label.toStdString()); +// update(); +// +// objectFillOuts[OPERATE_BREP_SPHERE][0].def.str = label; +// objectFillOuts[OPERATE_BREP_SPHERE][1].def.point = center; +// objectFillOuts[OPERATE_BREP_SPHERE][2].def.d = radius; +// objectFillOuts[OPERATE_BREP_SPHERE][3].def.d = angle1; +// objectFillOuts[OPERATE_BREP_SPHERE][4].def.d = angle2; +// objectFillOuts[OPERATE_BREP_SPHERE][5].def.d = angle3; +//} void RenderViewer::slotCreateCylinderBRep() { - dialog->addElements(objectFillOuts[OPERATE_BREP_CYLINDER]); - dialog->setWindowTitle(tr("Create a Cylinder BRep")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateCylinderBRepAccepted); - dialog->open(); -} -void RenderViewer::slotCreateCylinderBRepAccepted(const ParamDialog::Response& data) { - QString label(data[0].second.str); - AMCAX::Coord3 b_center = data[1].second.point; - AMCAX::Coord3 axis = data[2].second.vec; - AMCAX::Coord3 xaxis = data[3].second.vec; - double radius = data[4].second.d; - double height = data[5].second.d; - double angle = data[6].second.d; - - coreCommand_->createCylinderBRepObject(b_center, axis, xaxis, radius, height, angle * M_PI, - label.toStdString()); - //record.recordCylinder(getLastPersistentId(), b_center, axis, xaxis, radius, - // height, angle, - // label.toStdString()); - update(); - - objectFillOuts[OPERATE_BREP_CYLINDER][0].def.str = label; - objectFillOuts[OPERATE_BREP_CYLINDER][1].def.point = b_center; - objectFillOuts[OPERATE_BREP_CYLINDER][2].def.vec = axis; - objectFillOuts[OPERATE_BREP_CYLINDER][3].def.vec = xaxis; - objectFillOuts[OPERATE_BREP_CYLINDER][4].def.d = radius; - objectFillOuts[OPERATE_BREP_CYLINDER][5].def.d = height; - objectFillOuts[OPERATE_BREP_CYLINDER][6].def.d = angle; + //dialog->addElements(objectFillOuts[OPERATE_BREP_CYLINDER]); + //dialog->setWindowTitle(tr("Create a Cylinder BRep")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateCylinderBRepAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("圆柱"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("轴向"),AMCAX::Coord3(0.0,0.0,1.0)}, + {tr("X轴向"),AMCAX::Coord3(1.0,0.0,0.0)}, + {tr("形状")}, + {tr("半径"),1.0,""}, + {tr("高度"),1.0,""}, + {tr("弧度"),2.0,tr("π")} + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createCylinderBRepObject(data[1].value.c3, data[2].value.c3, data[3].value.c3, + data[5].value.d, data[6].value.d, data[7].value.d * M_PI); + } } +//void RenderViewer::slotCreateCylinderBRepAccepted(const ParamDialog::Response& data) { +// QString label(data[0].second.str); +// AMCAX::Coord3 b_center = data[1].second.point; +// AMCAX::Coord3 axis = data[2].second.vec; +// AMCAX::Coord3 xaxis = data[3].second.vec; +// double radius = data[4].second.d; +// double height = data[5].second.d; +// double angle = data[6].second.d; +// +// coreCommand_->createCylinderBRepObject(b_center, axis, xaxis, radius, height, angle * M_PI, +// label.toStdString()); +// //record.recordCylinder(getLastPersistentId(), b_center, axis, xaxis, radius, +// // height, angle, +// // label.toStdString()); +// update(); +// +// objectFillOuts[OPERATE_BREP_CYLINDER][0].def.str = label; +// objectFillOuts[OPERATE_BREP_CYLINDER][1].def.point = b_center; +// objectFillOuts[OPERATE_BREP_CYLINDER][2].def.vec = axis; +// objectFillOuts[OPERATE_BREP_CYLINDER][3].def.vec = xaxis; +// objectFillOuts[OPERATE_BREP_CYLINDER][4].def.d = radius; +// objectFillOuts[OPERATE_BREP_CYLINDER][5].def.d = height; +// objectFillOuts[OPERATE_BREP_CYLINDER][6].def.d = angle; +//} void RenderViewer::slotCreateConeBrep() { - dialog->addElements(objectFillOuts[OPERATE_BREP_CONE]); - dialog->setWindowTitle(tr("Create a Cone BRep")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateConeBrepAccepted); - dialog->open(); -} -void RenderViewer::slotCreateConeBrepAccepted(const ParamDialog::Response& data) { - QString label(data[0].second.str); - AMCAX::Coord3 b_center = data[1].second.point; - AMCAX::Coord3 axis = data[2].second.vec; - AMCAX::Coord3 xaxis = data[3].second.vec; - double radiusR = data[4].second.d; - double radiusr = data[5].second.d; - double height = data[6].second.d; - double angle = data[7].second.d; - - coreCommand_->createConeBRepObject(b_center, axis, xaxis, radiusR, radiusr, height, angle * M_PI, - label.toStdString()); - //record.recordCone(getLastPersistentId(), b_center, axis, xaxis, radiusR, radiusr, - // height, angle, - // label.toStdString()); - update(); - - objectFillOuts[OPERATE_BREP_CONE][0].def.str = label; - objectFillOuts[OPERATE_BREP_CONE][1].def.point = b_center; - objectFillOuts[OPERATE_BREP_CONE][2].def.vec = axis; - objectFillOuts[OPERATE_BREP_CONE][3].def.vec = xaxis; - objectFillOuts[OPERATE_BREP_CONE][4].def.d = radiusR; - objectFillOuts[OPERATE_BREP_CONE][5].def.d = radiusr; - objectFillOuts[OPERATE_BREP_CONE][6].def.d = height; - objectFillOuts[OPERATE_BREP_CONE][7].def.d = angle; + //dialog->addElements(objectFillOuts[OPERATE_BREP_CONE]); + //dialog->setWindowTitle(tr("Create a Cone BRep")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateConeBrepAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("圆锥"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("轴向"),AMCAX::Coord3(0.0,0.0,1.0)}, + {tr("X轴向"),AMCAX::Coord3(1.0,0.0,0.0)}, + {tr("形状")}, + {tr("底部半径"),1.0,""}, + {tr("顶部半径"),0.0,""}, + {tr("高度"),1.0,""}, + {tr("弧度"),2.0,tr("π")} + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createConeBRepObject(data[1].value.c3, data[2].value.c3, data[3].value.c3, + data[5].value.d, data[6].value.d, data[7].value.d, data[8].value.d * M_PI); + } + } +//void RenderViewer::slotCreateConeBrepAccepted(const ParamDialog::Response& data) { +// QString label(data[0].second.str); +// AMCAX::Coord3 b_center = data[1].second.point; +// AMCAX::Coord3 axis = data[2].second.vec; +// AMCAX::Coord3 xaxis = data[3].second.vec; +// double radiusR = data[4].second.d; +// double radiusr = data[5].second.d; +// double height = data[6].second.d; +// double angle = data[7].second.d; +// +// coreCommand_->createConeBRepObject(b_center, axis, xaxis, radiusR, radiusr, height, angle * M_PI, +// label.toStdString()); +// //record.recordCone(getLastPersistentId(), b_center, axis, xaxis, radiusR, radiusr, +// // height, angle, +// // label.toStdString()); +// update(); +// +// objectFillOuts[OPERATE_BREP_CONE][0].def.str = label; +// objectFillOuts[OPERATE_BREP_CONE][1].def.point = b_center; +// objectFillOuts[OPERATE_BREP_CONE][2].def.vec = axis; +// objectFillOuts[OPERATE_BREP_CONE][3].def.vec = xaxis; +// objectFillOuts[OPERATE_BREP_CONE][4].def.d = radiusR; +// objectFillOuts[OPERATE_BREP_CONE][5].def.d = radiusr; +// objectFillOuts[OPERATE_BREP_CONE][6].def.d = height; +// objectFillOuts[OPERATE_BREP_CONE][7].def.d = angle; +//} void RenderViewer::slotCreateTorusBRep() { - dialog->addElements(objectFillOuts[OPERATE_BREP_TORUS]); - dialog->setWindowTitle(tr("Create a Torus BRep")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateTorusBRepAccepted); - dialog->open(); -} -void RenderViewer::slotCreateTorusBRepAccepted(const ParamDialog::Response& data) { - QString label(data[0].second.str); - AMCAX::Coord3 center = data[1].second.point; - AMCAX::Coord3 axis = data[2].second.vec; - AMCAX::Coord3 xaxis = data[3].second.vec; - double radius0 = data[4].second.d; - double radius1 = data[5].second.d; - double angle = data[6].second.d; - - coreCommand_->createTorusBRepObject(center, axis, xaxis, radius0, radius1, angle * M_PI, - label.toStdString()); - - //record.recordTorus(getLastPersistentId(), center, axis, xaxis, radius0, - // radius1, angle, - // label.toStdString()); - update(); - - objectFillOuts[OPERATE_BREP_TORUS][0].def.str = label; - objectFillOuts[OPERATE_BREP_TORUS][1].def.point = center; - objectFillOuts[OPERATE_BREP_TORUS][2].def.vec = axis; - objectFillOuts[OPERATE_BREP_TORUS][3].def.vec = xaxis; - objectFillOuts[OPERATE_BREP_TORUS][4].def.d = radius0; - objectFillOuts[OPERATE_BREP_TORUS][5].def.d = radius1; - objectFillOuts[OPERATE_BREP_TORUS][6].def.d = angle; + //dialog->addElements(objectFillOuts[OPERATE_BREP_TORUS]); + //dialog->setWindowTitle(tr("Create a Torus BRep")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreateTorusBRepAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("圆环"), + { + {tr("位置")}, + {tr("中心"),AMCAX::Coord3(0.0,0.0,0.0)}, + {tr("轴向"),AMCAX::Coord3(0.0,0.0,1.0)}, + {tr("X轴向"),AMCAX::Coord3(1.0,0.0,0.0)}, + {tr("形状")}, + {tr("主环半径"),2.0,""}, + {tr("管道半径"),1.0,""}, + {tr("弧度"),2.0,tr("π")} + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createTorusBRepObject(data[1].value.c3, data[2].value.c3, data[3].value.c3, + data[5].value.d, data[6].value.d, data[7].value.d * M_PI); + } } +//void RenderViewer::slotCreateTorusBRepAccepted(const ParamDialog::Response& data) { +// QString label(data[0].second.str); +// AMCAX::Coord3 center = data[1].second.point; +// AMCAX::Coord3 axis = data[2].second.vec; +// AMCAX::Coord3 xaxis = data[3].second.vec; +// double radius0 = data[4].second.d; +// double radius1 = data[5].second.d; +// double angle = data[6].second.d; +// +// coreCommand_->createTorusBRepObject(center, axis, xaxis, radius0, radius1, angle * M_PI, +// label.toStdString()); +// +// //record.recordTorus(getLastPersistentId(), center, axis, xaxis, radius0, +// // radius1, angle, +// // label.toStdString()); +// update(); +// +// objectFillOuts[OPERATE_BREP_TORUS][0].def.str = label; +// objectFillOuts[OPERATE_BREP_TORUS][1].def.point = center; +// objectFillOuts[OPERATE_BREP_TORUS][2].def.vec = axis; +// objectFillOuts[OPERATE_BREP_TORUS][3].def.vec = xaxis; +// objectFillOuts[OPERATE_BREP_TORUS][4].def.d = radius0; +// objectFillOuts[OPERATE_BREP_TORUS][5].def.d = radius1; +// objectFillOuts[OPERATE_BREP_TORUS][6].def.d = angle; +//} void RenderViewer::slotCreatePrismBRep() { - dialog->addElements(objectFillOuts[OPERATE_BREP_PRISM]); + /*dialog->addElements(objectFillOuts[OPERATE_BREP_PRISM]); dialog->setWindowTitle(tr("Create a Prism BRep")); connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatePrismBRepAccepted); - dialog->open(); -} -void RenderViewer::slotCreatePrismBRepAccepted(const ParamDialog::Response& data) { - QString label(data[0].second.str); - std::vector pl = data[1].second.pointarr; - AMCAX::Coord3d extaxis = AMCAX::Coord3d(data[2].second.vec.X(), data[2].second.vec.Y(), data[2].second.vec.Z()); - - coreCommand_->createPrismBRepObject(pl, extaxis, label.toStdString()); - //record.recordPrism(getLastPersistentId(), pl, extaxis, label.toStdString()); - update(); - - objectFillOuts[OPERATE_BREP_PRISM][0].def.str = label; - objectFillOuts[OPERATE_BREP_PRISM][1].def.point = pl[0]; - objectFillOuts[OPERATE_BREP_PRISM][2].def.vec = extaxis; + dialog->open();*/ + DialogAuto dialog(this); + + dialog.SetData(tr("棱柱"), + { + {tr("顶点位置"), + {AMCAX::Coord3(0.0,0.0,0.0),AMCAX::Coord3(1.0,0.0,0.0),AMCAX::Coord3(1.0,1.0,0.0),AMCAX::Coord3(0.0,1.0,0.0)}, + tr("点%1")}, + {tr("形状")}, + {tr("拉升轴"),AMCAX::Coord3(0.0,0.0,2.0)}, + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); + + coreCommand_->createPrismBRepObject(data[0].value.c3s, data[2].value.c3); + } + } +//void RenderViewer::slotCreatePrismBRepAccepted(const ParamDialog::Response& data) { +// QString label(data[0].second.str); +// std::vector pl = data[1].second.pointarr; +// AMCAX::Coord3d extaxis = AMCAX::Coord3d(data[2].second.vec.X(), data[2].second.vec.Y(), data[2].second.vec.Z()); +// +// coreCommand_->createPrismBRepObject(pl, extaxis, label.toStdString()); +// //record.recordPrism(getLastPersistentId(), pl, extaxis, label.toStdString()); +// update(); +// +// objectFillOuts[OPERATE_BREP_PRISM][0].def.str = label; +// objectFillOuts[OPERATE_BREP_PRISM][1].def.point = pl[0]; +// objectFillOuts[OPERATE_BREP_PRISM][2].def.vec = extaxis; +//} void RenderViewer::slotBRepExtrude() { - dialog->addElements(objectFillOuts[OPERATE_BREP_EXTRUDE]); - dialog->setWindowTitle(tr("Set The Extrude Dir and Length")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotBRepExtrudeAccepted); - dialog->open(); -} -void RenderViewer::slotBRepExtrudeAccepted(const ParamDialog::Response& data) -{ - AMCAX::Coord3d vec = data[0].second.vec; - setOperationVector(vec); + //dialog->addElements(objectFillOuts[OPERATE_BREP_EXTRUDE]); + //dialog->setWindowTitle(tr("Set The Extrude Dir and Length")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotBRepExtrudeAccepted); + //dialog->open(); + std::cout << "RenderViewer::slotBRepExtrude" << std::endl; + DialogAuto dialog(this); + dialog.SetData(tr("挤出面"), + { + {tr("挤出的方向和长度")}, + {tr("向量"),AMCAX::Coord3(0.0,0.0,1.0)}, + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; + + if (dialog.exec()) + { + auto& data = dialog.GetData(); - BRepObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, - MeshOperationType::MeshExtrudeFace); + setOperationVector(data[1].value.c3); - objectFillOuts[OPERATE_BREP_EXTRUDE][0].def.vec = vec; + BRepObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, + MeshOperationType::MeshExtrudeFace); + + } } +//void RenderViewer::slotBRepExtrudeAccepted(const ParamDialog::Response& data) +//{ +// //AMCAX::Coord3d vec = data[0].second.vec; +// //setOperationVector(vec); +// +// //BRepObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, +// // MeshOperationType::MeshExtrudeFace); +// +// //objectFillOuts[OPERATE_BREP_EXTRUDE][0].def.vec = vec; +//} void RenderViewer::slotCreatePolygonBRep() { - dialog->addElements(objectFillOuts[OPERATE_BREP_POLYGON]); - dialog->setWindowTitle(tr("Create a Polygon BRep")); - connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatePolygonBRepAccepted); - dialog->open(); -} -void RenderViewer::slotCreatePolygonBRepAccepted(const ParamDialog::Response& data) -{ - QString label(data[0].second.str); - std::vector pl = data[1].second.pointarr; + //dialog->addElements(objectFillOuts[OPERATE_BREP_POLYGON]); + //dialog->setWindowTitle(tr("Create a Polygon BRep")); + //connect(dialog, &ParamDialog::dataReady, this, &RenderViewer::slotCreatePolygonBRepAccepted); + //dialog->open(); + DialogAuto dialog(this); + + dialog.SetData(tr("多边形"), + { + {tr("顶点列表"), + {AMCAX::Coord3(0.0,0.0,0.0),AMCAX::Coord3(1.0,0.0,0.0),AMCAX::Coord3(1.0,1.0,0.0)}, + tr("点%1")}, + + }); + + ///std::cout << "RenderViewer::slotCreatePlaneTSpline " << result << std::endl; - coreCommand_->createPolygonBRepObject(pl, label.toStdString()); - //record.recordPolygon(getLastPersistentId(), pl, label.toStdString()); - update(); + if (dialog.exec()) + { + auto& data = dialog.GetData(); - objectFillOuts[OPERATE_BREP_POLYGON][0].def.str = label; - objectFillOuts[OPERATE_BREP_POLYGON][1].def.point = pl[0]; + coreCommand_->createPolygonBRepObject(data[0].value.c3s); + } } +//void RenderViewer::slotCreatePolygonBRepAccepted(const ParamDialog::Response& data) +//{ +// QString label(data[0].second.str); +// std::vector pl = data[1].second.pointarr; +// +// coreCommand_->createPolygonBRepObject(pl, label.toStdString()); +// //record.recordPolygon(getLastPersistentId(), pl, label.toStdString()); +// update(); +// +// objectFillOuts[OPERATE_BREP_POLYGON][0].def.str = label; +// objectFillOuts[OPERATE_BREP_POLYGON][1].def.point = pl[0]; +//} diff --git a/Src/Windows/RibbonViewer.cpp b/Src/Windows/RibbonViewer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bcde8ae5a327d0419113b988dcac6744da72e769 --- /dev/null +++ b/Src/Windows/RibbonViewer.cpp @@ -0,0 +1,1078 @@ +#include "RibbonViewer.h" +//#if !SARIBBON_USE_3RDPARTY_FRAMELESSHELPER +#include "SAFramelessHelper.h" +//#endif +#include "SARibbonApplicationButton.h" +#include "SARibbonBar.h" +#include "SARibbonButtonGroupWidget.h" +#include "SARibbonCategory.h" +#include "SARibbonCheckBox.h" +#include "SARibbonColorToolButton.h" +#include "SARibbonComboBox.h" +#include "SARibbonCustomizeDialog.h" +#include "SARibbonCustomizeWidget.h" +#include "SARibbonGallery.h" +#include "SARibbonLineEdit.h" +#include "SARibbonMenu.h" +#include "SARibbonPannel.h" +#include "SARibbonQuickAccessBar.h" +#include "SARibbonToolButton.h" +#include "colorWidgets/SAColorGridWidget.h" +#include "colorWidgets/SAColorPaletteGridWidget.h" +#include "SARibbonSystemButtonBar.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "RenderViewer.h" +#include "../Core/DataManager.h" +#include "../Core/ACAMCore.h" +#include +#include +#include +#include + +using namespace acamcad; + +namespace +{ + + +#define PRINT_COST_START() \ + QElapsedTimer __TMP_COST; \ + __TMP_COST.start(); \ + int __TMP_LASTTIMES = 0 + +#define PRINT_COST(STR) \ + do { \ + int ___TMP_INT = __TMP_COST.elapsed(); \ + qDebug() << STR << " cost " << ___TMP_INT - __TMP_LASTTIMES << " ms (" << ___TMP_INT << ")"; \ + mTextedit->append(QString("%1 cost %2 ms(%3)").arg(STR).arg(___TMP_INT - __TMP_LASTTIMES).arg(___TMP_INT)); \ + __TMP_LASTTIMES = ___TMP_INT; \ + } while (0) + +} + +RibbonViewer::RibbonViewer(QWidget* ptr) : SARibbonMainWindow(ptr) +{ + + //Window + setMinimumSize(QSize(1280, 850)); + setWindowIcon(QIcon(":images/images/logo.ico")); + setStatusBar(new QStatusBar); + ribbonBar()->setContentsMargins(0, 0, 0, 0); + //setWindowTitle("九韶精灵"); + refresh_title(QString()); + + + initData(); + initRender(); + + initQuickBar(); + initFile(); + initTMSpline(); + initSubD(); + initBRrep(); + initAnalysis(); + initHelper(); + initToolBar(); + + refreshSize(); + ///show(); +} + +RibbonViewer::~RibbonViewer() +{ + acamcad::LocalDataManager::DataModelPosition data; + + data.array = saveGeometry(); + + dataManager_->execStore(data); +} + +void RibbonViewer::refresh_view(acamcad::DrawModel model) +{ + switch (model) + { + case acamcad::DrawModel::WIRE_MODE: + button_view_->setIcon(QIcon(":/icon/icon/wireframe.svg")); + button_view_->setToolTip(tr("线框")); + break; + case acamcad::DrawModel::SHADING_MODEL: + button_view_->setIcon(QIcon(":/icon/icon/smooth.svg")); + button_view_->setToolTip(tr("渲染")); + break; + case acamcad::DrawModel::RENDERING_MODEL: + button_view_->setIcon(QIcon(":/icon/icon/render.svg")); + button_view_->setToolTip(tr("平滑")); + break; + default: + break; + } +} +void RibbonViewer::refresh_select(acamcad::SelectModel model) +{ + switch (model) + { + case acamcad::SelectModel::VERTEX_MODEL: + button_select_->setIcon(QIcon(":/icon/icon/vertex-select.svg")); + button_select_->setToolTip(tr("点选择")); + break; + case acamcad::SelectModel::EDGE_MODEL: + button_select_->setIcon(QIcon(":/icon/icon/line-select.svg")); + button_select_->setToolTip(tr("线选择")); + break; + case acamcad::SelectModel::FACE_MODEL: + button_select_->setIcon(QIcon(":/icon/icon/face-select.svg")); + button_select_->setToolTip(tr("面选择")); + break; + case acamcad::SelectModel::OBJECT_MODEL: + button_select_->setIcon(QIcon(":/icon/icon/object-select.svg")); + button_select_->setToolTip(tr("体选择")); + break; + default: + break; + } +} + +void RibbonViewer::refresh_title(QString title) +{ + if (title.isEmpty()) + setWindowTitle(tr("九韶精灵")); + else { + QFileInfo info(title); + setWindowTitle(QString("%1 - 九韶精灵").arg(info.fileName())); + } +} + +void RibbonViewer::refresh_file(QString title) +{ + qint64 time = QDateTime::currentDateTime().toMSecsSinceEpoch(); + std::vector vec; + + vec.push_back({ title,time }); + + dataManager_->execStore(vec); + + refreshFileRecently(); +} + +QAction* RibbonViewer::createAction(const QString& text, const QString& iconurl, const QString& objName) +{ + QAction* act = new QAction(this); + act->setText(text); + act->setIcon(QIcon(iconurl)); + act->setObjectName(objName); + return act; +} + +QAction* RibbonViewer::createAction(const QString& text, const QString& iconurl) +{ + QAction* act = new QAction(this); + act->setText(text); + act->setIcon(QIcon(iconurl)); + act->setObjectName(text); + + return act; +} + +void RibbonViewer::initQuickBar() +{ + SARibbonQuickAccessBar* quickAccessBar = ribbonBar()->quickAccessBar(); + ribbonBar()->showMinimumModeButton(true); + //ribbonBar()->activeRightButtonGroup(); + + QAction* p = createAction(tr("保存"), ":/icon/icon/save-small.svg", "save-quickbar"); + connect(p, &QAction::triggered, core_.get(), QOverload<>::of(&AMCore::saveObjects)); + quickAccessBar->addAction(p); + quickAccessBar->addSeparator(); + + action_undo_ = createAction(tr("撤销"), ":/icon/icon/undo.svg"); + action_undo_->setShortcut(QKeySequence("Ctrl+z")); + action_undo_->setShortcutContext(Qt::ApplicationShortcut); + connect(action_undo_, &QAction::triggered, render_viewer_, &RenderViewer::slotUndo); + quickAccessBar->addAction(action_undo_); + + action_redo_ = createAction(tr("重做"), ":/icon/icon/redo.svg"); + action_redo_->setShortcut(QKeySequence("Ctrl+y")); + action_redo_->setShortcutContext(Qt::ApplicationShortcut); + connect(action_redo_, &QAction::triggered, render_viewer_, &RenderViewer::slotRedo); + quickAccessBar->addAction(action_redo_); + + refreshQuickBar(); + +} + +void RibbonViewer::initData() +{ + //render_viewer_ = new acamcad::RenderViewer(this); + dataManager_ = std::make_unique(); + dataManager_->setFunc(std::bind(&RibbonViewer::refreshQuickBar, this)); + + core_ = std::make_unique(); + core_->setDataManager(dataManager_.get()); + core_->setListener(this); + +} + +void RibbonViewer::initRender() +{ + render_viewer_ = new acamcad::RenderViewer(this); + render_viewer_->setListen(this); + render_viewer_->setDataManger(dataManager_.get()); + render_viewer_->setCore(core_.get()); + + + setCentralWidget(render_viewer_); +} + +void RibbonViewer::refreshQuickBar() +{ + action_undo_->setEnabled(dataManager_->canUndo()); + action_redo_->setEnabled(dataManager_->canRedo()); +} + +void RibbonViewer::initFile() +{ + SARibbonBar* ribbon = ribbonBar(); + + auto btn = ribbon->applicationButton(); + SARibbonApplicationButton* appBtn = qobject_cast(btn); + + appBtn->setText(" 文件 "); + auto mMenuApplicationBtn = new SARibbonMenu(this); + + QAction* p = createAction("新建", ":/icon/icon/new.svg"); + connect(p, &QAction::triggered, this, [this](bool) { + if (core_->newObjects()) + { + dataManager_->clearSelected(); + dataManager_->clearHistory(); + dataManager_->deleteAllObject(); + } + }); + mMenuApplicationBtn->addAction(p); + + p = createAction("打开", ":/icon/icon/open.svg"); + connect(p, &QAction::triggered, core_.get(), &AMCore::loadObjects); + mMenuApplicationBtn->addAction(p); + + p = action_file_recently_ = createAction("最近打开文件", ":/icon/icon/open-recent.svg"); + //connect(p, &QAction::triggered, core_.get(), &AMCore::loadObjects); + mMenuApplicationBtn->addAction(p); + + + QMenu* pM = menu_file_recently_ = new QMenu(this); + p->setMenu(pM); + + mMenuApplicationBtn->addSeparator(); + + p = createAction("保存", ":/icon/icon/save.svg"); + connect(p, &QAction::triggered, core_.get(), QOverload<>::of(&AMCore::saveObjects)); + mMenuApplicationBtn->addAction(p); + + p = createAction("另存为", ":/icon/icon/save-as.svg"); + connect(p, &QAction::triggered, core_.get(), QOverload<>::of(&AMCore::saveAsObjects)); + mMenuApplicationBtn->addAction(p); + + p = createAction("保存选中", ":/icon/icon/export-selected.svg"); + connect(p, &QAction::triggered, core_.get(), &AMCore::saveSelectedObjects); + mMenuApplicationBtn->addAction(p); + + + mMenuApplicationBtn->addSeparator(); + p = createAction("退出", ":/icon/icon/exit.svg"); + connect(p, &QAction::triggered, this, &RibbonViewer::close); + mMenuApplicationBtn->addAction(p); + appBtn->setMenu(mMenuApplicationBtn); + + mMenuApplicationBtn->setMinimumWidth(150); + //ribbon->setApplicationButton(btn); + + refreshFileRecently(); +} + +void RibbonViewer::refreshFileRecently() +{ + std::vector data; + + dataManager_->execGet(data); + + menu_file_recently_->clear(); + + for (int i = 0; i < data.size(); i++) + { + auto& iter = data[i]; + QAction* p = new QAction(this); + p->setText(QString("%1 | %2").arg(i + 1).arg(iter.file)); + connect(p, &QAction::triggered, this, [this, iter](bool) { + core_->loadObjectsFromFile(iter.file); + }); + menu_file_recently_->addAction(p); + } + + action_file_recently_->setEnabled(!data.empty()); + +} + +void RibbonViewer::initTMSpline() +{ + SARibbonCategory* category = new SARibbonCategory(this); + category->setCategoryName(" TMSpline "); + category->setObjectName("categoryTMSpline"); + ribbonBar()->addCategoryPage(category); + + SARibbonPannel* pannel1 = new SARibbonPannel(tr("创建"), this); + category->addPannel(pannel1); + + auto pannel1Button = pannel1->pannelLayout()->pannelTitleLabel(); + pannel1Button->setPopupMode(QToolButton::InstantPopup); + + auto pMenu = new SARibbonMenu(this); + pannel1Button->setMenu(pMenu); + pMenu->setMinimumWidth(250); + + QAction* action = createAction(tr("单一面"), ":/TMSpline/TMSpline/single-face.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateSingleFaceTSpline); + pannel1->addAction(action); + pMenu->addAction(action); + + + action = createAction(tr("平面"), ":/TMSpline/TMSpline/plane.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreatePlaneTSpline); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("正方体"), ":/TMSpline/TMSpline/cube.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateCubeTSpline); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("球体"), ":/TMSpline/TMSpline/sphere.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateUVSphereTSpline); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("四分球"), ":/TMSpline/TMSpline/quad-sphere.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateQuadSphereTSpline); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("圆柱体"), ":/TMSpline/TMSpline/cylinder.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateCylinderTSpline); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("圆锥"), ":/TMSpline/TMSpline/cone.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateConeTSpline); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("圆环"), ":/TMSpline/TMSpline/torus.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateTorusTSpline); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("扇形"), ":/TMSpline/TMSpline/circular-arc.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateCircularTSpline); + pannel1->addAction(action); + pMenu->addAction(action); + + SARibbonPannel* pannel2 = new SARibbonPannel("编辑", this); + category->addPannel(pannel2); + + auto pannel2Button = pannel2->pannelLayout()->pannelTitleLabel(); + pannel2Button->setPopupMode(QToolButton::InstantPopup); + + pMenu = new SARibbonMenu(this); + pannel2Button->setMenu(pMenu); + pMenu->setMinimumWidth(600); + + action = createAction(tr("锐化"), ":/TMSpline/TMSpline/add-crease-edge.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotAddCreaseEdge); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("取消锐化"), ":/TMSpline/TMSpline/remove-crease-edge.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotRemoveCreaseEdge); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("均匀分布"), ":/TMSpline/TMSpline/make-uniform.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotTsplineUniform); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("修复实体"), ":/TMSpline/TMSpline/repair.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotTsplineRepair); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("倒角"), ":/TMSpline/TMSpline/chamfer.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshChamferEdge); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("桥接边"), ":/TMSpline/TMSpline/brige-edge.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshBridgeEdge); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("垂直\n挤出边"), ":/TMSpline/TMSpline/extrude-edge-v.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperationSelect(SelectModel::EDGE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeEdgeVertical); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("水平\n挤出边"), ":/TMSpline/TMSpline/extrude-edge-h.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperationSelect(SelectModel::EDGE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeEdgeHorizontal); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("挤出面"), ":/TMSpline/TMSpline/extrude-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperationSelect(SelectModel::FACE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeFaceNew); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("翻转\n法向量"), ":/TMSpline/TMSpline/reverse-normal.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshReverseNormal); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("填充洞口"), ":/TMSpline/TMSpline/fill-hole.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshFillHole); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("加厚"), ":/TMSpline/TMSpline/thicken.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshThicken); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("删除面"), ":/TMSpline/TMSpline/delete-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::FACE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshDeleteElement); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("嵌入面"), ":/TMSpline/TMSpline/embed-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::VERTEX_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshEmbedFace); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("分离"), ":/TMSpline/TMSpline/separate.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshSeparate); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("缝合"), ":/TMSpline/TMSpline/weld.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshWeldEdge); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("循环分割"), ":/TMSpline/TMSpline/split-loop.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::PICK_OPERATION_MOVE, MeshOperationType::MeshSplitLoop); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("十字分割"), ":/TMSpline/TMSpline/split-face-cross.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::FACE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshSplitFace); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("分割面"), ":/TMSpline/TMSpline/split-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::PICK_OPERATION_MOVE_MULTI, MeshOperationType::MeshSplitFaceByEdge); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("切割面"), ":/TMSpline/TMSpline/cut-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::VERTEX_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshSplitFaceVertex); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("复制面"), ":/TMSpline/TMSpline/copy-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::FACE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshCopy); + }); + pannel2->addAction(action); + pMenu->addAction(action); + +} + +void RibbonViewer::BaseObjectOperation(const SelectModel& s_model, const OperationType& op_type, const MeshOperationType mesh_operate_type) +{ + render_viewer_->clearSelected(); + render_viewer_->setSelectModel(s_model); + render_viewer_->setOperateType(op_type); + + core_->setMeshOperationType(mesh_operate_type); + render_viewer_->selectCheckChanged(); + render_viewer_->update(); +} + +void RibbonViewer::BaseObjectOperationSelect(const SelectModel& s_model, const OperationType& op_type, const MeshOperationType mesh_operate_type) +{ + if (render_viewer_->getSelectModel() != s_model) + render_viewer_->clearSelected(); + + render_viewer_->setSelectModel(s_model); + render_viewer_->setOperateType(op_type); + + core_->setMeshOperationType(mesh_operate_type); + + render_viewer_->selectCheckChanged(); + render_viewer_->update(); +} + +void RibbonViewer::initToolBar() +{ + //return; + toolbar_ = new QToolBar(this); + + addToolBar(Qt::ToolBarArea_Mask, toolbar_); + + toolbar_->setStyleSheet("QToolBar{border: none; background-color: transparent;} "); + + //bar->move(100, 300); + toolbar_->setMovable(false); + toolbar_->setFixedSize(QSize(405, 40)); + //toolbar_->setFixedHeight(40); + toolbar_->setIconSize(QSize(28, 28)); + + //QPushButton* btn = new QPushButton("PushBuuton", this); + auto action = createAction(tr("移动"), ":/icon/icon/move.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + render_viewer_->setOperateType(OperationType::MOVE_OPERATION); + }); + toolbar_->addAction(action); + + action = createAction(tr("旋转"), ":/icon/icon/rotate.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + render_viewer_->setOperateType(OperationType::ROTATE_OPERATION); + }); + toolbar_->addAction(action); + + action = createAction(tr("缩放"), ":/icon/icon/scale.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + render_viewer_->setOperateType(OperationType::SCALE_OPERATION); + }); + toolbar_->addAction(action); + + action = createAction(tr("复制"), ":/icon/icon/copy-object.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCopyObject); + toolbar_->addAction(action); + + action = createAction(tr("镜像"), ":/icon/icon/mirror.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotMirrorObject); + toolbar_->addAction(action); + + action = createAction(tr("合并对象"), ":/icon/icon/create_component.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCombineObject); + toolbar_->addAction(action); + + action = createAction(tr("删除"), ":/icon/icon/delete.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotDeleteObject); + toolbar_->addAction(action); + + toolbar_->addSeparator(); + + { + QToolButton* pButton = button_view_ = new QToolButton(this); + pButton->setText(tr("显示模式")); + pButton->setIcon(QIcon(":/icon/icon/render.svg")); + pButton->setPopupMode(QToolButton::InstantPopup); + toolbar_->addWidget(pButton); + + auto pMenu = new SARibbonMenu(this); + + QAction* p = createAction("线框", ":/icon/icon/wireframe.svg"); + connect(p, &QAction::triggered, this, [this](bool) { + render_viewer_->slotRenderMode_Wire(); + //pButton->setIcon(QIcon(":/icon/icon/wireframe.svg")); + });; + pMenu->addAction(p); + + + p = createAction("渲染", ":/icon/icon/render.svg"); + connect(p, &QAction::triggered, this, [this](bool) { + render_viewer_->slotRenderMode_Rendering(); + //pButton->setIcon(QIcon(":/icon/icon/render.svg")); + });; + pMenu->addAction(p); + + p = createAction("平滑", ":/icon/icon/smooth.svg"); + connect(p, &QAction::triggered, this, [this](bool) { + render_viewer_->slotRenderMode_Shading(); + //pButton->setIcon(QIcon(":/icon/icon/smooth.svg")); + }); + pMenu->addAction(p); + + pButton->setMenu(pMenu); + + } + + { + QToolButton* pButton = button_select_ = new QToolButton(this); + pButton->setText(tr("选择模式")); + pButton->setIcon(QIcon(":/icon/icon/object-select.svg")); + pButton->setPopupMode(QToolButton::InstantPopup); + toolbar_->addWidget(pButton); + + auto pMenu = new SARibbonMenu(this); + pButton->setMenu(pMenu); + + QAction* p = createAction("点选择", ":/icon/icon/vertex-select.svg"); + connect(p, &QAction::triggered, this, [this](bool) { + render_viewer_->slotSelectMode_Vert(); + //pButton->setIcon(QIcon(":/icon/icon/vertex-select.svg")); + });; + pMenu->addAction(p); + + + p = createAction("线选择", ":/icon/icon/line-select.svg"); + connect(p, &QAction::triggered, this, [this](bool) { + render_viewer_->slotSelectMode_Edge(); + //pButton->setIcon(QIcon(":/icon/icon/line-select.svg")); + });; + pMenu->addAction(p); + + p = createAction("面选择", ":/icon/icon/face-select.svg"); + connect(p, &QAction::triggered, this, [this](bool) { + render_viewer_->slotSelectMode_Face(); + //pButton->setIcon(QIcon(":/icon/icon/face-select.svg")); + }); + pMenu->addAction(p); + + p = createAction("体选择", ":/icon/icon/object-select.svg"); + connect(p, &QAction::triggered, this, [this](bool) { + render_viewer_->slotSelectMode_Object(); + //pButton->setIcon(QIcon(":/icon/icon/object-select.svg")); + }); + pMenu->addAction(p); + + } + + toolbar_->addSeparator(); + + action = createAction(tr("适应窗口"), ":/icon/icon/fit-screen.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotSetScenesCenter); + toolbar_->addAction(action); + + action = createAction(tr("工作平面"), ":/icon/icon/workplane.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotSetWorkPlane); + toolbar_->addAction(action); + + + refreshToolBar(); + +} + +void RibbonViewer::refreshToolBar() +{ + //toolbar_->setGeometry + auto w = width() - toolbar_->width(); + toolbar_->move(w / 2, 180); + +} + +void RibbonViewer::resizeEvent(QResizeEvent* event) +{ + SARibbonMainWindow::resizeEvent(event); + refreshToolBar(); +} + +void RibbonViewer::initSubD() +{ + SARibbonCategory* category = new SARibbonCategory(this); + category->setCategoryName(" SubD "); + category->setObjectName("categorySubD"); + ribbonBar()->addCategoryPage(category); + + SARibbonPannel* pannel1 = new SARibbonPannel(tr("创建"), this); + category->addPannel(pannel1); + + auto pannel1Button = pannel1->pannelLayout()->pannelTitleLabel(); + pannel1Button->setPopupMode(QToolButton::InstantPopup); + + auto pMenu = new SARibbonMenu(this); + pannel1Button->setMenu(pMenu); + pMenu->setMinimumWidth(80); + + QAction* action = createAction(tr("单一面"), ":/TMSpline/TMSpline/single-face.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateSingleFaceMesh); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("平面"), ":/TMSpline/TMSpline/plane.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreatPlaneMesh); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("立方体"), ":/TMSpline/TMSpline/cube.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreatCubeMesh); + pannel1->addAction(action); + pMenu->addAction(action); + + SARibbonPannel* pannel2 = new SARibbonPannel("编辑", this); + category->addPannel(pannel2); + + auto pannel2Button = pannel2->pannelLayout()->pannelTitleLabel(); + pannel2Button->setPopupMode(QToolButton::InstantPopup); + + pMenu = new SARibbonMenu(this); + pannel2Button->setMenu(pMenu); + pMenu->setMinimumWidth(500); + + action = createAction(tr("细分方法\nCatmull Clark"), ":/TMSpline/TMSpline/subd-catmull-clark.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotMeshSubdiveCatmullClark); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("细分方法\nLoop"), ":/TMSpline/TMSpline/subd-loop.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotMeshSubdiveLoop); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("三角化\n网格"), ":/TMSpline/TMSpline/triangulated-mesh.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotMeshTriangulation); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("桥接边"), ":/TMSpline/TMSpline/brige-edge.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshBridgeEdge); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("垂直\n挤出边"), ":/TMSpline/TMSpline/extrude-edge-v.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperationSelect(SelectModel::EDGE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeEdgeVertical); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("水平\n挤出边"), ":/TMSpline/TMSpline/extrude-edge-h.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperationSelect(SelectModel::EDGE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeEdgeHorizontal); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("挤出面"), ":/TMSpline/TMSpline/extrude-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperationSelect(SelectModel::FACE_MODEL, OperationType::CTRL_OPERATION, MeshOperationType::MeshExtrudeFaceNew); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("翻转\n法向量"), ":/TMSpline/TMSpline/reverse-normal.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshReverseNormal); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + + action = createAction(tr("填充洞口"), ":/TMSpline/TMSpline/fill-hole.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshFillHole); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("加厚"), ":/TMSpline/TMSpline/thicken.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshThicken); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("删除面"), ":/TMSpline/TMSpline/delete-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::FACE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshDeleteElement); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("嵌入面"), ":/TMSpline/TMSpline/embed-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::VERTEX_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshEmbedFace); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("分离"), ":/TMSpline/TMSpline/separate.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshSeparate); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("缝合"), ":/TMSpline/TMSpline/weld.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshWeldEdge); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("循环分割"), ":/TMSpline/TMSpline/split-loop.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::PICK_OPERATION_MOVE, MeshOperationType::MeshSplitLoop); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("分割面"), ":/TMSpline/TMSpline/split-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::PICK_OPERATION_MOVE_MULTI, MeshOperationType::MeshSplitFaceByEdge); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("切割面"), ":/TMSpline/TMSpline/cut-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::VERTEX_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshSplitFaceVertex); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("复制面"), ":/TMSpline/TMSpline/copy-face.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::FACE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshCopy); + }); + pannel2->addAction(action); + pMenu->addAction(action); +} + +void RibbonViewer::initBRrep() +{ + SARibbonCategory* category = new SARibbonCategory(this); + category->setCategoryName(" BRep "); + category->setObjectName("categoryBRep"); + ribbonBar()->addCategoryPage(category); + + SARibbonPannel* pannel1 = new SARibbonPannel(tr("创建"), this); + category->addPannel(pannel1); + + auto pannel1Button = pannel1->pannelLayout()->pannelTitleLabel(); + pannel1Button->setPopupMode(QToolButton::InstantPopup); + + auto pMenu = new SARibbonMenu(this); + pannel1Button->setMenu(pMenu); + pMenu->setMinimumWidth(250); + + QAction* action = createAction(tr("单一面"), ":/TMSpline/TMSpline/single-face.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateSingleFaceBRep); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("平面"), ":/TMSpline/TMSpline/plane.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreatPlaneBRep); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("多边形"), ":/TMSpline/TMSpline/polygon.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreatePolygonBRep); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("立方体"), ":/TMSpline/TMSpline/cube.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreatCubeBRep); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("球体"), ":/TMSpline/TMSpline/sphere.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateSphereBRep); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("圆柱"), ":/TMSpline/TMSpline/cylinder.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateCylinderBRep); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("圆锥"), ":/TMSpline/TMSpline/cone.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateConeBrep); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("圆环"), ":/TMSpline/TMSpline/torus.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreateTorusBRep); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("棱柱"), ":/TMSpline/TMSpline/prism.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotCreatePrismBRep); + pannel1->addAction(action); + pMenu->addAction(action); + + SARibbonPannel* pannel2 = new SARibbonPannel("编辑", this); + category->addPannel(pannel2); + + auto pannel2Button = pannel2->pannelLayout()->pannelTitleLabel(); + pannel2Button->setPopupMode(QToolButton::InstantPopup); + + pMenu = new SARibbonMenu(this); + pannel2Button->setMenu(pMenu); + pMenu->setMinimumWidth(180); + + + action = createAction(tr("倒角"), ":/TMSpline/TMSpline/chamfer.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + this->BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshChamferEdge); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("圆角"), ":/TMSpline/TMSpline/fillet.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + BaseObjectOperation(SelectModel::EDGE_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshFilletEdge); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("合并"), ":/TMSpline/TMSpline/union.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshFuse); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("减去"), ":/TMSpline/TMSpline/union-1.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshCut); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("相交"), ":/TMSpline/TMSpline/union-2.svg"); + connect(action, &QAction::triggered, this, [this](bool) { + BaseObjectOperation(SelectModel::OBJECT_MODEL, OperationType::ENTER_OPERATION, MeshOperationType::MeshCommon); + }); + pannel2->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("挤出面"), ":/TMSpline/TMSpline/extrude-face.svg"); + connect(action, &QAction::triggered, render_viewer_, &RenderViewer::slotBRepExtrude); + pannel2->addAction(action); + pMenu->addAction(action); + +} + +void RibbonViewer::initAnalysis() +{ + SARibbonCategory* category = new SARibbonCategory(this); + category->setCategoryName(" 分析 "); + category->setObjectName("categoryAnalysis"); + ribbonBar()->addCategoryPage(category); + + SARibbonPannel* pannel1 = new SARibbonPannel(tr("分析"), this); + category->addPannel(pannel1); + //pannel1->setEnableShowTitle(false); + + auto pannel1Button = pannel1->pannelLayout()->pannelTitleLabel(); + pannel1Button->setPopupMode(QToolButton::InstantPopup); + + auto pMenu = new SARibbonMenu(this); + pannel1Button->setMenu(pMenu); + //pMenu->setMinimumWidth(120); + + QAction* action = createAction(tr("斑纹\n分析"), ":/TMSpline/TMSpline/zebra.svg"); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("曲率映射\n分析"), ":/TMSpline/TMSpline/curvature.svg"); + pannel1->addAction(action); + pMenu->addAction(action); + + action = createAction(tr("曲率梳\n分析"), ":/TMSpline/TMSpline/curvature-comb.svg"); + pannel1->addAction(action); + pMenu->addAction(action); +} + +void RibbonViewer::initHelper() +{ + SARibbonCategory* category = new SARibbonCategory(this); + category->setCategoryName(" 帮助 "); + category->setObjectName("categoryAnalysis"); + ribbonBar()->addCategoryPage(category); + + SARibbonPannel* pannel1 = new SARibbonPannel(tr("关于"), this); + category->addPannel(pannel1); + //pannel1->setEnableShowTitle(false); + + auto pannel1Button = pannel1->pannelLayout()->pannelTitleLabel(); + pannel1Button->setPopupMode(QToolButton::InstantPopup); + + auto pMenu = new SARibbonMenu(this); + pannel1Button->setMenu(pMenu); + //pMenu->setMinimumWidth(250); + + QAction* action = createAction(tr("帮助"), ":/icon/icon/about.svg"); + pannel1->addAction(action); + pMenu->addAction(action); +} + +void RibbonViewer::refreshSize() +{ + acamcad::LocalDataManager::DataModelPosition data; + + dataManager_->execGet(data); + + //QByteArray array = data.array; + show(); + restoreGeometry(data.array); + +} \ No newline at end of file diff --git a/Src/Windows/RibbonViewer.h b/Src/Windows/RibbonViewer.h new file mode 100644 index 0000000000000000000000000000000000000000..52a36a7616ea7219781d55dae1387629c7067a3a --- /dev/null +++ b/Src/Windows/RibbonViewer.h @@ -0,0 +1,94 @@ +#pragma once +#include "SARibbonMainWindow.h" +class SARibbonCategory; +class SARibbonContextCategory; +class SARibbonCustomizeWidget; +class SARibbonActionsManager; +class SARibbonQuickAccessBar; +class SARibbonButtonGroupWidget; +class SARibbonPannel; +class QTextEdit; +class QComboBox; +class QCloseEvent; +class QLineEdit; +#include + + +#include +#include "../Core/CoreDefine.h" +#include "../Core/ACAMCore.h" +#include "../Core/DataManager.h" +#include "RenderViewer.h" + + +namespace acamcad { + class RenderViewer; + class DataManager; + //class BackupManager; + class AMCore; + //class OperateToolBar; + //class UndoRedoHandler; +} + +class RibbonViewer :public SARibbonMainWindow, public RenderViewerListen, public AMCoreListener +{ + Q_OBJECT +public: + RibbonViewer(QWidget* ptr = nullptr); + ~RibbonViewer(); + + // RenderViewerListen + virtual void refresh_view(acamcad::DrawModel model) override; + virtual void refresh_select(acamcad::SelectModel model) override; + + //AMCoreListener + virtual void refresh_title(QString title); + virtual void refresh_file(QString title); + + +protected: + QAction* createAction(const QString& text, const QString& iconurl, const QString& objName); + QAction* createAction(const QString& text, const QString& iconurl); + + void initQuickBar(); + void initData(); + void initRender(); + void refreshQuickBar(); + void initFile(); + void initTMSpline(); + void initToolBar(); + void refreshToolBar(); + void initSubD(); + void initBRrep(); + void initAnalysis(); + void initHelper(); + + + void BaseObjectOperation(const acamcad::SelectModel& s_model, + const acamcad::OperationType& op_type, const acamcad::MeshOperationType mesh_operate_type); + void BaseObjectOperationSelect(const acamcad::SelectModel& s_model, + const acamcad::OperationType& op_type, const acamcad::MeshOperationType mesh_operate_type); + + //QMainWindow; + virtual void resizeEvent(QResizeEvent* event) override; + + void refreshFileRecently(); + void refreshSize(); + +private: + acamcad::RenderViewer* render_viewer_; + std::unique_ptr dataManager_; + std::unique_ptr core_; + + //UI + QAction* action_undo_; + QAction* action_redo_; + QToolBar* toolbar_; + + QToolButton* button_select_; + QToolButton* button_view_; + + QMenu* menu_file_recently_; + QAction* action_file_recently_; +}; + diff --git a/Src/Windows/SelectTool.cpp b/Src/Windows/SelectTool.cpp index f1b09515ddf79a56c786bf37c6743be68d4e0366..641e5ea863a3bda161e4d3674f5b0dcc776fe6e8 100644 --- a/Src/Windows/SelectTool.cpp +++ b/Src/Windows/SelectTool.cpp @@ -370,7 +370,8 @@ AMCAX::Coord3 acamcad::SelectTool::getCenter() void SelectTool::clearSelected() { - dataManager_->clearSelected(); + if(dataManager_) + dataManager_->clearSelected(); select_info_list_.clear(); select_infoCamera_list_.clear(); diff --git a/commit_log b/commit_log index b33c83f27fc388a4dcfd3c5b29d9b5964d00cc1c..6e88a1007ec5d3a121ce6329405facac634ae317 100644 --- a/commit_log +++ b/commit_log @@ -2,3 +2,5 @@ 6a09d79592f7e3b8f8178d892cdad2a6b989da05 b4ded3f37f1eee358b42567df08f23aa78d870dd 43557e4f3d53dbd3612788448250b3fc885a3cf0 +cc2d9937ced215e765bc13bc994d7deebc3046dd + diff --git a/doc/pic/screenshot/screenshot-file.png b/doc/pic/screenshot/screenshot-file.png new file mode 100644 index 0000000000000000000000000000000000000000..84ebfd9c811c0bef2115f4b541573911881a7fd9 Binary files /dev/null and b/doc/pic/screenshot/screenshot-file.png differ diff --git a/doc/pic/screenshot/screenshot.png b/doc/pic/screenshot/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c6ec3092f226c2541e1673cb50753f13ee003793 Binary files /dev/null and b/doc/pic/screenshot/screenshot.png differ diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbon.cpp b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbon.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aa966f7491bc2690e7a49cc667edef2c2c0ce449 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbon.cpp @@ -0,0 +1,15472 @@ +// 定义此宏,将SA_RIBBON_EXPORT定义为空 +#ifndef SA_RIBBON_BAR_NO_EXPORT +#define SA_RIBBON_BAR_NO_EXPORT +#endif +// 定义此宏,将SA_COLOR_WIDGETS_API定义为空 +#ifndef SA_COLOR_WIDGETS_NO_DLL +#define SA_COLOR_WIDGETS_NO_DLL +#endif + +/*** Start of inlined file: SARibbonAmalgamTemplateHeaderGlue.h ***/ +// This file provides an extra level of indirection for the @remap in the template +#include "SARibbon.h" + +/*** End of inlined file: SARibbonAmalgamTemplateHeaderGlue.h ***/ + +// disable warnings about unsafe standard library calls +#ifdef _MSC_VER +#pragma push_macro("_CRT_SECURE_NO_WARNINGS") +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#pragma warning(push) +#pragma warning(disable : 4996) // deprecated POSIX names +#endif + +/*** Start of inlined file: SAColorMenu.cpp ***/ +#include +#include +#include +#include +#include +#include + +class SAColorMenu::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorMenu) +public: + PrivateData(SAColorMenu* p); + QColor getColorByDialog(); + void recordCustomColor(const QColor& c); + QWidgetAction* addWidget(QWidget* w); + // 创建一个无颜色的icon + QIcon createNoneColorIcon(QSize baseSize = QSize(32, 32)); + +public: + QLabel* mTitleLabel { nullptr }; ///< 主题颜色标题 + QWidgetAction* mTitleLabelAction { nullptr }; + SAColorPaletteGridWidget* mThemeColorsWidget { nullptr }; ///< 主题颜色 + QWidgetAction* mThemeColorPaletteAction { nullptr }; ///< ThemeColorsPalette对应的action + QAction* mCustomColorAction { nullptr }; ///< 自定义颜色action + SAColorGridWidget* mCustomColorsWidget { nullptr }; ///< 自定义颜色记录 + QWidgetAction* mCustomColorsWidgetAction { nullptr }; ///< 自定义颜色窗口对应的action + QAction* mNoneColorAction { nullptr }; ///< 无颜色action + QList< QColor > mCustomColors; + int mMaxCustomColorSize { 10 }; ///< 记录最多的自定义颜色数量 + QScopedPointer< QColorDialog > mColorDlg { nullptr }; ///< 颜色对话框 +}; + +SAColorMenu::PrivateData::PrivateData(SAColorMenu* p) : q_ptr(p) +{ +} + +QColor SAColorMenu::PrivateData::getColorByDialog() +{ + if (nullptr == mColorDlg) { + mColorDlg.reset(new QColorDialog()); + } + if (QDialog::Accepted == mColorDlg->exec()) { + return mColorDlg->currentColor(); + } + return QColor(); +} + +void SAColorMenu::PrivateData::recordCustomColor(const QColor& c) +{ + if (mCustomColors.size() < mMaxCustomColorSize) { + mCustomColors.push_back(c); + } else { + // 超过数量,就左移动 + for (int i = 1; i < mCustomColors.size(); ++i) { + mCustomColors[ i - 1 ] = mCustomColors[ i ]; + } + mCustomColors.back() = c; + } +} + +QWidgetAction* SAColorMenu::PrivateData::addWidget(QWidget* w) +{ + QWidgetAction* wa = new QWidgetAction(q_ptr); + wa->setDefaultWidget(w); + q_ptr->addAction(wa); + return wa; +} + +QIcon SAColorMenu::PrivateData::createNoneColorIcon(QSize baseSize) +{ + QPixmap pixmap(baseSize); + pixmap.fill(Qt::transparent); + QPainter p(&pixmap); + SAColorToolButton::paintNoneColor(&p, QRect(0, 0, pixmap.width(), pixmap.height()).adjusted(1, 1, -1, -1)); + return QIcon(pixmap); +} +//=================================================== +// SAColorMenu +//=================================================== + +SAColorMenu::SAColorMenu(QWidget* parent) : QMenu(parent), d_ptr(new SAColorMenu::PrivateData(this)) +{ + init(SA::getStandardColorList()); +} + +SAColorMenu::SAColorMenu(const QString& title, QWidget* parent) + : QMenu(title, parent), d_ptr(new SAColorMenu::PrivateData(this)) +{ + init(SA::getStandardColorList()); +} + +SAColorMenu::~SAColorMenu() +{ +} + +/** + * @brief 快速绑定ColorToolButton + * @param btn + */ +void SAColorMenu::bindToColorToolButton(SAColorToolButton* btn) +{ + if (!btn) { + return; + } + if (btn->menu() != this) { + btn->setMenu(this); + } + connect(this, &SAColorMenu::selectedColor, btn, &SAColorToolButton::setColor); +} + +/** + * @brief ThemeColorsPalette对应的action + * @return + */ +QWidgetAction* SAColorMenu::themeColorsPaletteAction() const +{ + return d_ptr->mThemeColorPaletteAction; +} + +/** + * @brief CustomColorsWidget对应的action + * @return + */ +QWidgetAction* SAColorMenu::getCustomColorsWidgetAction() const +{ + return d_ptr->mCustomColorsWidgetAction; +} + +/** + * @brief 自定义颜色action + * @return + */ +QAction* SAColorMenu::customColorAction() const +{ + return d_ptr->mCustomColorAction; +} + +/** + * @brief 获取ThemeColorsPalette + * @return + */ +SAColorPaletteGridWidget* SAColorMenu::colorPaletteGridWidget() const +{ + return d_ptr->mThemeColorsWidget; +} + +/** + * @brief 获取自定义颜色grid + * @return + */ +SAColorGridWidget* SAColorMenu::customColorsWidget() const +{ + return d_ptr->mCustomColorsWidget; +} + +/** + * @brief 建立没有颜色的action,可以选择无颜色 + * + * 无颜色选中会发射selectedColor(QColor()) + * @param on + */ +void SAColorMenu::enableNoneColorAction(bool on) +{ + // 无颜色默认是在自定义颜色的上方 + if (on) { + if (d_ptr->mNoneColorAction) { + if (actions().contains(d_ptr->mNoneColorAction)) { + // 已经包含了NoneColorAction,退出 + return; + } else { + insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); + } + } else { + QIcon ic = d_ptr->createNoneColorIcon(); + d_ptr->mNoneColorAction = new QAction(ic, tr("None"), this); + connect(d_ptr->mNoneColorAction, &QAction::triggered, this, &SAColorMenu::onNoneColorActionTriggered); + insertAction(d_ptr->mCustomColorAction, d_ptr->mNoneColorAction); + } + } else { + removeAction(d_ptr->mNoneColorAction); + // remove后暂时不删除action + } +} + +/** + * @brief 获取None Color Action + * + * @note 注意,enableNoneColorAction(true),之后才不是nullptr + * @return 如果没有建立NoneColorAction,会返回nullptr + */ +QAction* SAColorMenu::noneColorAction() const +{ + return d_ptr->mNoneColorAction; +} + +/** + * @brief 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 + * @param c + */ +void SAColorMenu::emitSelectedColor(const QColor& c) +{ + emit selectedColor(c); + hide(); +} + +void SAColorMenu::init(const QList< QColor >& themeCls) +{ + d_ptr->mTitleLabel = new QLabel(this); + d_ptr->mTitleLabel->setText(tr("Theme Colors")); + d_ptr->mTitleLabelAction = d_ptr->addWidget(d_ptr->mTitleLabel); + + d_ptr->mThemeColorsWidget = new SAColorPaletteGridWidget(themeCls, this); + d_ptr->mThemeColorsWidget->setColorCheckable(false); + d_ptr->mThemeColorPaletteAction = d_ptr->addWidget(d_ptr->mThemeColorsWidget); + + d_ptr->mCustomColorAction = new QAction(tr("Custom Color"), this); // cn:自定义颜色 + addAction(d_ptr->mCustomColorAction); + + QSize clrSize = d_ptr->mThemeColorsWidget->colorIconSize(); + d_ptr->mCustomColorsWidget = new SAColorGridWidget(this); + d_ptr->mCustomColorsWidget->setRowMinimumHeight(0, clrSize.height()); + d_ptr->mCustomColorsWidget->setHorizontalSpacerToRight(); + d_ptr->mCustomColorsWidget->setColorIconSize(clrSize); + d_ptr->mCustomColorsWidget->setColumnCount(d_ptr->mMaxCustomColorSize); + d_ptr->mCustomColorsWidget->setColorCheckable(false); + d_ptr->mCustomColorsWidgetAction = d_ptr->addWidget(d_ptr->mCustomColorsWidget); + + connect(d_ptr->mCustomColorAction, &QAction::triggered, this, &SAColorMenu::onCustomColorActionTriggered); + connect(d_ptr->mThemeColorsWidget, &SAColorPaletteGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); + connect(d_ptr->mCustomColorsWidget, &SAColorGridWidget::colorClicked, this, &SAColorMenu::emitSelectedColor); +} + +/** + * @brief 自定义颜色 + * @param on + */ +void SAColorMenu::onCustomColorActionTriggered(bool on) +{ + Q_UNUSED(on); + QColor c = d_ptr->getColorByDialog(); + if (c.isValid()) { + d_ptr->recordCustomColor(c); + d_ptr->mCustomColorsWidget->setColorList(d_ptr->mCustomColors); + updateGeometry(); + emitSelectedColor(c); + } +} + +/** + * @brief 无颜色 + * @param on + */ +void SAColorMenu::onNoneColorActionTriggered(bool on) +{ + Q_UNUSED(on); + emitSelectedColor(QColor()); +} + +/*** End of inlined file: SAColorMenu.cpp ***/ + +/*** Start of inlined file: SAColorGridWidget.cpp ***/ +#include +#include +#include + +class SAColorGridWidget::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorGridWidget) +public: + PrivateData(SAColorGridWidget* p); + // 获取ColorToolButton + SAColorToolButton* getColorToolButtonAt(int index); + SAColorToolButton* getColorToolButtonAt(int r, int c); + SAColorToolButton* getCheckedButton() const; + void updateGridColor(bool isRemoveSpacer = false); + void updateGridColorSize(); + void updateGridColorCheckable(); + void iterationColorBtns(SAColorGridWidget::FunColorBtn fn); + void removeAt(int r, int c); + void setColorAt(const QColor& clr, int r, int c); + bool isSpacer(int r, int c) const; + +public: + QList< QColor > mColors; + QGridLayout* mGridLayout { nullptr }; + QButtonGroup* mButtonGroup { nullptr }; + QSize mIconSize { 16, 16 }; + int mColumnCount { 8 }; ///< 列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 + bool mColorCheckable; ///< 设置颜色是否是checkable + bool mHorizontalSpacerToRight { false }; ///< 最右边是否有弹簧 +}; + +SAColorGridWidget::PrivateData::PrivateData(SAColorGridWidget* p) : q_ptr(p) +{ + mGridLayout = new QGridLayout(p); + p->setLayout(mGridLayout); + mGridLayout->setSpacing(0); + mGridLayout->setContentsMargins(1, 1, 1, 1); + mButtonGroup = new QButtonGroup(p); + mButtonGroup->setExclusive(true); + p->setMinimumHeight(mIconSize.height()); + p->setMinimumWidth(mIconSize.width()); +} + +SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int index) +{ + QLayoutItem* item = mGridLayout->itemAt(index); + if (nullptr == item) { + return nullptr; + } + return qobject_cast< SAColorToolButton* >(item->widget()); +} + +SAColorToolButton* SAColorGridWidget::PrivateData::getColorToolButtonAt(int r, int c) +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (nullptr == item) { + return nullptr; + } + return qobject_cast< SAColorToolButton* >(item->widget()); +} + +SAColorToolButton* SAColorGridWidget::PrivateData::getCheckedButton() const +{ + return qobject_cast< SAColorToolButton* >(mButtonGroup->checkedButton()); +} + +/** + * @brief 根据mColors更新布局 + */ +void SAColorGridWidget::PrivateData::updateGridColor(bool isRemoveSpacer) +{ + int row = 1; + int col = mColumnCount; + if (col <= 0) { + col = mColors.size(); + } else { + row = std::ceil(mColors.size() / (float)col); + } + int index = 0; + for (int r = 0; r < row; ++r) { + for (int c = 0; c < col; ++c) { + if (index < mColors.size()) { + setColorAt(mColors[ index ], r, c); + ++index; + } else { + removeAt(r, c); + } + } + } + // 清除多余单元格 + int nowGridRow = mGridLayout->rowCount(); + int nowGridCol = mGridLayout->columnCount(); + if (nowGridRow > row) { + // 多余的清除 + for (int r = row; r < nowGridRow; ++r) { + for (int c = 0; c < nowGridCol; ++c) { + removeAt(r, c); + } + } + } + if (nowGridCol > col) { + // 多余的列清除 + for (int r = 0; r < row; ++r) { + for (int c = col; c < nowGridCol; ++c) { + if (isRemoveSpacer) { + removeAt(r, c); + } else { + if (!isSpacer(r, c)) { + removeAt(r, c); + } + } + } + } + } +} + +/** + * @brief 更新colorsize + */ +void SAColorGridWidget::PrivateData::updateGridColorSize() +{ + QSize s = mIconSize; + iterationColorBtns([ s ](SAColorToolButton* btn) { + if (btn) { + btn->setIconSize(s); + } + }); +} + +void SAColorGridWidget::PrivateData::updateGridColorCheckable() +{ + bool v = mColorCheckable; + iterationColorBtns([ v ](SAColorToolButton* btn) { + if (btn) { + btn->setCheckable(v); + } + }); +} + +/** + * @brief 遍历所有的button + * @param fn + */ +void SAColorGridWidget::PrivateData::iterationColorBtns(FunColorBtn fn) +{ + int cnt = mGridLayout->count(); + for (int i = 0; i < cnt; ++i) { + SAColorToolButton* tl = getColorToolButtonAt(i); + fn(tl); + } +} + +/** + * @brief 删除网格窗口 + * @param r + * @param c + */ +void SAColorGridWidget::PrivateData::removeAt(int r, int c) +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (item) { + QWidget* w = item->widget(); + mGridLayout->removeItem(item); + delete item; + if (w) { + w->deleteLater(); + } + } +} + +void SAColorGridWidget::PrivateData::setColorAt(const QColor& clr, int r, int c) +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (item) { + SAColorToolButton* tl = qobject_cast< SAColorToolButton* >(item->widget()); + if (tl) { + tl->setColor(clr); + } + } else { + SAColorToolButton* tl = new SAColorToolButton(q_ptr); + tl->setToolButtonStyle(Qt::ToolButtonIconOnly); + tl->setIconSize(mIconSize); + tl->setMargins(QMargins(4, 4, 4, 4)); + tl->setColor(clr); + tl->setCheckable(mColorCheckable); + tl->setAutoRaise(true); + mButtonGroup->addButton(tl, r + c); + mGridLayout->addWidget(tl, r, c); + } +} + +bool SAColorGridWidget::PrivateData::isSpacer(int r, int c) const +{ + QLayoutItem* item = mGridLayout->itemAtPosition(r, c); + if (item) { + if (QSpacerItem* si = dynamic_cast< QSpacerItem* >(item)) { + return true; + } + } + return false; +} + +//============================================================== +// SAColorGridWidget +//============================================================== + +SAColorGridWidget::SAColorGridWidget(QWidget* par) : QWidget(par), d_ptr(new SAColorGridWidget::PrivateData(this)) +{ + connect(d_ptr->mButtonGroup, QOverload< QAbstractButton* >::of(&QButtonGroup::buttonClicked), this, &SAColorGridWidget::onButtonClicked); + connect(d_ptr->mButtonGroup, QOverload< QAbstractButton* >::of(&QButtonGroup::buttonPressed), this, &SAColorGridWidget::onButtonPressed); + connect(d_ptr->mButtonGroup, QOverload< QAbstractButton* >::of(&QButtonGroup::buttonReleased), this, &SAColorGridWidget::onButtonReleased); + connect(d_ptr->mButtonGroup, QOverload< QAbstractButton*, bool >::of(&QButtonGroup::buttonToggled), this, &SAColorGridWidget::onButtonToggled); +} + +SAColorGridWidget::~SAColorGridWidget() +{ +} + +/** + * @brief 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 + * @param c + */ +void SAColorGridWidget::setColumnCount(int c) +{ + d_ptr->mColumnCount = c; + d_ptr->updateGridColor(true); + if (d_ptr->mHorizontalSpacerToRight) { + setHorizontalSpacerToRight(); + } + updateGeometry(); +} + +/** + * @brief 设置颜色列表 + * @param c + */ +void SAColorGridWidget::setColorList(const QList< QColor >& cls) +{ + d_ptr->mColors = cls; + d_ptr->updateGridColor(); + updateGeometry(); +} + +/** + * @brief 获取颜色列表 + * @return + */ +QList< QColor > SAColorGridWidget::getColorList() const +{ + return d_ptr->mColors; +} + +/** + * @brief 获取间隔 + * @return + */ +int SAColorGridWidget::spacing() const +{ + return d_ptr->mGridLayout->spacing(); +} + +/** + * @brief 设置间隔 + * @param v + */ +void SAColorGridWidget::setSpacing(int v) +{ + d_ptr->mGridLayout->setSpacing(v); +} + +/** + * @brief 获取颜色的数量 + * @return + */ +int SAColorGridWidget::colorCount() const +{ + return d_ptr->mColors.size(); +} + +/** + * @brief 设置图标 size + * @return + */ +void SAColorGridWidget::setColorIconSize(const QSize& s) +{ + d_ptr->mIconSize = s; + setMinimumHeight(s.height()); + setMinimumWidth(s.width()); + d_ptr->updateGridColorSize(); +} + +/** + * @brief 获取图标 size + * @return + */ +QSize SAColorGridWidget::colorIconSize() const +{ + return d_ptr->mIconSize; +} + +/** + * @brief 设置颜色是否是checkable + * + * checkable的颜色按钮是可checked的 + * @param on + */ +void SAColorGridWidget::setColorCheckable(bool on) +{ + d_ptr->mColorCheckable = on; + d_ptr->updateGridColorCheckable(); +} + +/** + * @brief 颜色是否是checkable + * @return + */ +bool SAColorGridWidget::isColorCheckable() const +{ + return d_ptr->mColorCheckable; +} + +/** + * @brief 获取当前选中的颜色 + * @return + */ +QColor SAColorGridWidget::currentCheckedColor() const +{ + QAbstractButton* btn = d_ptr->mButtonGroup->checkedButton(); + if (nullptr == btn) { + return QColor(); + } + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (nullptr == t) { + return QColor(); + } + return t->color(); +} + +/** + * @brief 获取index对应的colorbutton + * @param index + * @return 如果没有返回nullptr + */ +SAColorToolButton* SAColorGridWidget::colorButton(int index) const +{ + return d_ptr->getColorToolButtonAt(index); +} + +/** + * @brief 等同GridLayout的VerticalSpacing属性 + * @param v + */ +void SAColorGridWidget::setVerticalSpacing(int v) +{ + d_ptr->mGridLayout->setVerticalSpacing(v); +} +/** + * @brief 等同GridLayout的VerticalSpacing属性 + * @return + */ +int SAColorGridWidget::verticalSpacing() const +{ + return d_ptr->mGridLayout->verticalSpacing(); +} +/** + * @brief 等同GridLayout的HorizontalSpacing属性 + * @param v + */ +void SAColorGridWidget::setHorizontalSpacing(int v) +{ + d_ptr->mGridLayout->setHorizontalSpacing(v); +} +/** + * @brief 等同GridLayout的HorizontalSpacing属性 + * @return + */ +int SAColorGridWidget::horizontalSpacing() const +{ + return d_ptr->mGridLayout->horizontalSpacing(); +} + +/** + * @brief 清除选中状态,这时没有颜色是选中的 + */ +void SAColorGridWidget::clearCheckedState() +{ + if (d_ptr->mButtonGroup->exclusive()) { + SAColorToolButton* btn = d_ptr->getCheckedButton(); + if (btn) { + d_ptr->mButtonGroup->setExclusive(false); + btn->setChecked(false); + d_ptr->mButtonGroup->setExclusive(true); + } + } else { + d_ptr->iterationColorBtns([](SAColorToolButton* btn) { + if (btn->isChecked()) { + btn->setChecked(false); + } + }); + } +} + +void SAColorGridWidget::iterationColorBtns(SAColorGridWidget::FunColorBtn fn) +{ + d_ptr->iterationColorBtns(fn); +} + +void SAColorGridWidget::setRowMinimumHeight(int row, int minSize) +{ + d_ptr->mGridLayout->setRowMinimumHeight(row, minSize); +} + +void SAColorGridWidget::setHorizontalSpacerToRight(bool on) +{ + if (on) { + QSpacerItem* horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + d_ptr->mGridLayout->addItem(horizontalSpacer, 0, d_ptr->mColumnCount, 1, 1); + } else { + d_ptr->removeAt(0, d_ptr->mColumnCount); + } + d_ptr->mHorizontalSpacerToRight = on; +} + +void SAColorGridWidget::onButtonClicked(QAbstractButton* btn) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + emit colorClicked(t->color()); + } +} + +void SAColorGridWidget::onButtonPressed(QAbstractButton* btn) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + emit colorPressed(t->color()); + } +} + +void SAColorGridWidget::onButtonToggled(QAbstractButton* btn, bool on) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + emit colorToggled(t->color(), on); + } +} + +QSize SAColorGridWidget::sizeHint() const +{ + return d_ptr->mGridLayout->sizeHint(); + // int w = d_ptr->mIconSize.width() + d_ptr->mGridLayout->verticalSpacing(); + // int h = d_ptr->mIconSize.height(); + // if (d_ptr->mColumnCount > 0) { + // h *= d_ptr->mColumnCount; + // if (!d_ptr->mColors.empty()) { + // int r = std::ceil(d_ptr->mColors.size() / (float)(d_ptr->mColumnCount)); + // if (r > 0) { + // w *= r; + // } + // } + // } + // return QSize(w, h); +} + +void SAColorGridWidget::onButtonReleased(QAbstractButton* btn) +{ + SAColorToolButton* t = qobject_cast< SAColorToolButton* >(btn); + if (t) { + emit colorReleased(t->color()); + } +} + +namespace SA +{ + +QList< QColor > getStandardColorList() +{ + static QList< QColor > s_standardColorList({ QColor(192, 0, 0), + QColor(255, 0, 0), + QColor(255, 192, 0), + QColor(255, 255, 0), + QColor(146, 208, 80), + QColor(0, 176, 80), + QColor(0, 176, 240), + QColor(0, 112, 192), + QColor(0, 32, 96), + QColor(112, 48, 160) }); + return s_standardColorList; +} +} + +/*** End of inlined file: SAColorGridWidget.cpp ***/ + +/*** Start of inlined file: SAColorPaletteGridWidget.cpp ***/ +// Qt +#include +#include +#include +#include +// SA + +class SAColorPaletteGridWidget::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorPaletteGridWidget) +public: + PrivateData(SAColorPaletteGridWidget* p); + // 生成color palette + QList< QColor > makeColorPalette(const QList< QColor >& clrList) const; + +public: + QList< int > mFactor { 180, 160, 140, 75, 50 }; ///< palette的比例因子,将调用QColor的lighter函数执行 + QVBoxLayout* mLayout { nullptr }; ///< 垂直布局 + SAColorGridWidget* mMainColorList { nullptr }; ///< 这个用于显示标准颜色 + SAColorGridWidget* mPaletteColorGrid { nullptr }; ///< 这个用于生成3行亮色,2行暗色的palette +}; + +SAColorPaletteGridWidget::PrivateData::PrivateData(SAColorPaletteGridWidget* p) : q_ptr(p) +{ + mLayout = new QVBoxLayout(p); + p->setLayout(mLayout); + mMainColorList = new SAColorGridWidget(p); + mPaletteColorGrid = new SAColorGridWidget(p); + mLayout->addWidget(mMainColorList); + mLayout->addWidget(mPaletteColorGrid); + mLayout->setContentsMargins(1, 1, 1, 1); + mLayout->setSpacing(8); + QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + mMainColorList->setSizePolicy(sizePolicy); + mPaletteColorGrid->setSizePolicy(sizePolicy); + mMainColorList->setColumnCount(0); + mPaletteColorGrid->setVerticalSpacing(0); +} + +QList< QColor > SAColorPaletteGridWidget::PrivateData::makeColorPalette(const QList< QColor >& clrList) const +{ + QList< QColor > res; + for (int f : qAsConst(mFactor)) { + for (const QColor& c : qAsConst(clrList)) { + res.append(c.lighter(f)); + } + } + return res; +} +//============================================================== +// name +//============================================================== + +/** + * @brief 使用默认的标准颜色生成一个color palette + * @param par + */ +SAColorPaletteGridWidget::SAColorPaletteGridWidget(QWidget* par) : QWidget(par), d_ptr(new PrivateData(this)) +{ + init(); + setColorList(SA::getStandardColorList()); +} + +/** + * @brief 根据指定的颜色生成一个color palette + * @param cls + * @param par + */ +SAColorPaletteGridWidget::SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par) + : QWidget(par), d_ptr(new PrivateData(this)) +{ + init(); + setColorList(cls); +} + +SAColorPaletteGridWidget::~SAColorPaletteGridWidget() +{ +} +void SAColorPaletteGridWidget::init() +{ + connect(d_ptr->mMainColorList, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onMainColorClicked); + connect(d_ptr->mPaletteColorGrid, &SAColorGridWidget::colorClicked, this, &SAColorPaletteGridWidget::onPaletteColorClicked); + QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + setSizePolicy(sizePolicy); + setColorIconSize(QSize(10, 10)); +} + +/** + * @brief 设置颜色系列,颜色系列的个数决定了主色卡的数量,palette色卡会根据factor进行生成 + * @param cls + */ +void SAColorPaletteGridWidget::setColorList(const QList< QColor >& cls) +{ + d_ptr->mMainColorList->setColumnCount(0); + d_ptr->mMainColorList->setColorList(cls); + d_ptr->mPaletteColorGrid->setColumnCount(cls.size()); + d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(cls)); +} + +/** + * @brief 获取颜色系列 + * @return + */ +QList< QColor > SAColorPaletteGridWidget::colorList() const +{ + return d_ptr->mMainColorList->getColorList(); +} + +/** + * @brief 设置颜色深浅比例factor,factor决定了palette的行数,factor有5个,就有5行 + * + * 默认为{ 180, 160, 140, 75, 50 },相当于会有5行,每个系数会对标准颜色执行QColor::lighter操作 + * @param factor + */ +void SAColorPaletteGridWidget::setFactor(const QList< int >& factor) +{ + d_ptr->mFactor = factor; + d_ptr->mPaletteColorGrid->setColorList(d_ptr->makeColorPalette(d_ptr->mMainColorList->getColorList())); +} + +/** + * @brief 获取色卡 + * @return + */ +QList< int > SAColorPaletteGridWidget::factor() const +{ + return d_ptr->mFactor; +} + +/** + * @brief 设置颜色块的尺寸,默认为10*10 + * @param s + */ +void SAColorPaletteGridWidget::setColorIconSize(const QSize& s) +{ + d_ptr->mMainColorList->setColorIconSize(s); + d_ptr->mPaletteColorGrid->setColorIconSize(s); +} + +QSize SAColorPaletteGridWidget::colorIconSize() const +{ + return d_ptr->mMainColorList->colorIconSize(); +} + +/** + * @brief 设置颜色块是否能被checked + * @param on + */ +void SAColorPaletteGridWidget::setColorCheckable(bool on) +{ + d_ptr->mMainColorList->setColorCheckable(on); + d_ptr->mPaletteColorGrid->setColorCheckable(on); +} + +bool SAColorPaletteGridWidget::isColorCheckable() const +{ + return d_ptr->mMainColorList->isColorCheckable(); +} + +void SAColorPaletteGridWidget::onMainColorClicked(const QColor& c) +{ + d_ptr->mPaletteColorGrid->clearCheckedState(); + emit colorClicked(c); +} + +void SAColorPaletteGridWidget::onPaletteColorClicked(const QColor& c) +{ + d_ptr->mMainColorList->clearCheckedState(); + emit colorClicked(c); +} + +/*** End of inlined file: SAColorPaletteGridWidget.cpp ***/ + +/*** Start of inlined file: SAColorToolButton.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#define SAColorToolButton_DEBUG_PRINT 0 +class SAColorToolButton::PrivateData +{ + SA_COLOR_WIDGETS_DECLARE_PUBLIC(SAColorToolButton) +public: + PrivateData(SAColorToolButton* p); + void calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + void calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + void calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + void calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect); + QRect getButtonRect(const QStyleOptionToolButton& opt); + QRect getIndicatorRect(const QStyleOptionToolButton& opt); + QStyle::State getButtonStyleState(const QStyleOptionToolButton& opt); + QStyle::State getButtonMenuStyleState(const QStyleOptionToolButton& opt); + +public: + QColor mColor { Qt::white }; + int mSpacing { 2 }; ///< 间隔 + QMargins mMargins { 3, 3, 3, 3 }; ///< box + + static int s_indicatorArrorWidth; ///< 菜单宽度 +}; + +int SAColorToolButton::PrivateData::s_indicatorArrorWidth = 8; + +SAColorToolButton::PrivateData::PrivateData(SAColorToolButton* p) : q_ptr(p) +{ +} + +void SAColorToolButton::PrivateData::calcSizeOfToolButtonIconOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect) +{ + // 确定文本区域 + textRect = QRect(); + QRect buttonRect = getButtonRect(opt); + if (opt.icon.isNull()) { + colorRect = buttonRect; + iconRect = QRect(); + } else { + QSize tmpSize = opt.iconSize; + if (tmpSize.isNull()) { + tmpSize = QSize(16, 16); + } + tmpSize = buttonRect.size().boundedTo(tmpSize); + int colorHeight = tmpSize.height() / 4; + int totalHeight = colorHeight + tmpSize.height() + mSpacing; + if (totalHeight > buttonRect.height()) { + // 过高,这时要通过高度反推tmpSize和colorHeight + colorHeight = (buttonRect.height() - mSpacing) / 5; + tmpSize.setHeight(colorHeight * 4); + if (opt.iconSize.height() > 0) { + tmpSize.setWidth(tmpSize.height() * opt.iconSize.width() / opt.iconSize.height()); // 等比例 + } + tmpSize = buttonRect.size().boundedTo(tmpSize); + } + // 有icon,颜色位于图标下面 + int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 + int x = (buttonRect.width() - tmpSize.width()) / 2; + iconRect = QRect(buttonRect.left() + x, buttonRect.top() + y, tmpSize.width(), tmpSize.height()); + colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); + } +} + +/** + * @brief 纯文本情况,左边有5像素显示颜色 + * @param opt + * @param iconRect + * @param textRect + * @param colorRect 左边有5像素显示颜色 + */ +void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextOnly(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect) +{ + QRect buttonRect = getButtonRect(opt); + iconRect = QRect(); + QSize colorSize = opt.iconSize; + if (colorSize.isNull()) { + colorSize = QSize(16, 16); + } + colorSize = buttonRect.size().boundedTo(colorSize); + colorRect = QRect(buttonRect.left(), + buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, + colorSize.width(), + colorSize.height()); + textRect = buttonRect.adjusted(colorRect.right() + mSpacing, 0, 0, 0); +} + +void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextBesideIcon(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + QRect buttonRect = getButtonRect(opt); + if (opt.icon.isNull()) { + QSize colorSize = opt.iconSize; + if (colorSize.isNull()) { + colorSize = QSize(16, 16); + } + // 说明没有icon + // 这时所有都是color + // iconRect=Null + colorSize = buttonRect.size().boundedTo(colorSize); + iconRect = QRect(); + colorRect = QRect(buttonRect.left(), + buttonRect.top() + (buttonRect.height() - colorSize.height()) / 2, + colorSize.width(), + colorSize.height()); + textRect = buttonRect.adjusted(colorRect.width() + mSpacing, 0, 0, 0); + } else { // 有图标 + QSize tmpSize = opt.iconSize; + if (tmpSize.isNull()) { + tmpSize = QSize(16, 16); + } + tmpSize = buttonRect.size().boundedTo(tmpSize); + int colorHeight = tmpSize.height() / 4; + int totalHeight = colorHeight + tmpSize.height() + mSpacing; + if (totalHeight > buttonRect.height()) { + // 过高,这时要通过高度反推tmpSize和colorHeight + colorHeight = (buttonRect.height() - mSpacing) / 5; + tmpSize.setHeight(colorHeight * 4); + tmpSize.setWidth(tmpSize.height()); // 对于异形也设置为正方行 + } + // 有icon,颜色位于图标下面 + int y = (buttonRect.height() - colorHeight - mSpacing - tmpSize.height()) / 2; //(ButtonHeight-TotalHeight)/2 + iconRect = QRect(buttonRect.left(), buttonRect.top() + y, tmpSize.width(), tmpSize.height()); + colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); + textRect = buttonRect.adjusted(iconRect.right() + mSpacing, 0, 0, 0); + } +} + +void SAColorToolButton::PrivateData::calcSizeOfToolButtonTextUnderIcon(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& colorRect) +{ + QRect buttonRect = getButtonRect(opt); + QSize tmpSize = opt.iconSize; + if (tmpSize.isNull()) { + tmpSize = QSize(16, 16); + } + // 获取字体高度 + int textHeight = opt.fontMetrics.height(); + tmpSize = buttonRect.size().boundedTo(tmpSize); + if (opt.icon.isNull()) { + int totalHeight = textHeight + opt.iconSize.height() + mSpacing; + if (totalHeight < buttonRect.height()) { + // 足够高 + colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + (buttonRect.height() - totalHeight) / 2, + tmpSize.width(), + opt.iconSize.height()); + } else { + // 空间不足 + colorRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + mSpacing, + tmpSize.width(), + opt.iconSize.height()); + } + iconRect = QRect(); + textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); + + } else { + // 有图标 + int colorHeight = tmpSize.height() / 4; + int totalHeight = textHeight + opt.iconSize.height() + colorHeight + 2 * mSpacing; + if (totalHeight < buttonRect.height()) { + // 高度空间足够 + // 先布置icon + iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + (buttonRect.height() - totalHeight) / 2, + tmpSize.width(), + opt.iconSize.height()); + + } else { + // 空间不足 + iconRect = QRect(buttonRect.left() + (buttonRect.width() - tmpSize.width()) / 2, + buttonRect.top() + mSpacing, + tmpSize.width(), + opt.iconSize.height()); + } + colorRect = QRect(iconRect.x(), iconRect.bottom() + mSpacing, iconRect.width(), colorHeight); + textRect = QRect(buttonRect.left(), colorRect.bottom() + mSpacing, buttonRect.width(), textHeight); + } +} + +QPixmap SAColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QRect& iconRect) +{ + if (opt.icon.isNull()) { + return (QPixmap()); + } + // 有图标 + QIcon::State state = opt.state & QStyle::State_On ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(opt.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { + mode = QIcon::Active; + } else { + mode = QIcon::Normal; + } + // return (opt.icon.pixmap(this->window()->windowHandle(), opt.rect.size().boundedTo(realConSize), mode, state)); + return (opt.icon.pixmap(iconRect.size(), mode, state)); +} + +/** + * @brief 获取按钮的矩形区域 + * @param opt + * @return + */ +QRect SAColorToolButton::PrivateData::getButtonRect(const QStyleOptionToolButton& opt) +{ + QRect btnRect = q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButton, q_ptr); + return btnRect.marginsRemoved(mMargins); +} + +/** + * @brief 获取ToolButtonMenu的区域 + * @note 注意,如果不是QStyleOptionToolButton::MenuButtonPopup|QStyleOptionToolButton::HasMenu下调用此函数,返回的是QRect() + * @param opt + * @return + */ +QRect SAColorToolButton::PrivateData::getIndicatorRect(const QStyleOptionToolButton& opt) +{ + if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { + return q_ptr->style()->subControlRect(QStyle::CC_ToolButton, &opt, QStyle::SC_ToolButtonMenu, q_ptr); + } + return QRect(); +} + +/** + * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 + * @param opt + * @return + */ +QStyle::State SAColorToolButton::PrivateData::getButtonStyleState(const QStyleOptionToolButton& opt) +{ + QStyle::State bflags = opt.state & ~QStyle::State_Sunken; + if (bflags & QStyle::State_AutoRaise) { + if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { + bflags &= ~QStyle::State_Raised; + } + } + if (opt.state & QStyle::State_Sunken) { + if (opt.activeSubControls & QStyle::SC_ToolButton) { + bflags |= QStyle::State_Sunken; + } + } + return bflags; +} + +/** + * @brief 基于Qt qcommonStyle源码,对QStyleOptionToolButton的style修正 + * @param opt + * @return + */ +QStyle::State SAColorToolButton::PrivateData::getButtonMenuStyleState(const QStyleOptionToolButton& opt) +{ + QStyle::State mflags = opt.state & ~QStyle::State_Sunken; + if (mflags & QStyle::State_AutoRaise) { + if (!(mflags & QStyle::State_MouseOver) || !(mflags & QStyle::State_Enabled)) { + mflags &= ~QStyle::State_Raised; + } + } + if (opt.state & QStyle::State_Sunken) { + mflags |= QStyle::State_Sunken; + } + return mflags; +} + +//============================================================== +// SAColorToolButton +//============================================================== +SAColorToolButton::SAColorToolButton(QWidget* parent) : QToolButton(parent), d_ptr(new PrivateData(this)) +{ + QStyleOptionToolButton opt; + initStyleOption(&opt); + connect(this, &QToolButton::clicked, this, &SAColorToolButton::onButtonClicked); +} + +SAColorToolButton::~SAColorToolButton() +{ +} + +/** + * @brief 获取颜色 + * @return + */ +QColor SAColorToolButton::color() const +{ + return d_ptr->mColor; +} + +/** + * @brief 设置Margins + * @param mg + */ +void SAColorToolButton::setMargins(const QMargins& mg) +{ + d_ptr->mMargins = mg; + repaint(); +} + +QMargins SAColorToolButton::margins() const +{ + return d_ptr->mMargins; +} + +/** + * @brief 绘制无颜色表示 + * @param p + * @param colorRect 绘制的区域 + */ +void SAColorToolButton::paintNoneColor(QPainter* p, const QRect& colorRect) +{ + p->save(); + QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); + p->setPen(pen); + p->setRenderHint(QPainter::SmoothPixmapTransform, true); + p->setRenderHint(QPainter::Antialiasing, true); + int ss = colorRect.width() / 3; + p->drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); + pen.setColor(Qt::black); + p->setPen(pen); + p->drawRect(colorRect); + p->restore(); +} + +/** + * @brief 设置颜色 + * @note 会发射@sa colorChanged 信号 + * @param c + */ +void SAColorToolButton::setColor(const QColor& c) +{ + if (d_ptr->mColor != c) { + d_ptr->mColor = c; + repaint(); + emit colorChanged(c); + } +} + +/** + * @brief 计算各个基本组件的位置 + * @param opt + * @param iconRect 图标区域 + * @param textRect 文本区域 + * @param colorRect 颜色区域 + */ +void SAColorToolButton::calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect) +{ + switch (opt.toolButtonStyle) { + case Qt::ToolButtonTextOnly: + d_ptr->calcSizeOfToolButtonTextOnly(opt, iconRect, textRect, colorRect); + break; + case Qt::ToolButtonTextBesideIcon: + d_ptr->calcSizeOfToolButtonTextBesideIcon(opt, iconRect, textRect, colorRect); + break; + case Qt::ToolButtonTextUnderIcon: + d_ptr->calcSizeOfToolButtonTextUnderIcon(opt, iconRect, textRect, colorRect); + break; + case Qt::ToolButtonFollowStyle: + case Qt::ToolButtonIconOnly: + d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); + break; + default: + d_ptr->calcSizeOfToolButtonIconOnly(opt, iconRect, textRect, colorRect); + break; + } +} + +void SAColorToolButton::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + QStylePainter p(this); + + QStyleOptionToolButton opt; + + initStyleOption(&opt); + QRect iconRect, colorRect, textRect; + calcRect(opt, iconRect, textRect, colorRect); + paintButton(&p, opt); + // 绘制图标 + paintIcon(&p, iconRect, opt); + // 绘制文字 + paintText(&p, textRect, opt); + // 绘制颜色 + paintColor(&p, colorRect, d_ptr->mColor, opt); +} + +void SAColorToolButton::resizeEvent(QResizeEvent* e) +{ + // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 + QToolButton::resizeEvent(e); +} + +/** + * @brief sizeHint重载 + * @return + */ +QSize SAColorToolButton::sizeHint() const +{ + ensurePolished(); + QStyleOptionToolButton opt; + initStyleOption(&opt); + int w = 0, h = 0; + if (Qt::ToolButtonIconOnly == opt.toolButtonStyle || Qt::ToolButtonFollowStyle == opt.toolButtonStyle) { + // 和文本无关 + w = opt.iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); + h = opt.iconSize.height() + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); + } else if (Qt::ToolButtonTextOnly == opt.toolButtonStyle || Qt::ToolButtonTextBesideIcon == opt.toolButtonStyle) { + QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); + textSize.setHeight(textSize.height() + 4); + QSize iconSize = opt.iconSize; + if (!opt.icon.isNull()) { + // 有图标,要有iconsize高度的1/4给颜色 + iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); + } + w = textSize.width() + d_ptr->mSpacing + iconSize.width() + d_ptr->mMargins.left() + d_ptr->mMargins.right(); + h = qMax(textSize.height(), iconSize.height()) + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); + } else if (Qt::ToolButtonTextUnderIcon == opt.toolButtonStyle) { + QSize textSize = opt.fontMetrics.size(Qt::TextSingleLine | Qt::TextShowMnemonic, opt.text); + textSize.setHeight(textSize.height() + 4); + QSize iconSize = opt.iconSize; + if (!opt.icon.isNull()) { + // 有图标,要有iconsize高度的1/4给颜色 + iconSize.setHeight(iconSize.height() + iconSize.height() / 4 + d_ptr->mSpacing); + } + w = qMax(textSize.width(), iconSize.width()) + d_ptr->mMargins.left() + d_ptr->mMargins.right(); + h = textSize.height() + iconSize.height() + d_ptr->mSpacing + d_ptr->mMargins.top() + d_ptr->mMargins.bottom(); + } + opt.rect.setSize(QSize(w, h)); // PM_MenuButtonIndicator depends on the height + if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { + w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this); + } + //! Qt6.4 取消了QApplication::globalStrut + return style()->sizeFromContents(QStyle::CT_ToolButton, &opt, QSize(w, h), this).expandedTo(QSize(2, 2)); +} + +void SAColorToolButton::onButtonClicked(bool checked) +{ + emit colorClicked(color(), checked); +} + +/** + * @brief 绘制按钮 + * @param p + * @param opt + */ +void SAColorToolButton::paintButton(QStylePainter* p, const QStyleOptionToolButton& opt) +{ + bool autoRaise = opt.state & QStyle::State_AutoRaise; + // 绘制按钮 + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &opt, p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &opt, p, this); + } + if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { + QStyleOption tool = opt; + tool.state = d_ptr->getButtonMenuStyleState(opt); + tool.rect = d_ptr->getIndicatorRect(opt); + // if (tool.state & (QStyle::State_Sunken | QStyle::State_On | QStyle::State_Raised)) { + // style()->drawPrimitive(QStyle::PE_IndicatorButtonDropDown, &tool, &p, this); + // } + style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, p, this); + } + + // 绘制focus + if (opt.state & QStyle::State_HasFocus) { + QStyleOptionFocusRect fr; + fr.QStyleOption::operator=(opt); + fr.rect.adjust(3, 3, -3, -3); + if (opt.features & QStyleOptionToolButton::MenuButtonPopup) { + fr.rect.adjust(0, 0, style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this), 0); + } + style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, p, this); + } +} + +/** + * @brief 绘制icon + * @param p + * @param iconRect + * @param opt icon信息从QStyleOptionToolButton获取 + */ +void SAColorToolButton::paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt) +{ + if (!iconRect.isNull()) { + QPixmap pm = d_ptr->createIconPixmap(opt, iconRect); + style()->drawItemPixmap(p, iconRect, Qt::AlignCenter, pm); + } +} + +/** + * @brief 绘制文本 + * @param p + * @param textRect + * @param opt + */ +void SAColorToolButton::paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt) +{ + if (opt.text.isEmpty()) { + return; + } + if (Qt::ToolButtonIconOnly == opt.toolButtonStyle) { + return; + } + p->save(); + p->setFont(opt.font); + int alignment = Qt::TextShowMnemonic; + // 快捷键的下划线 + if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { + alignment |= Qt::TextHideMnemonic; + } + alignment |= Qt::AlignHCenter | Qt::AlignVCenter; + style()->drawItemText(p, + QStyle::visualRect(opt.direction, opt.rect, textRect), + alignment, + opt.palette, + opt.state & QStyle::State_Enabled, + opt.text, + QPalette::ButtonText); + p->restore(); +} + +/** + * @brief 绘制color + * @param p + * @param iconRect + * @param opt + */ +void SAColorToolButton::paintColor(QStylePainter* p, const QRect& colorRect, const QColor& color, const QStyleOptionToolButton& opt) +{ + Q_UNUSED(opt); + if (colorRect.isNull()) { + return; + } + // 绘制颜色 + if (color.isValid()) { + p->fillRect(colorRect, color); + } else { + paintNoneColor(p, colorRect); + } +} + +/*** End of inlined file: SAColorToolButton.cpp ***/ + +// sa ribbon + +/*** Start of inlined file: SAFramelessHelper.cpp ***/ +#include +#include +#include +#include +#include +#include + +class SAPrivateFramelessWidgetData; + +/***** + * FramelessHelperPrivate + * 存储界面对应的数据集合,以及是否可移动、可缩放属性 + *****/ +class SAFramelessHelper::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SAFramelessHelper) + friend class SAPrivateFramelessWidgetData; + +public: + PrivateData(SAFramelessHelper* p); + QHash< QWidget*, SAPrivateFramelessWidgetData* > m_widgetDataHash; + bool m_bWidgetMovable { true }; + bool m_bWidgetResizable { true }; + bool m_bRubberBandOnResize { true }; + bool m_bRubberBandOnMove { true }; +}; + +SAFramelessHelper::PrivateData::PrivateData(SAFramelessHelper* p) : q_ptr(p) +{ +} + +/***** + * CursorPosCalculator + * 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角 + *****/ +class SAPrivateFramelessCursorPosCalculator +{ +public: + explicit SAPrivateFramelessCursorPosCalculator(); + void reset(); + void recalculate(const QPoint& globalMousePos, const QRect& frameRect); + +public: + bool m_bOnEdges { true }; + bool m_bOnLeftEdge { true }; + bool m_bOnRightEdge { true }; + bool m_bOnTopEdge { true }; + bool m_bOnBottomEdge { true }; + bool m_bOnTopLeftEdge { true }; + bool m_bOnBottomLeftEdge { true }; + bool m_bOnTopRightEdge { true }; + bool m_bOnBottomRightEdge { true }; + + static int m_nBorderWidth; + static int m_nTitleHeight; +}; + +int SAPrivateFramelessCursorPosCalculator::m_nBorderWidth = 5; +int SAPrivateFramelessCursorPosCalculator::m_nTitleHeight = 30; + +/***** CursorPosCalculator *****/ +SAPrivateFramelessCursorPosCalculator::SAPrivateFramelessCursorPosCalculator() +{ + reset(); +} + +void SAPrivateFramelessCursorPosCalculator::reset() +{ + m_bOnEdges = false; + m_bOnLeftEdge = false; + m_bOnRightEdge = false; + m_bOnTopEdge = false; + m_bOnBottomEdge = false; + m_bOnTopLeftEdge = false; + m_bOnBottomLeftEdge = false; + m_bOnTopRightEdge = false; + m_bOnBottomRightEdge = false; +} + +void SAPrivateFramelessCursorPosCalculator::recalculate(const QPoint& gMousePos, const QRect& frameRect) +{ + int globalMouseX = gMousePos.x(); + int globalMouseY = gMousePos.y(); + + int frameX = frameRect.x(); + int frameY = frameRect.y(); + + int frameWidth = frameRect.width(); + int frameHeight = frameRect.height(); + + m_bOnLeftEdge = (globalMouseX >= frameX && globalMouseX <= frameX + m_nBorderWidth); + + m_bOnRightEdge = (globalMouseX >= frameX + frameWidth - m_nBorderWidth && globalMouseX <= frameX + frameWidth); + + m_bOnTopEdge = (globalMouseY >= frameY && globalMouseY <= frameY + m_nBorderWidth); + + m_bOnBottomEdge = (globalMouseY >= frameY + frameHeight - m_nBorderWidth && globalMouseY <= frameY + frameHeight); + + m_bOnTopLeftEdge = m_bOnTopEdge && m_bOnLeftEdge; + m_bOnBottomLeftEdge = m_bOnBottomEdge && m_bOnLeftEdge; + m_bOnTopRightEdge = m_bOnTopEdge && m_bOnRightEdge; + m_bOnBottomRightEdge = m_bOnBottomEdge && m_bOnRightEdge; + + m_bOnEdges = m_bOnLeftEdge || m_bOnRightEdge || m_bOnTopEdge || m_bOnBottomEdge; +} + +/***** + * WidgetData + * 更新鼠标样式、移动窗体、缩放窗体 + *****/ +class SAPrivateFramelessWidgetData +{ +public: + explicit SAPrivateFramelessWidgetData(SAFramelessHelper::PrivateData* pd, QWidget* pTopLevelWidget); + ~SAPrivateFramelessWidgetData(); + QWidget* widget(); + + // 处理鼠标事件-划过、按下、释放、移动 + bool handleWidgetEvent(QEvent* event); + + // 更新橡皮筋状态 + void updateRubberBandStatus(); + +private: + // 更新鼠标样式 + void updateCursorShape(const QPoint& gMousePos); + + // 重置窗体大小 + void resizeWidget(const QPoint& gMousePos); + + // 移动窗体 + void moveWidget(const QPoint& gMousePos); + + // 处理鼠标按下 + bool handleMousePressEvent(QMouseEvent* event); + + // 处理鼠标释放 + bool handleMouseReleaseEvent(QMouseEvent* event); + + // 处理鼠标移动 + bool handleMouseMoveEvent(QMouseEvent* event); + + // 处理鼠标离开 + bool handleLeaveEvent(QEvent* event); + + // 处理鼠标进入 + bool handleHoverMoveEvent(QHoverEvent* event); + + // 处理鼠标双击事件 + bool handleDoubleClickedMouseEvent(QMouseEvent* event); + +private: + SAFramelessHelper::PrivateData* d; + QRubberBand* m_pRubberBand; + QWidget* m_pWidget; + QPoint m_ptDragPos; + SAPrivateFramelessCursorPosCalculator m_pressedMousePos; + SAPrivateFramelessCursorPosCalculator m_moveMousePos; + bool m_bLeftButtonPressed; + bool m_bCursorShapeChanged; + bool m_bLeftButtonTitlePressed; + Qt::WindowFlags m_windowFlags; +}; + +/***** WidgetData *****/ +SAPrivateFramelessWidgetData::SAPrivateFramelessWidgetData(SAFramelessHelper::PrivateData* pd, QWidget* pTopLevelWidget) +{ + d = pd; + m_pWidget = pTopLevelWidget; + m_bLeftButtonPressed = false; + m_bCursorShapeChanged = false; + m_bLeftButtonTitlePressed = false; + m_pRubberBand = NULL; + + m_windowFlags = m_pWidget->windowFlags(); + m_pWidget->setMouseTracking(true); + m_pWidget->setAttribute(Qt::WA_Hover, true); + + updateRubberBandStatus(); +} + +SAPrivateFramelessWidgetData::~SAPrivateFramelessWidgetData() +{ + m_pWidget->setMouseTracking(false); + m_pWidget->setWindowFlags(m_windowFlags); + m_pWidget->setAttribute(Qt::WA_Hover, false); + + delete m_pRubberBand; + m_pRubberBand = NULL; +} + +QWidget* SAPrivateFramelessWidgetData::widget() +{ + return (m_pWidget); +} + +bool SAPrivateFramelessWidgetData::handleWidgetEvent(QEvent* event) +{ + switch (event->type()) { + case QEvent::MouseButtonPress: + return (handleMousePressEvent(static_cast< QMouseEvent* >(event))); + + case QEvent::MouseButtonRelease: + return (handleMouseReleaseEvent(static_cast< QMouseEvent* >(event))); + + case QEvent::MouseMove: + return (handleMouseMoveEvent(static_cast< QMouseEvent* >(event))); + + case QEvent::Leave: + return (handleLeaveEvent(static_cast< QMouseEvent* >(event))); + + case QEvent::HoverMove: + return (handleHoverMoveEvent(static_cast< QHoverEvent* >(event))); + + case QEvent::MouseButtonDblClick: + return (handleDoubleClickedMouseEvent(static_cast< QMouseEvent* >(event))); + + default: + break; + } + return (false); +} + +void SAPrivateFramelessWidgetData::updateRubberBandStatus() +{ + if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize) { + if (NULL == m_pRubberBand) { + m_pRubberBand = new QRubberBand(QRubberBand::Rectangle); + } + } else { + delete m_pRubberBand; + m_pRubberBand = NULL; + } +} + +void SAPrivateFramelessWidgetData::updateCursorShape(const QPoint& gMousePos) +{ + if (m_pWidget->isFullScreen() || m_pWidget->isMaximized()) { + if (m_bCursorShapeChanged) { + m_pWidget->unsetCursor(); + } + return; + } + + m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry()); + + if (m_moveMousePos.m_bOnTopLeftEdge || m_moveMousePos.m_bOnBottomRightEdge) { + m_pWidget->setCursor(Qt::SizeFDiagCursor); + m_bCursorShapeChanged = true; + } else if (m_moveMousePos.m_bOnTopRightEdge || m_moveMousePos.m_bOnBottomLeftEdge) { + m_pWidget->setCursor(Qt::SizeBDiagCursor); + m_bCursorShapeChanged = true; + } else if (m_moveMousePos.m_bOnLeftEdge || m_moveMousePos.m_bOnRightEdge) { + m_pWidget->setCursor(Qt::SizeHorCursor); + m_bCursorShapeChanged = true; + } else if (m_moveMousePos.m_bOnTopEdge || m_moveMousePos.m_bOnBottomEdge) { + m_pWidget->setCursor(Qt::SizeVerCursor); + m_bCursorShapeChanged = true; + } else { + if (m_bCursorShapeChanged) { + m_pWidget->unsetCursor(); + m_bCursorShapeChanged = false; + } + } +} + +void SAPrivateFramelessWidgetData::resizeWidget(const QPoint& gMousePos) +{ + QRect origRect; + + if (d->m_bRubberBandOnResize) { + origRect = m_pRubberBand->frameGeometry(); + } else { + origRect = m_pWidget->frameGeometry(); + } + + int left = origRect.left(); + int top = origRect.top(); + int right = origRect.right(); + int bottom = origRect.bottom(); + + origRect.getCoords(&left, &top, &right, &bottom); + + int minWidth = m_pWidget->minimumWidth(); + int minHeight = m_pWidget->minimumHeight(); + + if (m_pressedMousePos.m_bOnTopLeftEdge) { + left = gMousePos.x(); + top = gMousePos.y(); + } else if (m_pressedMousePos.m_bOnBottomLeftEdge) { + left = gMousePos.x(); + bottom = gMousePos.y(); + } else if (m_pressedMousePos.m_bOnTopRightEdge) { + right = gMousePos.x(); + top = gMousePos.y(); + } else if (m_pressedMousePos.m_bOnBottomRightEdge) { + right = gMousePos.x(); + bottom = gMousePos.y(); + } else if (m_pressedMousePos.m_bOnLeftEdge) { + left = gMousePos.x(); + } else if (m_pressedMousePos.m_bOnRightEdge) { + right = gMousePos.x(); + } else if (m_pressedMousePos.m_bOnTopEdge) { + top = gMousePos.y(); + } else if (m_pressedMousePos.m_bOnBottomEdge) { + bottom = gMousePos.y(); + } + + QRect newRect(QPoint(left, top), QPoint(right, bottom)); + + if (newRect.isValid()) { + if (minWidth > newRect.width()) { + if (left != origRect.left()) { + newRect.setLeft(origRect.left()); + } else { + newRect.setRight(origRect.right()); + } + } + if (minHeight > newRect.height()) { + if (top != origRect.top()) { + newRect.setTop(origRect.top()); + } else { + newRect.setBottom(origRect.bottom()); + } + } + + if (d->m_bRubberBandOnResize) { + m_pRubberBand->setGeometry(newRect); + } else { + m_pWidget->setGeometry(newRect); + } + } +} + +void SAPrivateFramelessWidgetData::moveWidget(const QPoint& gMousePos) +{ + if (d->m_bRubberBandOnMove) { + m_pRubberBand->move(gMousePos - m_ptDragPos); + } else { + m_pWidget->move(gMousePos - m_ptDragPos); + } +} + +bool SAPrivateFramelessWidgetData::handleMousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) { + m_bLeftButtonPressed = true; + m_bLeftButtonTitlePressed = event->pos().y() < m_moveMousePos.m_nTitleHeight; + + QRect frameRect = m_pWidget->frameGeometry(); + auto gp = SA_MOUSEEVENT_GLOBALPOS_POINT(event); + m_pressedMousePos.recalculate(gp, frameRect); + + m_ptDragPos = gp - frameRect.topLeft(); + + if (m_pressedMousePos.m_bOnEdges) { + if (m_pWidget->isMaximized()) { + // 窗口在最大化状态时,点击边界不做任何处理 + return (false); + } + if (d->m_bRubberBandOnResize) { + m_pRubberBand->setGeometry(frameRect); + m_pRubberBand->show(); + return (true); + } + } else if (d->m_bRubberBandOnMove && m_bLeftButtonTitlePressed) { + if (m_pWidget->isMaximized()) { + // 窗口在最大化状态时,点击标题栏不做任何处理 + return (false); + } + m_pRubberBand->setGeometry(frameRect); + m_pRubberBand->show(); + return (true); + } + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleMouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) { + m_bLeftButtonPressed = false; + m_bLeftButtonTitlePressed = false; + m_pressedMousePos.reset(); + if (m_pRubberBand && m_pRubberBand->isVisible()) { + m_pRubberBand->hide(); + m_pWidget->setGeometry(m_pRubberBand->geometry()); + return (true); + } + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleMouseMoveEvent(QMouseEvent* event) +{ + QPoint p = SA_MOUSEEVENT_GLOBALPOS_POINT(event); + if (m_bLeftButtonPressed) { + if (d->m_bWidgetResizable && m_pressedMousePos.m_bOnEdges) { + if (m_pWidget->isMaximized()) { + // 窗口在最大化状态时,点击边界不做任何处理 + return (false); + } + resizeWidget(p); + return (true); + } else if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed) { + if (m_pWidget->isMaximized()) { + // 先求出窗口到鼠标的相对位置 + QRect normalGeometry = m_pWidget->normalGeometry(); + m_pWidget->showNormal(); + p.ry() -= 10; + p.rx() -= (normalGeometry.width() / 2); + m_pWidget->move(p); + // 这时要重置m_ptDragPos + m_ptDragPos = QPoint(normalGeometry.width() / 2, 10); + return (true); + } + moveWidget(p); + return (true); + } + return (false); + } else if (d->m_bWidgetResizable) { + updateCursorShape(p); + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleLeaveEvent(QEvent* event) +{ + Q_UNUSED(event) + if (!m_bLeftButtonPressed) { + m_pWidget->unsetCursor(); + return (true); + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleHoverMoveEvent(QHoverEvent* event) +{ + if (d->m_bWidgetResizable) { + updateCursorShape(m_pWidget->mapToGlobal(SA_HOVEREVENT_POS_POINT(event))); + } + return (false); +} + +bool SAPrivateFramelessWidgetData::handleDoubleClickedMouseEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) { + if (m_pWidget) { + SARibbonMainWindow* mainwindow = qobject_cast< SARibbonMainWindow* >(m_pWidget); + if (mainwindow) { + if (mainwindow->windowFlags() & Qt::WindowMaximizeButtonHint) { + // 在最大化按钮显示时才进行shownormal处理 + bool titlePressed = event->pos().y() < m_moveMousePos.m_nTitleHeight; + if (titlePressed) { + if (m_pWidget->isMaximized()) { + m_pWidget->showNormal(); + } else { + m_pWidget->showMaximized(); + } + return (true); + } + } + } + } + } + return (false); +} + +//=================================================== +// SAFramelessHelper +//=================================================== +SAFramelessHelper::SAFramelessHelper(QObject* parent) : QObject(parent), d_ptr(new SAFramelessHelper::PrivateData(this)) +{ + d_ptr->m_bWidgetMovable = true; + d_ptr->m_bWidgetResizable = true; + d_ptr->m_bRubberBandOnResize = false; + d_ptr->m_bRubberBandOnMove = false; + if (parent) { + QWidget* w = qobject_cast< QWidget* >(parent); + if (w) { + w->setWindowFlags(w->windowFlags() | Qt::FramelessWindowHint); + setWidgetMovable(true); // 设置窗体可移动 + setWidgetResizable(true); // 设置窗体可缩放 + setRubberBandOnMove(false); // 设置橡皮筋效果-可移动 + setRubberBandOnResize(true); // 设置橡皮筋效果-可缩放 + activateOn(w); // 激活当前窗体 + } + } +} + +SAFramelessHelper::~SAFramelessHelper() +{ + QList< QWidget* > keys = d_ptr->m_widgetDataHash.keys(); + int size = keys.size(); + + for (int i = 0; i < size; ++i) { + delete d_ptr->m_widgetDataHash.take(keys[ i ]); + } +} + +bool SAFramelessHelper::eventFilter(QObject* obj, QEvent* event) +{ + switch (event->type()) { + case QEvent::MouseMove: + case QEvent::HoverMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::Leave: { + SAPrivateFramelessWidgetData* data = d_ptr->m_widgetDataHash.value(static_cast< QWidget* >(obj)); + if (data) { + return (data->handleWidgetEvent(event)); + } + break; + } + + default: + break; + } + return (QObject::eventFilter(obj, event)); +} + +void SAFramelessHelper::activateOn(QWidget* topLevelWidget) +{ + if (!d_ptr->m_widgetDataHash.contains(topLevelWidget)) { + SAPrivateFramelessWidgetData* data = new SAPrivateFramelessWidgetData(d_ptr.get(), topLevelWidget); + d_ptr->m_widgetDataHash.insert(topLevelWidget, data); + + topLevelWidget->installEventFilter(this); + } +} + +void SAFramelessHelper::removeFrom(QWidget* topLevelWidget) +{ + SAPrivateFramelessWidgetData* data = d_ptr->m_widgetDataHash.take(topLevelWidget); + + if (data) { + topLevelWidget->removeEventFilter(this); + delete data; + } +} + +void SAFramelessHelper::setRubberBandOnMove(bool movable) +{ + d_ptr->m_bRubberBandOnMove = movable; + QList< SAPrivateFramelessWidgetData* > list = d_ptr->m_widgetDataHash.values(); + + foreach (SAPrivateFramelessWidgetData* data, list) { + data->updateRubberBandStatus(); + } +} + +void SAFramelessHelper::setWidgetMovable(bool movable) +{ + d_ptr->m_bWidgetMovable = movable; +} + +void SAFramelessHelper::setWidgetResizable(bool resizable) +{ + d_ptr->m_bWidgetResizable = resizable; +} + +void SAFramelessHelper::setRubberBandOnResize(bool resizable) +{ + d_ptr->m_bRubberBandOnResize = resizable; + QList< SAPrivateFramelessWidgetData* > list = d_ptr->m_widgetDataHash.values(); + + foreach (SAPrivateFramelessWidgetData* data, list) { + data->updateRubberBandStatus(); + } +} + +void SAFramelessHelper::setBorderWidth(int width) +{ + if (width > 0) { + SAPrivateFramelessCursorPosCalculator::m_nBorderWidth = width; + } +} + +void SAFramelessHelper::setTitleHeight(int height) +{ + if (height > 0) { + SAPrivateFramelessCursorPosCalculator::m_nTitleHeight = height; + } +} + +bool SAFramelessHelper::widgetMovable() +{ + return (d_ptr->m_bWidgetMovable); +} + +bool SAFramelessHelper::widgetResizable() +{ + return (d_ptr->m_bWidgetResizable); +} + +bool SAFramelessHelper::rubberBandOnMove() +{ + return (d_ptr->m_bRubberBandOnMove); +} + +bool SAFramelessHelper::rubberBandOnResisze() +{ + return (d_ptr->m_bRubberBandOnResize); +} + +uint SAFramelessHelper::borderWidth() +{ + return (SAPrivateFramelessCursorPosCalculator::m_nBorderWidth); +} + +uint SAFramelessHelper::titleHeight() +{ + return (SAPrivateFramelessCursorPosCalculator::m_nTitleHeight); +} + +/*** End of inlined file: SAFramelessHelper.cpp ***/ + +/*** Start of inlined file: SARibbonApplicationButton.cpp ***/ +SARibbonApplicationButton::SARibbonApplicationButton(QWidget* parent) : QToolButton(parent) +{ + setFocusPolicy(Qt::NoFocus); + setAutoRaise(true); + setPopupMode(QToolButton::InstantPopup); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); +} + +SARibbonApplicationButton::SARibbonApplicationButton(const QString& text, QWidget* parent) : QToolButton(parent) +{ + setFocusPolicy(Qt::NoFocus); + setAutoRaise(true); + setPopupMode(QToolButton::InstantPopup); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setText(text); +} + +SARibbonApplicationButton::SARibbonApplicationButton(const QIcon& icon, const QString& text, QWidget* parent) + : QToolButton(parent) +{ + setFocusPolicy(Qt::NoFocus); + setAutoRaise(true); + setPopupMode(QToolButton::InstantPopup); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setIcon(icon); + setText(text); +} + +/*** End of inlined file: SARibbonApplicationButton.cpp ***/ + +/*** Start of inlined file: SARibbonSystemButtonBar.cpp ***/ +#include +#include +#include +#include +#include + +// 为了避免使用此框架的app设置了全局的qpushbutton 的 qss样式影响此按钮,定义了一个类 + +/** + * @brief The SARibbonSystemButtonBar class + */ +class SARibbonSystemButtonBar::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonSystemButtonBar) +public: + SARibbonSystemToolButton* buttonClose { nullptr }; + SARibbonSystemToolButton* buttonMinimize { nullptr }; + SARibbonSystemToolButton* buttonMaximize { nullptr }; + int mCloseStretch { 4 }; + int mMaxStretch { 3 }; + int mMinStretch { 3 }; + int mWindowButtonWidth { 35 }; + int mTitleBarHeight { 28 }; + Qt::WindowFlags mFlags { Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint }; + SARibbonButtonGroupWidget* mButtonGroup; + +public: + PrivateData(SARibbonSystemButtonBar* p) : q_ptr(p) + { + mButtonGroup = new SARibbonButtonGroupWidget(p); + mButtonGroup->setObjectName("SASystemButtonGroup"); + } + + void setupMinimizeButton(bool on) + { + SARibbonSystemButtonBar* par = q_ptr; + + if (on) { + if (buttonMinimize) { + buttonMinimize->deleteLater(); + buttonMinimize = nullptr; + } + buttonMinimize = new SARibbonSystemToolButton(par); + buttonMinimize->setObjectName(QStringLiteral("SAMinimizeWindowButton")); + buttonMinimize->setFocusPolicy(Qt::NoFocus); // 避免铺抓到 + buttonMinimize->show(); + par->connect(buttonMinimize, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::minimizeWindow); + } else { + if (buttonMinimize) { + delete buttonMinimize; + buttonMinimize = nullptr; + } + } + updateSize(); + } + + void setupMaximizeButton(bool on) + { + SARibbonSystemButtonBar* par = q_ptr; + + if (on) { + if (buttonMaximize) { + buttonMaximize->deleteLater(); + buttonMaximize = nullptr; + } + buttonMaximize = new SARibbonSystemToolButton(par); + buttonMaximize->setObjectName(QStringLiteral("SAMaximizeWindowButton")); + buttonMaximize->setCheckable(true); + buttonMaximize->setFocusPolicy(Qt::NoFocus); // 避免铺抓到 + // buttonMaximize->setIconSize(buttonMaximize->size() * mIconscale); + buttonMaximize->show(); + par->connect(buttonMaximize, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::maximizeWindow); + } else { + if (buttonMaximize) { + delete buttonMaximize; + buttonMaximize = nullptr; + } + } + updateSize(); + } + + void setupCloseButton(bool on) + { + SARibbonSystemButtonBar* par = q_ptr; + + if (on) { + if (buttonClose) { + buttonClose->deleteLater(); + buttonClose = nullptr; + } + buttonClose = new SARibbonSystemToolButton(par); + buttonClose->setObjectName(QStringLiteral("SACloseWindowButton")); + buttonClose->setFocusPolicy(Qt::NoFocus); // 避免铺抓到 + // buttonClose->setFlat(true); + par->connect(buttonClose, &QAbstractButton::clicked, par, &SARibbonSystemButtonBar::closeWindow); + // buttonClose->setIconSize(buttonClose->size() * mIconscale); + buttonClose->show(); + } else { + if (buttonClose) { + delete buttonClose; + buttonClose = nullptr; + } + } + updateSize(); + } + + void updateSize() + { + resizeElement(q_ptr->size()); + } + + void resizeElement(QSize size) + { + int x = size.width(); + if (buttonClose) { + int w = closeButtonWidthHint(); + x -= w; + buttonClose->setGeometry(x, 0, w, size.height()); + } + if (buttonMaximize) { + int w = maxButtonWidthHint(); + x -= w; + buttonMaximize->setGeometry(x, 0, w, size.height()); + } + if (buttonMinimize) { + int w = minButtonWidthHint(); + x -= w; + buttonMinimize->setGeometry(x, 0, w, size.height()); + } + if (mButtonGroup) { + mButtonGroup->setGeometry(0, 0, x, size.height()); + } + } + + int closeButtonWidthHint() const + { + qreal t = mCloseStretch + mMaxStretch + mMinStretch; + return (mCloseStretch * (3 * mWindowButtonWidth)) / t; + } + + int maxButtonWidthHint() const + { + qreal t = mCloseStretch + mMaxStretch + mMinStretch; + return (mMaxStretch * (3 * mWindowButtonWidth)) / t; + } + + int minButtonWidthHint() const + { + qreal t = mCloseStretch + mMaxStretch + mMinStretch; + return (mMinStretch * (3 * mWindowButtonWidth)) / t; + } + + QSize sizeHint() const + { + int height = mTitleBarHeight; + if (height < 20) { + height = 20; + } + QSize res(0, 0); + if (mButtonGroup) { + res = mButtonGroup->sizeHint(); + } + res.setHeight(height); + if (buttonClose) { + res.setWidth(res.width() + closeButtonWidthHint()); + } + if (buttonMaximize) { + res.setWidth(res.width() + maxButtonWidthHint()); + } + if (buttonMinimize) { + res.setWidth(res.width() + minButtonWidthHint()); + } + return res; + } +}; + +//=================================================== +// SARibbonSystemToolButton +//=================================================== +SARibbonSystemToolButton::SARibbonSystemToolButton(QWidget* p) : QToolButton(p) +{ + setAutoRaise(true); +} +//=================================================== +// SARibbonSystemButtonBar +//=================================================== +SARibbonSystemButtonBar::SARibbonSystemButtonBar(QWidget* parent) + : QFrame(parent), d_ptr(new SARibbonSystemButtonBar::PrivateData(this)) +{ + updateWindowFlag(); +} + +/** + * @brief 构造函数,强制使用flags,而不是用parent的flags进行构造 + * @param parent + * @param flags + */ +SARibbonSystemButtonBar::SARibbonSystemButtonBar(QWidget* parent, Qt::WindowFlags flags) + : QFrame(parent), d_ptr(new SARibbonSystemButtonBar::PrivateData(this)) +{ + d_ptr->mFlags = flags; + updateWindowFlag(); +} + +SARibbonSystemButtonBar::~SARibbonSystemButtonBar() +{ +} + +void SARibbonSystemButtonBar::setupMinimizeButton(bool on) +{ + d_ptr->setupMinimizeButton(on); +} + +void SARibbonSystemButtonBar::setupMaximizeButton(bool on) +{ + d_ptr->setupMaximizeButton(on); +} + +void SARibbonSystemButtonBar::setupCloseButton(bool on) +{ + d_ptr->setupCloseButton(on); +} + +void SARibbonSystemButtonBar::updateWindowFlag() +{ + QWidget* topedWidget = this; + // 找到最顶层窗口 + while (topedWidget->parentWidget()) { + topedWidget = topedWidget->parentWidget(); + } + Qt::WindowFlags flags = topedWidget->windowFlags(); + updateWindowFlag(flags); +} + +/** + * @brief 此函数仅用于控制最小最大化和关闭按钮的显示 + * @param flags + */ +void SARibbonSystemButtonBar::updateWindowFlag(Qt::WindowFlags flags) +{ + d_ptr->mFlags = flags; + setupMinimizeButton(flags & Qt::WindowMinimizeButtonHint); + setupMaximizeButton(flags & Qt::WindowMaximizeButtonHint); + setupCloseButton(flags & Qt::WindowCloseButtonHint); +} + +/** + * @brief 设置按钮的宽度比例,最终按钮宽度将按照此比例进行设置 + * @param close 关闭按钮比例 + * @param max 最大化按钮比例 + * @param min 最小化按钮比例 + */ +void SARibbonSystemButtonBar::setButtonWidthStretch(int close, int max, int min) +{ + d_ptr->mMaxStretch = max; + d_ptr->mMinStretch = min; + d_ptr->mCloseStretch = close; +} + +/** + * @brief 标题栏高度 + * + * 标题栏高度会影响sizeHint + * @param h + */ +void SARibbonSystemButtonBar::setWindowTitleHeight(int h) +{ + d_ptr->mTitleBarHeight = h; +} + +/** + * @brief 标题栏高度 + * @return + */ +int SARibbonSystemButtonBar::windowTitleHeight() const +{ + return d_ptr->mTitleBarHeight; +} + +/** + * @brief 系统按钮的宽度 + * @param w + */ +void SARibbonSystemButtonBar::setWindowButtonWidth(int w) +{ + d_ptr->mWindowButtonWidth = w; +} + +/** + * @brief 系统按钮的宽度 + * @param w + */ +int SARibbonSystemButtonBar::windowButtonWidth() const +{ + return d_ptr->mWindowButtonWidth; +} + +/** + * @brief 设置窗口状态(最大最小化按钮状态) + * @param s + */ +void SARibbonSystemButtonBar::setWindowStates(Qt::WindowStates s) +{ + if (d_ptr->buttonMaximize) { + bool on = s.testFlag(Qt::WindowMaximized); + d_ptr->buttonMaximize->setChecked(on); + d_ptr->buttonMaximize->setToolTip(on ? tr("Restore") : tr("Maximize")); + } +} + +/** + * @brief 此函数返回的flags仅包括 Qt::WindowCloseButtonHint,Qt::WindowMaximizeButtonHint,Qt::WindowMinimizeButtonHint + * 三个 + * + * @return + */ +Qt::WindowFlags SARibbonSystemButtonBar::windowButtonFlags() const +{ + Qt::WindowFlags f = Qt::Widget; // widget是000 + + if (d_ptr->mFlags & Qt::WindowCloseButtonHint) { + f |= Qt::WindowCloseButtonHint; + } + if (d_ptr->mFlags & Qt::WindowMaximizeButtonHint) { + f |= Qt::WindowMaximizeButtonHint; + } + if (d_ptr->mFlags & Qt::WindowMinimizeButtonHint) { + f |= Qt::WindowMinimizeButtonHint; + } + + return (f); +} + +QSize SARibbonSystemButtonBar::sizeHint() const +{ + return (d_ptr->sizeHint()); +} + +QAbstractButton* SARibbonSystemButtonBar::minimizeButton() const +{ + return d_ptr->buttonMinimize; +} + +QAbstractButton* SARibbonSystemButtonBar::maximizeButton() const +{ + return d_ptr->buttonMaximize; +} + +QAbstractButton* SARibbonSystemButtonBar::closeButton() const +{ + return d_ptr->buttonClose; +} + +void SARibbonSystemButtonBar::setIconSize(const QSize& ic) +{ + d_ptr->mButtonGroup->setIconSize(ic); +} + +QSize SARibbonSystemButtonBar::iconSize() const +{ + return d_ptr->mButtonGroup->iconSize(); +} + +QAction* SARibbonSystemButtonBar::addAction(QAction* a, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + return d_ptr->mButtonGroup->addAction(a, buttonStyle, popMode); +} + +QAction* SARibbonSystemButtonBar::addAction(const QString& text, const QIcon& icon, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + return d_ptr->mButtonGroup->addAction(text, icon, buttonStyle, popMode); +} + +QAction* SARibbonSystemButtonBar::addMenu(QMenu* menu, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + return d_ptr->mButtonGroup->addMenu(menu, buttonStyle, popMode); +} + +QAction* SARibbonSystemButtonBar::addSeparator() +{ + return d_ptr->mButtonGroup->addSeparator(); +} + +QAction* SARibbonSystemButtonBar::addWidget(QWidget* w) +{ + return d_ptr->mButtonGroup->addWidget(w); +} + +void SARibbonSystemButtonBar::resizeEvent(QResizeEvent* e) +{ + Q_UNUSED(e); + d_ptr->resizeElement(size()); +} + +void SARibbonSystemButtonBar::closeWindow() +{ + if (parentWidget()) { + parentWidget()->close(); + } +} + +void SARibbonSystemButtonBar::minimizeWindow() +{ + if (parentWidget()) { + parentWidget()->showMinimized(); + } +} + +void SARibbonSystemButtonBar::maximizeWindow() +{ + QWidget* par = parentWidget(); + + if (par) { + if (par->isMaximized()) { + par->showNormal(); + } else { + par->showMaximized(); + } + } +} + +/*** End of inlined file: SARibbonSystemButtonBar.cpp ***/ + +/*** Start of inlined file: SARibbonToolButton.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @def 定义文字换行时2行文本的矩形高度系数,此系数决定文字区域的高度 + * + * fontMetrics.lineSpacing*系数 = 文本区域高度 + */ +#define SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR 2.05 + +/** + * @def 定义文字不换行时单行文本的矩形高度系数,此系数决定文字区域的高度 + * + * fontMetrics.lineSpacing*系数 = 文本区域高度 + */ +#define SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR 1.2 + +/** + * @def 定义小按钮的矩形高度系数,此系数决定文字区域的高度 + * + * fontMetrics.lineSpacing*系数 = 文本区域高度 + */ +#define SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR 1.4 + +/** + * @def 文本宽度估算时的宽度比高度系数 + * + * 超过此系数的宽度时,开始尝试换行,例如按钮高度为h,如果单行文本的宽度大于h*系数,则触发换行估算 + */ +#define SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO 1.4 + +/** + * @def 开启此宏会打印一些常见信息 + */ +#define SA_RIBBON_TOOLBUTTON_DEBUG_PRINT 0 + +#define SARIBBONTOOLBUTTON_DEBUG_DRAW 0 + +#if SARIBBONTOOLBUTTON_DEBUG_DRAW +#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + p.setPen(Qt::red); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) + +#else +#define SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, rect) +#endif +namespace SA +{ + +QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt) +{ + debug << "==============" + << "\nQStyleOption(" << (QStyleOption)opt << ")" + << "\n QStyleOptionComplex:" + "\n subControls(" + << opt.subControls + << " ) " + "\n activeSubControls(" + << opt.activeSubControls + << "\n QStyleOptionToolButton" + "\n features(" + << opt.features + << ")" + "\n toolButtonStyle(" + << opt.toolButtonStyle << ")"; + + return (debug); +} +} + +//=================================================== +// SARibbonToolButtonProxyStyle +//=================================================== + +class SARibbonToolButtonProxyStyle : public QProxyStyle +{ +public: + void drawPrimitive(PrimitiveElement pe, const QStyleOption* opt, QPainter* p, const QWidget* widget = nullptr) const override + { + if (pe == PE_IndicatorArrowUp || pe == PE_IndicatorArrowDown || pe == PE_IndicatorArrowRight || pe == PE_IndicatorArrowLeft) { + if (opt->rect.width() <= 1 || opt->rect.height() <= 1) + return; + + QRect r = opt->rect; + int size = qMin(r.height(), r.width()); + QPixmap pixmap; + qreal pixelRatio = p->device()->devicePixelRatio(); + int border = qRound(pixelRatio * (size / 4)); + int sqsize = qRound(pixelRatio * (2 * (size / 2))); + QImage image(sqsize, sqsize, QImage::Format_ARGB32_Premultiplied); + image.fill(Qt::transparent); + QPainter imagePainter(&image); + + QPolygon a; + switch (pe) { + case PE_IndicatorArrowUp: + a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize - border, sqsize / 2); + break; + case PE_IndicatorArrowDown: + a.setPoints(3, border, sqsize / 2, sqsize / 2, sqsize - border, sqsize - border, sqsize / 2); + break; + case PE_IndicatorArrowRight: + a.setPoints(3, sqsize - border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); + break; + case PE_IndicatorArrowLeft: + a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border); + break; + default: + break; + } + + int bsx = 0; + int bsy = 0; + + if (opt->state & State_Sunken) { + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget); + } + + QRect bounds = a.boundingRect(); + int sx = sqsize / 2 - bounds.center().x() - 1; + int sy = sqsize / 2 - bounds.center().y() - 1; + imagePainter.translate(sx + bsx, sy + bsy); + imagePainter.setPen(QPen(opt->palette.buttonText().color(), 1.4)); + imagePainter.setBrush(Qt::NoBrush); + + if (!(opt->state & State_Enabled)) { + imagePainter.translate(1, 1); + imagePainter.setPen(QPen(opt->palette.light().color(), 1.4)); + imagePainter.drawPolyline(a); + imagePainter.translate(-1, -1); + imagePainter.setPen(QPen(opt->palette.mid().color(), 1.4)); + } + + imagePainter.drawPolyline(a); + imagePainter.end(); + pixmap = QPixmap::fromImage(image); + pixmap.setDevicePixelRatio(pixelRatio); + + int xOffset = r.x() + (r.width() - size) / 2; + int yOffset = r.y() + (r.height() - size) / 2; + p->drawPixmap(xOffset, yOffset, pixmap); + } else { + QProxyStyle::drawPrimitive(pe, opt, p, widget); + } + } +}; + +//=================================================== +// SARibbonToolButton::PrivateData +//=================================================== + +class SARibbonToolButton::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonToolButton) +public: + PrivateData(SARibbonToolButton* p); + // 根据鼠标位置更新按钮的信息 + void updateStatusByMousePosition(const QPoint& pos); + // 更新绘图相关的尺寸 + void updateDrawRect(const QStyleOptionToolButton& opt); + // 更新SizeHint + void updateSizeHint(const QStyleOptionToolButton& opt); + // 计算涉及到的rect尺寸 + void calcDrawRects(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& indicatorArrowRect, int spacing, int indicatorLen) const; + // 计算小按钮模式下的尺寸 + void calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const; + // 计算大按钮模式下的尺寸 + void calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const; + // 根据按钮的尺寸调节iconsize(注意这里的buttonRect是已经减去mSpacing的情况) + QSize adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const; + // 判断是否有Indicator + bool hasIndicator(const QStyleOptionToolButton& opt) const; + // 计算sizehint + QSize calcSizeHint(const QStyleOptionToolButton& opt); + QSize calcSmallButtonSizeHint(const QStyleOptionToolButton& opt); + QSize calcLargeButtonSizeHint(const QStyleOptionToolButton& opt); + + // 计算文本绘制矩形的高度 + int calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const; + // 估算一个最优的文本宽度 + int estimateLargeButtonTextWidth(int buttonHeight, + int textDrawRectHeight, + const QString& text, + const QFontMetrics& fm, + float widthHeightRatio = SARIBBONTOOLBUTTON_WORDWRAP_WIDTH_PER_HEIGHT_RATIO, + int maxTrycount = 3); + QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; + // 获取文字的对其方式 + int getTextAlignment() const; + // 确认文字是否确切要换行显示 + bool isTextNeedWrap() const; + // 仅仅对\n进行剔除,和QString::simplified不一样 + static QString simplified(const QString& str); + +public: + bool mMouseOnSubControl { false }; ///< 这个用于标记MenuButtonPopup模式下,鼠标在文本区域 + bool mMenuButtonPressed { false }; ///< 由于Indicator改变,因此hitButton不能用QToolButton的hitButton + bool mWordWrap { false }; ///< 标记是否文字换行 @default false + SARibbonToolButton::RibbonButtonType mButtonType { SARibbonToolButton::LargeButton }; + int mSpacing { 1 }; ///< 按钮和边框的距离 + int mIndicatorLen { 8 }; ///< Indicator的长度 + QRect mDrawIconRect; ///< 记录icon的绘制位置 + QRect mDrawTextRect; ///< 记录text的绘制位置 + QRect mDrawIndicatorArrowRect; ///< 记录IndicatorArrow的绘制位置 + QSize mSizeHint; ///< 保存计算好的sizehint + bool mIsTextNeedWrap { false }; ///< 标记文字是否需要换行显示 +public: + static bool s_enableWordWrap; ///< 在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 +}; + +// 静态参数初始化 +bool SARibbonToolButton::PrivateData::s_enableWordWrap = false; + +SARibbonToolButton::PrivateData::PrivateData(SARibbonToolButton* p) : q_ptr(p) +{ + auto proxy = new SARibbonToolButtonProxyStyle(); + proxy->setParent(p); // take ownership to avoid memleak + p->setStyle(proxy); +} + +/** + * @brief 根据鼠标的位置更新状态,主要用于判断鼠标是否位于subcontrol + * + * 此函数主要应用在action menu模式下 + * @param pos + */ +void SARibbonToolButton::PrivateData::updateStatusByMousePosition(const QPoint& pos) +{ + bool isMouseOnSubControl(false); + if (SARibbonToolButton::LargeButton == mButtonType) { + isMouseOnSubControl = mDrawTextRect.united(mDrawIndicatorArrowRect).contains(pos); + } else { + // 小按钮模式就和普通toolbutton一样 + isMouseOnSubControl = mDrawIndicatorArrowRect.contains(pos); + } + + if (mMouseOnSubControl != isMouseOnSubControl) { + mMouseOnSubControl = isMouseOnSubControl; + // 从icon变到text过程中刷新一次 + q_ptr->update(); + } +} + +/** + * @brief 更新绘图的几个关键尺寸 + * + * 包括: + * + * - DrawIconRect 绘制图标的矩形区域 + * + * - DrawTextRect 绘制文本的矩形区域 + * + * - DrawIndicatorArrowRect 绘制菜单下箭头的矩形区域 + * + * @param opt + */ +void SARibbonToolButton::PrivateData::updateDrawRect(const QStyleOptionToolButton& opt) +{ + if (!mSizeHint.isValid()) { + updateSizeHint(opt); + } + // 先更新IndicatorLen + mIndicatorLen = q_ptr->style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, q_ptr); + if (mIndicatorLen < 3) { + if (SARibbonToolButton::LargeButton == mButtonType) { + mIndicatorLen = 8; + } else { + mIndicatorLen = 12; // 小按钮模式下设置为10 + } + } + calcDrawRects(opt, mDrawIconRect, mDrawTextRect, mDrawIndicatorArrowRect, mSpacing, mIndicatorLen); +} + +/** + * @brief 更新sizehint + * @param opt + */ +void SARibbonToolButton::PrivateData::updateSizeHint(const QStyleOptionToolButton& opt) +{ + mSizeHint = calcSizeHint(opt); +} + +/** + * @brief 计算绘图的几个关键区域 + * @param opt + * @param iconRect 绘制图标的矩形区域 + * @param textRect 绘制文本的矩形区域 + * @param indicatorArrowRect 绘制菜单下箭头的矩形区域 + * @param spacing + * @param indicatorLen + */ +void SARibbonToolButton::PrivateData::calcDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const +{ + if (SARibbonToolButton::LargeButton == mButtonType) { + calcLargeButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); + + } else { + calcSmallButtonDrawRects(opt, iconRect, textRect, indicatorArrowRect, spacing, indicatorLen); + } +} + +/** + * @brief 计算小按钮模式下的绘制尺寸 + * @param opt + * @param iconRect + * @param textRect + * @param indicatorArrowRect + * @param spacing + * @param indicatorLen + */ +void SARibbonToolButton::PrivateData::calcSmallButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const +{ + switch (opt.toolButtonStyle) { + case Qt::ToolButtonIconOnly: { + if (hasIndicator(opt)) { + // 在仅有图标的小模式显示时,预留一个下拉箭头位置 + iconRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); + indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, iconRect.y(), indicatorLen, iconRect.height()); + } else { + iconRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); + indicatorArrowRect = QRect(); + } + // 文本区域为空 + textRect = QRect(); + } break; + case Qt::ToolButtonTextOnly: { + if (hasIndicator(opt)) { + // 在仅有图标的小模式显示时,预留一个下拉箭头位置 + textRect = opt.rect.adjusted(spacing, spacing, -indicatorLen - spacing, -spacing); + indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, spacing, indicatorLen, textRect.height()); + } else { + textRect = opt.rect.adjusted(spacing, spacing, -spacing, -spacing); + indicatorArrowRect = QRect(); + } + // 绘图区域为空 + iconRect = QRect(); + } break; + default: { + bool hasInd = hasIndicator(opt); + // icon Beside和under都是一样的 + QRect buttonRect = q_ptr->rect(); + buttonRect.adjust(spacing, spacing, -spacing, -spacing); + // 先设置IconRect + if (opt.icon.isNull()) { + // 没有图标 + iconRect = QRect(); + } else { + QSize iconSize = adjustIconSize(buttonRect, opt.iconSize); + iconRect = QRect(buttonRect.x(), buttonRect.y(), iconSize.width(), qMax(iconSize.height(), buttonRect.height())); + } + // 后设置TextRect + if (opt.text.isEmpty()) { + textRect = QRect(); + } else { + // 分有菜单和没菜单两种情况 + int adjx = iconRect.isValid() ? (iconRect.width() + spacing) : 0; // 在buttonRect上变换,因此如果没有图标是不用偏移spacing + if (hasInd) { + textRect = buttonRect.adjusted(adjx, 0, -indicatorLen, 0); + } else { + textRect = buttonRect.adjusted(adjx, 0, 0, 0); // 在buttonRect上变换,因此如果没有图标是不用偏移spacing + } + } + // 最后设置Indicator + if (hasInd) { + if (textRect.isValid()) { + indicatorArrowRect = QRect(buttonRect.right() - indicatorLen + 1, textRect.y(), indicatorLen, textRect.height()); + } else if (iconRect.isValid()) { + indicatorArrowRect = QRect(buttonRect.right() - indicatorLen + 1, iconRect.y(), indicatorLen, iconRect.height()); + } else { + indicatorArrowRect = buttonRect; + } + } else { + indicatorArrowRect = QRect(); + } + } + } +} + +/** + * @brief 计算大按钮模式下的绘制尺寸(普通) + * @param opt + * @param iconRect + * @param textRect + * @param indicatorArrowRect + * @param spacing + * @param indicatorLen + */ +void SARibbonToolButton::PrivateData::calcLargeButtonDrawRects(const QStyleOptionToolButton& opt, + QRect& iconRect, + QRect& textRect, + QRect& indicatorArrowRect, + int spacing, + int indicatorLen) const +{ + //! 3行模式的图标比较大,文字换行情况下,indicator会动态调整 + // 先获取文字矩形的高度 + int textHeight = calcTextDrawRectHeight(opt); + bool hIndicator = hasIndicator(opt); + if (!hIndicator) { + // 没有菜单,把len设置为0 + indicatorLen = 0; + indicatorArrowRect = QRect(); + } + // 这里要判断文字是否要换行显示,换行显示的文字的indicatorArrowRect所处的位置不一样 + // 先布置textRect + if (isEnableWordWrap()) { + // 在换行模式下 + if (isTextNeedWrap()) { + // 如果文字的确换行,indicator放在最右边 + textRect = QRect(spacing, opt.rect.bottom() - spacing - textHeight, opt.rect.width() - 2 * spacing - indicatorLen, textHeight); + if (hIndicator) { + indicatorArrowRect = QRect(textRect.right(), textRect.y() + textRect.height() / 2, indicatorLen, textHeight / 2); + } + } else { + // 如果文字不需要换行,indicator在下板行 + textRect = QRect(spacing, opt.rect.bottom() - spacing - textHeight, opt.rect.width() - 2 * spacing, textHeight); + if (hIndicator) { + int dy = textRect.height() / 2; + dy += (dy - indicatorLen) / 2; + indicatorArrowRect = QRect(textRect.x(), textRect.y() + dy, textRect.width(), indicatorLen); + } + } + } else { + // 文字不换行,indicator放在最右边 + int y = opt.rect.bottom() - spacing - textHeight; + if (hIndicator) { + // 先布置indicator + indicatorArrowRect = QRect(opt.rect.right() - indicatorLen - spacing, y, indicatorLen, textHeight); + textRect = QRect(spacing, y, indicatorArrowRect.x() - spacing, textHeight); + } else { + textRect = QRect(spacing, y, opt.rect.width() - 2 * spacing, textHeight); + } + } + // 剩下就是icon区域 + iconRect = QRect(spacing, spacing, opt.rect.width() - 2 * spacing, textRect.top() - 2 * spacing); +} + +/** + * @brief 适应iconsize + * @param buttonRect + * @param originIconSize + * @return + */ +QSize SARibbonToolButton::PrivateData::adjustIconSize(const QRect& buttonRect, const QSize& originIconSize) const +{ + QSize iconSize = originIconSize; + if (iconSize.height() > buttonRect.height()) { + // 说明图标的icon过大,要匹配到buttonRect + iconSize.setHeight(buttonRect.height()); + // 等比例设置宽度 + iconSize.setWidth(originIconSize.width() * iconSize.height() / originIconSize.height()); + } + return iconSize; +} + +/** + * @brief 判断是否有Indicator + * @param opt + * @return + */ +bool SARibbonToolButton::PrivateData::hasIndicator(const QStyleOptionToolButton& opt) const +{ + return ((opt.features & QStyleOptionToolButton::MenuButtonPopup) || (opt.features & QStyleOptionToolButton::HasMenu)); +} + +/** + * @brief 计算sizehint + * + * 此函数非常关键,因为所有尺寸计算都是基于原始的rect来的 + * @param opt + * @return + */ +QSize SARibbonToolButton::PrivateData::calcSizeHint(const QStyleOptionToolButton& opt) +{ + if (SARibbonToolButton::LargeButton == mButtonType) { + return calcLargeButtonSizeHint(opt); + } + return calcSmallButtonSizeHint(opt); +} + +QSize SARibbonToolButton::PrivateData::calcSmallButtonSizeHint(const QStyleOptionToolButton& opt) +{ + int w = 0, h = 0; + + switch (opt.toolButtonStyle) { + case Qt::ToolButtonIconOnly: { + w = opt.iconSize.width() + 2 * mSpacing; + h = opt.iconSize.height() + 2 * mSpacing; + } break; + case Qt::ToolButtonTextOnly: { + QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); + textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); + textSize.setHeight(calcTextDrawRectHeight(opt)); + w = textSize.width() + 2 * mSpacing; + h = textSize.height() + 2 * mSpacing; + } break; + default: { + // 先加入icon的尺寸 + w = opt.iconSize.width() + 2 * mSpacing; + h = opt.iconSize.height() + 2 * mSpacing; + // 再加入文本的长度 + if (!opt.text.isEmpty()) { + QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, simplified(opt.text)); + textSize.setWidth(textSize.width() + SA_FONTMETRICS_WIDTH(opt.fontMetrics, (QLatin1Char(' '))) * 2); + textSize.setHeight(calcTextDrawRectHeight(opt)); + w += mSpacing; + w += textSize.width(); + h = qMax(h, (textSize.height() + (2 * mSpacing))); + } else { + // 没有文本的时候也要设置一下高度 + QSize textSize = opt.fontMetrics.size(Qt::TextShowMnemonic, " "); + h = qMax(h, (textSize.height() + (2 * mSpacing))); + } + } + } + if (hasIndicator(opt)) { + // 存在indicator的按钮,宽度尺寸要扩展 + w += mIndicatorLen; + } + if (w < 16) { + w = 16; + } + //! Qt6.4 取消了QApplication::globalStrut + return QSize(w, h).expandedTo(QSize(2, 2)); +} + +QSize SARibbonToolButton::PrivateData::calcLargeButtonSizeHint(const QStyleOptionToolButton& opt) +{ + int w = 0; + int h = opt.fontMetrics.lineSpacing() * 4.8; // 3*1.6 + int minW = h * 0.75; // 最小宽度,在pannel里面的按钮,最小宽度要和icon适应 + + if (SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(q_ptr->parent())) { + // 对于建立在SARibbonPannel的基础上的大按钮,把高度设置为SARibbonPannel计算的大按钮高度 + h = pannel->largeButtonHeight(); + } + int textHeight = calcTextDrawRectHeight(opt); + // 估算字体的宽度作为宽度 + w = estimateLargeButtonTextWidth(h, textHeight, opt.text, opt.fontMetrics); + w += (2 * mSpacing); + // 判断是否需要加上indicator + if (isEnableWordWrap() && isTextNeedWrap()) { + w += mIndicatorLen; + } + +#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| | |-SARibbonToolButton::PrivateData::calcLargeButtonSizeHint,text=" << opt.text + << "\n| | | |-lineSpacing*4.5=" << opt.fontMetrics.lineSpacing() * 4.5 // + << "\n| | | |-textHeight=" << textHeight // + << "\n| | | |-mDrawIconRect=" << mDrawIconRect // + << "\n| | | |-minW=" << minW // + << "\n| | | |-w=" << w // + ; +#endif + //! Qt6.4 取消了QApplication::globalStrut + return QSize(w, h).expandedTo(QSize(minW, textHeight)); +} + +/** + * @brief 计算文本高度 + * @param opt + * @return + */ +int SARibbonToolButton::PrivateData::calcTextDrawRectHeight(const QStyleOptionToolButton& opt) const +{ + if (SARibbonToolButton::LargeButton == mButtonType) { + if (isEnableWordWrap()) { + return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_WORDWRAP_TEXT_FACTOR + opt.fontMetrics.leading(); + } else { + return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_NOWORDWRAP_TEXT_FACTOR; + } + } + // 小按钮 + return opt.fontMetrics.lineSpacing() * SARIBBONTOOLBUTTON_SMALLBUTTON_TEXT_FACTOR; +} + +/** + * @brief 估算一个最优的文字尺寸,在可以换行的情况下会进行换行,且只会换一行 + * @param buttonHeight 按钮的高度 + * @param textDrawRectHeight 文本绘制的高度 + * @param fm QFontMetrics + * @param widthHeightRatio 宽高比,宽度/高度的比值,如果大于这个比值,则会进行尝试换行以获取更低的宽度 + * @param maxTrycount 尝试次数 + * @return + */ +int SARibbonToolButton::PrivateData::estimateLargeButtonTextWidth(int buttonHeight, + int textDrawRectHeight, + const QString& text, + const QFontMetrics& fm, + float widthHeightRatio, + int maxTrycount) +{ + QSize textSize; + int space = SA_FONTMETRICS_WIDTH(fm, (QLatin1Char(' '))) * 2; + int hintMaxWidth = buttonHeight * widthHeightRatio; ///< 建议的宽度 + if (isEnableWordWrap()) { + textSize = fm.size(Qt::TextShowMnemonic, text); + textSize.setWidth(textSize.width() + space); + + if (textSize.height() > fm.lineSpacing() * 1.1) { + //! 说明文字带有换行符,是用户手动换行,这种情况就直接返回字体尺寸,不进行估算 + mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 + return textSize.width(); + } + + // 这时候需要估算文本的长度 + if (textSize.width() <= hintMaxWidth) { + // 范围合理,直接返回 + mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 + return textSize.width(); + } + + //! 大于宽高比尝试进行文字换行 + //! 这里先对文本长度逐渐加长估算,一直到和原来长度一致为止 + int trycount = 0; + int alignment = Qt::TextShowMnemonic | Qt::TextWordWrap; + // 对于英文字体,直接宽度减半是无法满足完全显示两行的,需要进行预估 + QRect textRect(0, 0, textSize.width(), textDrawRectHeight); + do { + //! 先计算两行文本的紧凑矩形 + //! 从一半开始逐渐递增 + //! 第1次为 w/2 + w/2 * (0/3) + //! 第2次为 w/2 + w/2 * (1/3) + //! 第3次为 w/2 + w/2 * (2/3) + textRect.setWidth(textSize.width() / 2 + (textSize.width() / 2) * (float(trycount) / maxTrycount)); + textRect = fm.boundingRect(textRect, alignment, text); + if (textRect.height() <= (fm.lineSpacing() * 2)) { + // 保证在两行 + mIsTextNeedWrap = true; // 文字需要换行显示,标记起来 + return textRect.width(); + } + ++trycount; +#if SARIBBONTOOLBUTTON_DEBUG_DRAW + if (trycount > 1) { + qDebug() << "estimateLargeButtonTextWidth,origin textSize=" << textSize << ",trycount=" << trycount + << ",textRect=" << textRect; + } +#endif + } while (trycount < 3); + // 到这里说明前面的尝试失败,最终使用原始的长度 + return textSize.width(); + } + + //! 说明是不换行 + + mIsTextNeedWrap = false; // 文字不需要换行显示,标记起来 + // 文字不换行情况下,做simplified处理 + textSize = fm.size(Qt::TextShowMnemonic, simplified(text)); + textSize.setWidth(textSize.width() + space); + if (textSize.width() < hintMaxWidth) { + // 范围合理,直接返回 + return textSize.width(); + } + if (textSize.width() > q_ptr->maximumWidth()) { + // 超出了极限,就返回极限 + return q_ptr->maximumWidth(); + } + return hintMaxWidth; +} + +QPixmap SARibbonToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const +{ + if (opt.icon.isNull()) { // 没有有图标 + return (QPixmap()); + } + QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(opt.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { + mode = QIcon::Active; + } else { + mode = QIcon::Normal; + } + // 添加高分屏支持 + QSize pxiampSize = iconsize - QSize(2, 2); + return opt.icon.pixmap(pxiampSize, mode, state); +} + +int SARibbonToolButton::PrivateData::getTextAlignment() const +{ + int alignment = Qt::TextShowMnemonic; + if (SARibbonToolButton::LargeButton == mButtonType) { + if (isEnableWordWrap()) { + alignment |= Qt::TextWordWrap | Qt::AlignTop | Qt::AlignHCenter; // 换行的情况下,顶部对齐 + } else { + alignment |= Qt::AlignCenter; + } + } else { + alignment |= Qt::AlignCenter; + } + return alignment; +} + +/** + * @brief 确认文字是否确切要换行显示 + * @return + */ +bool SARibbonToolButton::PrivateData::isTextNeedWrap() const +{ + return mIsTextNeedWrap; +} + +/** + * @brief 仅仅对\n进行剔除 + * @param str + * @return + */ +QString SARibbonToolButton::PrivateData::simplified(const QString& str) +{ + QString res = str; + res.remove('\n'); + return res; +} +//=================================================== +// SARibbonToolButton +//=================================================== + +SARibbonToolButton::SARibbonToolButton(QWidget* parent) + : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) +{ + setAutoRaise(true); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setButtonType(SmallButton); + setMouseTracking(true); +} + +SARibbonToolButton::SARibbonToolButton(QAction* defaultAction, QWidget* parent) + : QToolButton(parent), d_ptr(new SARibbonToolButton::PrivateData(this)) +{ + setAutoRaise(true); + setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + setDefaultAction(defaultAction); + setButtonType(SmallButton); + setMouseTracking(true); +} + +SARibbonToolButton::~SARibbonToolButton() +{ +} + +/** + * @brief 鼠标移动事件 + * + * 由于Ribbon的Indicator和正常的Toolbutton不一样,因此无法用QStyleOptionToolButton的activeSubControls的状态 + * + * 因此需要重新捕获鼠标的位置来更新按钮当前的一些状态 + * @param e + */ +void SARibbonToolButton::mouseMoveEvent(QMouseEvent* e) +{ + d_ptr->updateStatusByMousePosition(e->pos()); + QToolButton::mouseMoveEvent(e); +} + +/** + * @brief SARibbonToolButton::mousePressEvent + * @param e + */ +void SARibbonToolButton::mousePressEvent(QMouseEvent* e) +{ + if ((e->button() == Qt::LeftButton) && (popupMode() == MenuButtonPopup)) { + d_ptr->updateStatusByMousePosition(e->pos()); + if (d_ptr->mMouseOnSubControl) { + d_ptr->mMenuButtonPressed = true; + showMenu(); + // showmenu结束后,在判断当前的鼠标位置是否是在subcontrol + d_ptr->updateStatusByMousePosition(mapFromGlobal(QCursor::pos())); + return; + } + } + d_ptr->mMenuButtonPressed = false; + //! 注意这里要用QAbstractButton的mousePressEvent,而不是QToolButton的mousePressEvent + //! QToolButton的mousePressEvent主要是为了弹出菜单,这里弹出菜单的方式是不一样的,因此不能执行QToolButton的mousePressEvent + QAbstractButton::mousePressEvent(e); +} + +void SARibbonToolButton::mouseReleaseEvent(QMouseEvent* e) +{ + d_ptr->mMenuButtonPressed = false; + QToolButton::mouseReleaseEvent(e); +} + +void SARibbonToolButton::focusOutEvent(QFocusEvent* e) +{ + d_ptr->mMouseOnSubControl = false; + QToolButton::focusOutEvent(e); +} + +void SARibbonToolButton::leaveEvent(QEvent* e) +{ + d_ptr->mMouseOnSubControl = false; + QToolButton::leaveEvent(e); +} + +bool SARibbonToolButton::hitButton(const QPoint& pos) const +{ + if (QToolButton::hitButton(pos)) { + return (!d_ptr->mMenuButtonPressed); + } + return (false); +} + +/** + * @brief 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 + * @param e + */ +void SARibbonToolButton::resizeEvent(QResizeEvent* e) +{ + // 在resizeevent计算绘图所需的尺寸,避免在绘图过程中实时绘制提高效率 + QToolButton::resizeEvent(e); + updateRect(); +} + +/** + * @brief toolbutton的尺寸确定是先定下字体的尺寸,再定下icon的尺寸,自底向上,保证字体能显示两行 + * @note m_sizeHint的刷新需要注意 + * @return + */ +QSize SARibbonToolButton::sizeHint() const +{ + // if (!d_ptr->mSizeHint.isValid()) { // 22是给与sizehint的最小值,如果小于这个值,重新计算一下 + // QStyleOptionToolButton opt; + // initStyleOption(&opt); + // d_ptr->updateSizeHint(opt); + // } +#if SA_RIBBON_TOOLBUTTON_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| | |-SARibbonToolButton::sizeHint"; +#endif + QStyleOptionToolButton opt; + initStyleOption(&opt); + d_ptr->updateSizeHint(opt); + return d_ptr->mSizeHint; +} + +void SARibbonToolButton::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + QPainter p(this); + QStyleOptionToolButton opt; + initStyleOption(&opt); + if (opt.features & QStyleOptionToolButton::MenuButtonPopup || opt.features & QStyleOptionToolButton::HasMenu) { + // 在菜单弹出消失后,需要通过此方法取消掉鼠标停留 + if (!rect().contains(mapFromGlobal(QCursor::pos()))) { + opt.state &= ~QStyle::State_MouseOver; + } + } + paintButton(p, opt); + paintIcon(p, opt, d_ptr->mDrawIconRect); + paintText(p, opt, d_ptr->mDrawTextRect); + paintIndicator(p, opt, d_ptr->mDrawIndicatorArrowRect); +} + +/** + * @brief 绘制按钮 + * @param p + * @param opt + */ +void SARibbonToolButton::paintButton(QPainter& p, const QStyleOptionToolButton& opt) +{ + // QStyle::State_Sunken 代表按钮按下去了 + // QStyle::State_On 代表按钮按checked + // QStyle::State_MouseOver 代表当前鼠标位于按钮上面 + QStyleOption tool = opt; + bool autoRaise = opt.state & QStyle::State_AutoRaise; + // 绘制按钮 + if (autoRaise) { + // 这个是为了实现按钮点击下去后(QStyle::State_Sunken),能出现选中的状态 + // 先绘制一个鼠标不在按钮上的状态 + if (opt.state & QStyle::State_Sunken) { + tool.state &= ~QStyle::State_MouseOver; + } + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + // 针对MenuButtonPopup的ribbon样式的特殊绘制 + if ((opt.subControls & QStyle::SC_ToolButton) && (opt.features & QStyleOptionToolButton::MenuButtonPopup)) { + if (opt.state & QStyle::State_MouseOver) { // 鼠标在按钮上才进行绘制 + if (!(opt.activeSubControls & QStyle::SC_ToolButtonMenu)) { // 按钮的菜单弹出时不做处理 + if (LargeButton == d_ptr->mButtonType) { // 大按钮模式 + if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 + // 鼠标在文字区,把图标显示为正常(就是鼠标不放上去的状态) + tool.rect = d_ptr->mDrawIconRect; + tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 + tool.state &= ~QStyle::State_MouseOver; + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } else { + // 鼠标在图标区,把文字显示为正常 + if (!tool.state.testFlag(QStyle::State_Sunken)) { + // State_Sunken说明此按钮正在按下,这时候,文本区域不需要绘制,只有在非按下状态才需要绘制 + tool.state |= (QStyle::State_Raised); // 把图标区域显示为正常 + tool.state &= ~QStyle::State_MouseOver; + // 文字和Indicator都显示正常 + tool.rect = d_ptr->mDrawTextRect.united(d_ptr->mDrawIndicatorArrowRect); + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } + } + } else { // 小按钮模式 + if (d_ptr->mMouseOnSubControl) { // 此时鼠标在indecater那 + // 鼠标在文字区,把图标和文字显示为正常 + tool.rect = d_ptr->mDrawIconRect.united(d_ptr->mDrawTextRect); + tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } else { + // 鼠标在图标区,把文字显示为正常 + tool.state = (QStyle::State_Raised); // 把图标区域显示为正常 + // 文字和Indicator都显示正常 + tool.rect = d_ptr->mDrawIndicatorArrowRect; + if (autoRaise) { + style()->drawPrimitive(QStyle::PE_PanelButtonTool, &tool, &p, this); + } else { + style()->drawPrimitive(QStyle::PE_PanelButtonBevel, &tool, &p, this); + } + } + } + } + } + } + // 绘制Focus + // if (opt.state & QStyle::State_HasFocus) { + // QStyleOptionFocusRect fr; + // fr.QStyleOption::operator=(opt); + // fr.rect.adjust(d_ptr->mSpacing, d_ptr->mSpacing, -d_ptr->mSpacing, -d_ptr->mSpacing); + // style()->drawPrimitive(QStyle::PE_FrameFocusRect, &fr, &p, this); + // } +} + +/** + * @brief 绘制图标 + * @param p + * @param opt + */ +void SARibbonToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) +{ + if (!iconDrawRect.isValid()) { + return; + } + + QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); + style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); + SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, iconDrawRect); +} + +/** + * @brief 绘制文本 + * @param p + * @param opt + */ +void SARibbonToolButton::paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect) +{ + int alignment = d_ptr->getTextAlignment(); + + if (!style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, this)) { + alignment |= Qt::TextHideMnemonic; + } + QString text; + if (isSmallRibbonButton()) { + text = opt.fontMetrics.elidedText(PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); + } else { + if (!isEnableWordWrap()) { + text = opt.fontMetrics.elidedText(PrivateData::simplified(opt.text), Qt::ElideRight, textDrawRect.width(), alignment); + } else { + text = opt.text; + } + } + //! 以下内容参考QCommonStyle.cpp + //! void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt,QPainter *p, const QWidget *widget) const + //! case CC_ToolButton: + QStyle::State bflags = opt.state & ~QStyle::State_Sunken; + if (bflags & QStyle::State_AutoRaise) { + if (!(bflags & QStyle::State_MouseOver) || !(bflags & QStyle::State_Enabled)) { + bflags &= ~QStyle::State_Raised; + } + } + if (opt.state & QStyle::State_Sunken) { + if (opt.activeSubControls & QStyle::SC_ToolButton) { + bflags |= QStyle::State_Sunken; + } + } + QStyleOptionToolButton label = opt; + label.state = bflags; + style()->drawItemText(&p, textDrawRect, alignment, label.palette, label.state & QStyle::State_Enabled, text, QPalette::ButtonText); + SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, textDrawRect); +} + +/** + * @brief 绘制Indicator + * @param p + * @param opt + */ +void SARibbonToolButton::paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect) +{ + if (!indicatorDrawRect.isValid() || !d_ptr->hasIndicator(opt)) { + return; + } + + QStyleOption tool = opt; + tool.rect = indicatorDrawRect; + style()->drawPrimitive(QStyle::PE_IndicatorArrowDown, &tool, &p, this); + SARIBBONTOOLBUTTON_DEBUG_DRAW_RECT(p, indicatorDrawRect); +} + +void SARibbonToolButton::drawArrow(const QStyle* style, + const QStyleOptionToolButton* toolbutton, + const QRect& rect, + QPainter* painter, + const QWidget* widget) +{ + QStyle::PrimitiveElement pe; + + switch (toolbutton->arrowType) { + case Qt::LeftArrow: + pe = QStyle::PE_IndicatorArrowLeft; + break; + + case Qt::RightArrow: + pe = QStyle::PE_IndicatorArrowRight; + break; + + case Qt::UpArrow: + pe = QStyle::PE_IndicatorArrowUp; + break; + + case Qt::DownArrow: + pe = QStyle::PE_IndicatorArrowDown; + break; + + default: + return; + } + QStyleOption arrowOpt = *toolbutton; + + arrowOpt.rect = rect; + style->drawPrimitive(pe, &arrowOpt, painter, widget); +} + +void SARibbonToolButton::actionEvent(QActionEvent* e) +{ + QToolButton::actionEvent(e); + updateRect(); +} + +/** + * @brief 按钮样式 + * @sa setButtonType + * @return + */ +SARibbonToolButton::RibbonButtonType SARibbonToolButton::buttonType() const +{ + return (d_ptr->mButtonType); +} + +/** + * @brief 设置按钮样式 + * @note 设置按钮样式过程会调用setToolButtonStyle,如果要改变toolButtonStyle,如设置为Qt::ToolButtonIconOnly,需要在此函数之后设置 + * @param buttonType + */ +void SARibbonToolButton::setButtonType(const RibbonButtonType& buttonType) +{ + d_ptr->mButtonType = buttonType; + // 计算iconrect + // 根据字体计算文字的高度 + + if (LargeButton == buttonType) { + setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); + } else { + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); + } + + updateRect(); +} + +/** + * @brief 是否是小按钮 + * @return + */ +bool SARibbonToolButton::isSmallRibbonButton() const +{ + return (d_ptr->mButtonType == SmallButton); +} + +/** + * @brief 是否是大按钮 + * @return + */ +bool SARibbonToolButton::isLargeRibbonButton() const +{ + return (d_ptr->mButtonType == LargeButton); +} + +QSize SARibbonToolButton::minimumSizeHint() const +{ + return (sizeHint()); +} + +/** + * @brief 间距是几个重要矩形的间隔 + * @return + */ +int SARibbonToolButton::spacing() const +{ + return d_ptr->mSpacing; +} + +void SARibbonToolButton::updateRect() +{ + QStyleOptionToolButton opt; + initStyleOption(&opt); + d_ptr->updateDrawRect(opt); +} + +/** + * @brief 设置在lite模式下是否允许文字换行,如果允许,则图标相对比较小,默认不允许 + * @param on + */ +void SARibbonToolButton::setEnableWordWrap(bool on) +{ + SARibbonToolButton::PrivateData::s_enableWordWrap = on; +} + +/** + * @brief 在lite模式下是否允许文字换行 + * @return + */ +bool SARibbonToolButton::isEnableWordWrap() +{ + return SARibbonToolButton::PrivateData::s_enableWordWrap; +} + +bool SARibbonToolButton::event(QEvent* e) +{ + switch (e->type()) { + case QEvent::WindowDeactivate: + d_ptr->mMouseOnSubControl = false; + break; + case QEvent::ActionChanged: + case QEvent::ActionRemoved: + case QEvent::ActionAdded: { + d_ptr->mMouseOnSubControl = false; + updateRect(); + } break; + default: + break; + } + + return (QToolButton::event(e)); +} + +void SARibbonToolButton::changeEvent(QEvent* e) +{ + if (e) { + if (e->type() == QEvent::FontChange) { + // 说明字体改变,需要重新计算和字体相关的信息 + updateRect(); + } + } + QToolButton::changeEvent(e); +} + +/*** End of inlined file: SARibbonToolButton.cpp ***/ + +/*** Start of inlined file: SARibbonColorToolButton.cpp ***/ +#include +#include +#include + +//=================================================== +// SARibbonColorToolButton::PrivateData +//=================================================== +const int c_ribbonbutton_color_height = 5; ///< 颜色块的高度 + +class SARibbonColorToolButton::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonColorToolButton) +public: + PrivateData(SARibbonColorToolButton* p); + QPixmap createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const; + QIcon createColorIcon(const QColor& c, const QSize& size) const; + +public: + QColor mColor; ///< 记录颜色 + SARibbonColorToolButton::ColorStyle mColorStyle { SARibbonColorToolButton::ColorUnderIcon }; ///< 颜色显示样式 + QIcon mOldIcon; ///< 记录旧的icon +}; + +SARibbonColorToolButton::PrivateData::PrivateData(SARibbonColorToolButton* p) : q_ptr(p) +{ +} + +QPixmap SARibbonColorToolButton::PrivateData::createIconPixmap(const QStyleOptionToolButton& opt, const QSize& iconsize) const +{ + if (opt.icon.isNull()) { // 没有有图标 + return QPixmap(); + } + // 有icon,在icon下方加入颜色 + QIcon::State state = (opt.state & QStyle::State_On) ? QIcon::On : QIcon::Off; + QIcon::Mode mode; + if (!(opt.state & QStyle::State_Enabled)) { + mode = QIcon::Disabled; + } else if ((opt.state & QStyle::State_MouseOver) && (opt.state & QStyle::State_AutoRaise)) { + mode = QIcon::Active; + } else { + mode = QIcon::Normal; + } + QSize realIconSize = iconsize - QSize(0, c_ribbonbutton_color_height + 1); + QPixmap pixmap = opt.icon.pixmap(q_ptr->window()->windowHandle(), realIconSize, mode, state); + QPixmap res(pixmap.size() + QSize(4, c_ribbonbutton_color_height + 4)); // 宽度上,颜色块多出2px + res.fill(Qt::transparent); + QPainter painter(&res); + int xpixmap = (res.width() - pixmap.width()) / 2; + int ypixmap = (res.height() - c_ribbonbutton_color_height - 2 - pixmap.height()) / 2; // 这里要减去2而不是1,这样奇数偶数都不会影响 + int w = pixmap.width(); + int h = pixmap.height(); + QRect rpixmap = QRect(xpixmap, ypixmap, w, h); + painter.drawPixmap(rpixmap, pixmap); + QRect colorRect = rpixmap.adjusted(0, h + 1, 0, c_ribbonbutton_color_height + 1); + if (mColor.isValid()) { + painter.fillRect(colorRect, mColor); + } else { + QPen pen(Qt::red, 1, Qt::SolidLine, Qt::RoundCap); + painter.setPen(pen); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + painter.setRenderHint(QPainter::Antialiasing, true); + int ss = colorRect.width() / 3; + painter.drawLine(QPoint(colorRect.x() + ss, colorRect.bottom()), QPoint(colorRect.right() - ss, colorRect.top())); + pen.setColor(Qt::black); + painter.setPen(pen); + painter.drawRect(colorRect); + } + return res; +} + +QIcon SARibbonColorToolButton::PrivateData::createColorIcon(const QColor& c, const QSize& size) const +{ + QPixmap res(size); + res.fill(Qt::transparent); + QPainter painter(&res); + if (c.isValid()) { + painter.fillRect(QRect(1, 1, res.height() - 2, res.width() - 2), c); + } else { + QPen pen(Qt::black, 1, Qt::SolidLine, Qt::RoundCap); + painter.setPen(pen); + painter.drawRect(QRect(1, 1, res.height() - 2, res.width() - 2)); + pen.setColor(Qt::red); + painter.setPen(pen); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.drawLine(QPoint(1, size.height()), QPoint(size.width() - 1, 1)); + } + return QIcon(res); +} + +//=================================================== +// SARibbonColorToolButton +//=================================================== + +SARibbonColorToolButton::SARibbonColorToolButton(QWidget* parent) + : SARibbonToolButton(parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) +{ + connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); +} + +SARibbonColorToolButton::SARibbonColorToolButton(QAction* defaultAction, QWidget* parent) + : SARibbonToolButton(defaultAction, parent), d_ptr(new SARibbonColorToolButton::PrivateData(this)) +{ + connect(this, &QAbstractButton::clicked, this, &SARibbonColorToolButton::onButtonClicked); +} + +SARibbonColorToolButton::~SARibbonColorToolButton() +{ +} + +/** + * @brief 获取按钮维护的颜色 + * @return + */ +QColor SARibbonColorToolButton::color() const +{ + return d_ptr->mColor; +} + +/** + * @brief 设置颜色显示的样式 + * @param s + */ +void SARibbonColorToolButton::setColorStyle(SARibbonColorToolButton::ColorStyle s) +{ + if (d_ptr->mColorStyle == s) { + return; + } + d_ptr->mColorStyle = s; + if (ColorUnderIcon == s) { + setIcon(d_ptr->mOldIcon); + } else { + d_ptr->mOldIcon = icon(); + setIcon(d_ptr->createColorIcon(d_ptr->mColor, QSize(32, 32))); + } + repaint(); +} + +/** + * @brief 颜色显示的样式 + * @return + */ +SARibbonColorToolButton::ColorStyle SARibbonColorToolButton::colorStyle() const +{ + return d_ptr->mColorStyle; +} + +/** + * @brief 建立标准的颜色菜单 + * @return + */ +SAColorMenu* SARibbonColorToolButton::setupStandardColorMenu() +{ + setPopupMode(QToolButton::MenuButtonPopup); + SAColorMenu* m = new SAColorMenu(this); + m->enableNoneColorAction(true); + QAction* customColor = m->customColorAction(); + if (customColor) { + customColor->setIcon(QIcon(":/image/resource/define-color.svg")); + } + connect(m, &SAColorMenu::selectedColor, this, &SARibbonColorToolButton::setColor); + setMenu(m); + + updateRect(); + return m; +} + +/** + * @brief 设置按钮的颜色 + * + * 此时会生成一个新的icon + * @note 会发射@sa colorChanged 信号 + * @param c + */ +void SARibbonColorToolButton::setColor(const QColor& c) +{ + if (d_ptr->mColor != c) { + d_ptr->mColor = c; + if (ColorFillToIcon == colorStyle()) { + setIcon(d_ptr->createColorIcon(c, QSize(32, 32))); + } + repaint(); + emit colorChanged(c); + } +} + +void SARibbonColorToolButton::onButtonClicked(bool checked) +{ + emit colorClicked(d_ptr->mColor, checked); +} + +/** + * @brief 重写paintIcon函数,把颜色加到icon下面 + * @param p + * @param opt + * @param iconDrawRect + */ +void SARibbonColorToolButton::paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect) +{ + if (ColorUnderIcon == colorStyle()) { + // 有icon + QPixmap pm = d_ptr->createIconPixmap(opt, iconDrawRect.size()); + style()->drawItemPixmap(&p, iconDrawRect, Qt::AlignCenter, pm); + } else { + SARibbonToolButton::paintIcon(p, opt, iconDrawRect); + } +} + +/*** End of inlined file: SARibbonColorToolButton.cpp ***/ + +/*** Start of inlined file: SARibbonLineWidgetContainer.cpp ***/ +#include + +SARibbonLineWidgetContainer::SARibbonLineWidgetContainer(QWidget* par) : QWidget(par), m_innerWidget(nullptr) +{ + m_labelPrefix = new QLabel(this); + m_labelSuffix = new QLabel(this); + QHBoxLayout* lay = new QHBoxLayout(); + + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(0); + lay->addWidget(m_labelPrefix); + lay->addWidget(m_labelSuffix); + setLayout(lay); +} + +void SARibbonLineWidgetContainer::setWidget(QWidget* innerWidget) +{ + QHBoxLayout* lay = static_cast< QHBoxLayout* >(layout()); + + if (m_innerWidget) { + lay->replaceWidget(m_innerWidget, innerWidget); + } else { + lay->insertWidget(1, innerWidget); + } + m_innerWidget = innerWidget; +} + +void SARibbonLineWidgetContainer::setPrefix(const QString& str) +{ + m_labelPrefix->setText(str); +} + +void SARibbonLineWidgetContainer::setSuffix(const QString& str) +{ + m_labelSuffix->setText(str); +} + +QLabel* SARibbonLineWidgetContainer::labelPrefix() const +{ + return (m_labelPrefix); +} + +QLabel* SARibbonLineWidgetContainer::labelSuffix() const +{ + return (m_labelSuffix); +} + +/*** End of inlined file: SARibbonLineWidgetContainer.cpp ***/ + +/*** Start of inlined file: SARibbonActionsManager.cpp ***/ +#include +#include +#include + +class SARibbonActionsManager::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManager) +public: + PrivateData(SARibbonActionsManager* p); + void clear(); + + QMap< int, QList< QAction* > > mTagToActions; ///< tag : QList + QMap< int, QString > mTagToName; ///< tag对应的名字 + QHash< QString, QAction* > mKeyToAction; ///< key对应action + QMap< QAction*, QString > mActionToKey; ///< action对应key + QMap< int, SARibbonCategory* > mTagToCategory; ///< 仅仅在autoRegisteActions函数会有用 + int mSale; ///< 盐用于生成固定的id,在用户不主动设置key时,id基于msale生成,只要SARibbonActionsManager的调用registeAction顺序不变,生成的id都不变,因为它是基于自增实现的 +}; + +SARibbonActionsManager::PrivateData::PrivateData(SARibbonActionsManager* p) : q_ptr(p), mSale(0) +{ +} + +void SARibbonActionsManager::PrivateData::clear() +{ + mTagToActions.clear(); + mTagToName.clear(); + mKeyToAction.clear(); + mActionToKey.clear(); + mTagToCategory.clear(); + mSale = 0; +} + +SARibbonActionsManager::SARibbonActionsManager(SARibbonBar* bar) + : QObject(bar), d_ptr(new SARibbonActionsManager::PrivateData(this)) +{ + autoRegisteActions(bar); +} + +SARibbonActionsManager::~SARibbonActionsManager() +{ +} + +/** + * @brief 设置tag对应的名字,通过这个可以得到tag和文本的映射 + * @param tag + * @param name + * @note 在支持多语言的环境下,在语言切换时需要重新设置,以更新名字 + */ +void SARibbonActionsManager::setTagName(int tag, const QString& name) +{ + d_ptr->mTagToName[ tag ] = name; +} + +/** + * @brief 获取tag对应的中文名字 + * @param tag + * @return + */ +QString SARibbonActionsManager::tagName(int tag) const +{ + return (d_ptr->mTagToName.value(tag, "")); +} + +/** + * @brief 移除tag + * @note 注意,这个函数非常耗时 + * @param tag + */ +void SARibbonActionsManager::removeTag(int tag) +{ + QList< QAction* > oldacts = actions(tag); + + // 开始移除 + d_ptr->mTagToActions.remove(tag); + d_ptr->mTagToName.remove(tag); + // 开始查找需要移出总表的action + QList< QAction* > needRemoveAct; + QList< QAction* > total; + + for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { + total += i.value(); + } + for (QAction* a : qAsConst(oldacts)) { + if (!total.contains(a)) { + needRemoveAct.append(a); + } + } + // 从总表移除action + for (QAction* a : qAsConst(needRemoveAct)) { + auto i = d_ptr->mActionToKey.find(a); + if (i != d_ptr->mActionToKey.end()) { + d_ptr->mKeyToAction.remove(i.value()); + d_ptr->mActionToKey.erase(i); + } + } +} + +/** + * @brief 把action注册到管理器中,实现action的管理 + * @param act + * @param tag tag是可以按照位进行叠加,见 @ref ActionTag 如果 + * 要定义自己的标签,建议定义大于@ref ActionTag::UserDefineActionTag 的值, + * registeAction的tag是直接记录进去的,如果要多个标签并存,在registe之前先或好tag + * @param key key是action对应的key,一个key只对应一个action,是查找action的关键 + * ,默认情况为一个QString(),这时key是QAction的objectName + * @note 同一个action多次注册不同的tag可以通过tag索引到action,但通过action只能索引到最后一个注册的tag + * @note tag的新增会触发actionTagChanged信号 + */ +bool SARibbonActionsManager::registeAction(QAction* act, int tag, const QString& key, bool enableEmit) +{ + if (nullptr == act) { + return (false); + } + QString k = key; + + if (k.isEmpty()) { + k = QString("id_%1_%2").arg(d_ptr->mSale++).arg(act->objectName()); + } + if (d_ptr->mKeyToAction.contains(k)) { + qWarning() << "key: " << k << " have been exist,you can set key in an unique value when use SARibbonActionsManager::registeAction"; + return (false); + } + d_ptr->mKeyToAction[ k ] = act; + d_ptr->mActionToKey[ act ] = k; + // 记录tag 对 action + bool isneedemit = !(d_ptr->mTagToActions.contains(tag)); // 记录是否需要发射信号 + + d_ptr->mTagToActions[ tag ].append(act); + // 绑定槽 + connect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); + if (isneedemit && enableEmit) { + // 说明新增tag + emit actionTagChanged(tag, false); + } + return (true); +} + +/** + * @brief 取消action的注册 + * + * 如果tag对应的最后一个action被撤销,tag也将一块删除 + * @param act + * @note tag的删除会触发actionTagChanged信号 + * @note 如果action关联了多个tag,这些tag里的action都会被删除,对应的key也同理 + */ +void SARibbonActionsManager::unregisteAction(QAction* act, bool enableEmit) +{ + if (nullptr == act) { + return; + } + // 绑定槽 + disconnect(act, &QObject::destroyed, this, &SARibbonActionsManager::onActionDestroyed); + removeAction(act, enableEmit); +} + +/** + * @brief 移除action + * + * 仅移除内存内容 + * @param act + * @param enableEmit + */ +void SARibbonActionsManager::removeAction(QAction* act, bool enableEmit) +{ + QList< int > deletedTags; // 记录删除的tag,用于触发actionTagChanged + QMap< int, QList< QAction* > > tagToActions; ///< tag : QList + + for (auto i = d_ptr->mTagToActions.begin(); i != d_ptr->mTagToActions.end(); ++i) { + // 把不是act的内容转移到tagToActions和tagToActionKeys中,之后再和m_d里的替换 + auto tmpi = tagToActions.insert(i.key(), QList< QAction* >()); + int count = 0; + for (int j = 0; j < i.value().size(); ++j) { + if (i.value()[ j ] != act) { + tmpi.value().append(act); + ++count; + } + } + if (0 == count) { + // 说明这个tag没有内容 + tagToActions.erase(tmpi); + deletedTags.append(i.key()); + } + } + // 删除mKeyToAction + QString key = d_ptr->mActionToKey.value(act); + + d_ptr->mActionToKey.remove(act); + d_ptr->mKeyToAction.remove(key); + + // 置换 + d_ptr->mTagToActions.swap(tagToActions); + // 发射信号 + if (enableEmit) { + for (int tagdelete : qAsConst(deletedTags)) { + emit actionTagChanged(tagdelete, true); + } + } +} + +/** + * @brief 等同actions + * @param tag + * @return + */ +QList< QAction* >& SARibbonActionsManager::filter(int tag) +{ + return (actions(tag)); +} + +/** + * @brief 根据tag得到actions + * @param tag + * @return + */ +QList< QAction* >& SARibbonActionsManager::actions(int tag) +{ + return (d_ptr->mTagToActions[ tag ]); +} + +const QList< QAction* > SARibbonActionsManager::actions(int tag) const +{ + return (d_ptr->mTagToActions[ tag ]); +} + +/** + * @brief 获取所有的标签 + * @return + */ +QList< int > SARibbonActionsManager::actionTags() const +{ + return (d_ptr->mTagToActions.keys()); +} + +/** + * @brief 通过key获取action + * @param key + * @return 如果没有key,返回nullptr + */ +QAction* SARibbonActionsManager::action(const QString& key) const +{ + return (d_ptr->mKeyToAction.value(key, nullptr)); +} + +/** + * @brief 通过action找到key + * @param act + * @return 如果找不到,返回QString() + */ +QString SARibbonActionsManager::key(QAction* act) const +{ + return (d_ptr->mActionToKey.value(act, QString())); +} + +/** + * @brief 返回所有管理的action数 + * @return + */ +int SARibbonActionsManager::count() const +{ + return (d_ptr->mKeyToAction.size()); +} + +/** + * @brief 返回所有管理的actions + * @return + */ +QList< QAction* > SARibbonActionsManager::allActions() const +{ + return (d_ptr->mKeyToAction.values()); +} + +/** + * @brief 自动加载SARibbonBar的action + * 此函数会遍历@ref SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子object,找到action注册, + * 并会遍历所有@ref SARibbonCategory,把SARibbonCategory下的action按SARibbonCategory的title name进行分类 + * + * 此函数会把所有category下的action生成tag并注册,返回的QMap是记录了category对应的tag + * + * 此函数还会把SARibbonBar的父窗口(一般是SARibbonMainWindow)下面的action,但不在任何一个category下的作为NotInRibbonCategoryTag标签注册,默认名字会赋予not + * in ribbon, 可以通过@ref setTagName 改变 + * + * @param w + * @return + * @note 此函数的调用最好在category设置了标题后调用,因为会以category的标题作为标签的命名 + */ +QMap< int, SARibbonCategory* > SARibbonActionsManager::autoRegisteActions(SARibbonBar* bar) +{ + QMap< int, SARibbonCategory* > res; + // 先遍历SARibbonBar的父窗口(一般是SARibbonMainWindow)下的所有子对象,把所有action找到 + QWidget* parWidget = bar->parentWidget(); + QSet< QAction* > mainwindowActions; + if (parWidget) { + for (QObject* o : qAsConst(parWidget->children())) { + if (QAction* a = qobject_cast< QAction* >(o)) { + // 说明是action + if (!a->objectName().isEmpty()) { + mainwindowActions.insert(a); + } + } + } + } + // 开始遍历每个category,加入action + + if (nullptr == bar) { + // 非ribbon模式,直接退出 + return (res); + } + QSet< QAction* > categoryActions; + QList< SARibbonCategory* > categorys = bar->categoryPages(); + int tag = AutoCategoryDistinguishBeginTag; + + for (SARibbonCategory* c : qAsConst(categorys)) { + QList< SARibbonPannel* > pannels = c->pannelList(); + for (SARibbonPannel* p : qAsConst(pannels)) { + categoryActions += autoRegisteWidgetActions(p, tag, false); + } + setTagName(tag, c->categoryName()); + res[ tag ] = c; + ++tag; + } + // 找到不在功能区的actions + QSet< QAction* > notincategory = mainwindowActions - categoryActions; + + for (QAction* a : qAsConst(notincategory)) { + if (!a->objectName().isEmpty()) { + registeAction(a, NotInRibbonCategoryTag, a->objectName(), false); + } + } + if (notincategory.size() > 0) { + setTagName(NotInRibbonCategoryTag, tr("not in ribbon")); + } + for (auto i = res.begin(); i != res.end(); ++i) { + connect(i.value(), &SARibbonCategory::categoryNameChanged, this, &SARibbonActionsManager::onCategoryTitleChanged); + } + d_ptr->mTagToCategory = res; + return (res); +} + +/** + * @brief 自动加载action + * @param w + * @param tag + * @param enableEmit + * @return 返回成功加入SARibbonActionsManager管理的action + */ +QSet< QAction* > SARibbonActionsManager::autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit) +{ + QSet< QAction* > res; + QList< QAction* > was = w->actions(); + + for (QAction* a : qAsConst(was)) { + if (res.contains(a) || a->objectName().isEmpty()) { + // 重复内容不重复加入 + // 没有object name不加入 + continue; + } + if (registeAction(a, tag, a->objectName(), enableEmit)) { + res.insert(a); + } + } + return (res); +} + +/** + * @brief 根据标题查找action + * @param text + * @return + */ +QList< QAction* > SARibbonActionsManager::search(const QString& text) +{ + QList< QAction* > res; + + if (text.isEmpty()) { + return (res); + } + QStringList kws = text.split(" "); + + if (kws.isEmpty()) { + kws.append(text); + } + QList< QAction* > acts = d_ptr->mActionToKey.keys(); + + for (const QString& k : qAsConst(kws)) { + for (auto i = d_ptr->mActionToKey.begin(); i != d_ptr->mActionToKey.end(); ++i) { + if (i.key()->text().contains(k, Qt::CaseInsensitive)) { + res.append(i.key()); + } + } + } + return (res); +} + +void SARibbonActionsManager::clear() +{ + d_ptr->clear(); +} + +/** + * @brief action 被delete时,将触发此槽把管理的action删除 + * @param o + * @note 这个函数不会触发actionTagChanged信号 + */ +void SARibbonActionsManager::onActionDestroyed(QObject* o) +{ + QAction* act = static_cast< QAction* >(o); + + removeAction(act, false); +} + +/** + * @brief autoRegisteActions函数会关联此槽,在标签内容改变时改变tag 对应 文本 + * @param title + */ +void SARibbonActionsManager::onCategoryTitleChanged(const QString& title) +{ + SARibbonCategory* c = qobject_cast< SARibbonCategory* >(sender()); + + if (nullptr == c) { + return; + } + int tag = d_ptr->mTagToCategory.key(c, -1); + + if (tag == -1) { + return; + } + setTagName(tag, title); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// SARibbonActionsModel +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +class SARibbonActionsManagerModel::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonActionsManagerModel) +public: + PrivateData(SARibbonActionsManagerModel* p); + void updateRef(); + int count() const; + QAction* at(int index); + bool isNull() const; + +public: + SARibbonActionsManager* mMgr { nullptr }; + int mTag { SARibbonActionsManager::CommonlyUsedActionTag }; + QString mSeatchText; + QList< QAction* > mActions; +}; + +SARibbonActionsManagerModel::PrivateData::PrivateData(SARibbonActionsManagerModel* p) : q_ptr(p) +{ +} + +void SARibbonActionsManagerModel::PrivateData::updateRef() +{ + if (isNull()) { + return; + } + if (!mSeatchText.isEmpty()) { + mActions = mMgr->search(mSeatchText); + } else { + mActions = mMgr->actions(mTag); + } +} + +int SARibbonActionsManagerModel::PrivateData::count() const +{ + if (isNull()) { + return (0); + } + return (mActions.size()); +} + +QAction* SARibbonActionsManagerModel::PrivateData::at(int index) +{ + if (isNull()) { + return (nullptr); + } + if (index >= count()) { + return (nullptr); + } + return (mActions.at(index)); +} + +bool SARibbonActionsManagerModel::PrivateData::isNull() const +{ + return (mMgr == nullptr); +} + +//=================================================== +// SARibbonActionsManagerModel +//=================================================== + +SARibbonActionsManagerModel::SARibbonActionsManagerModel(QObject* p) + : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) +{ +} + +SARibbonActionsManagerModel::SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p) + : QAbstractListModel(p), d_ptr(new SARibbonActionsManagerModel::PrivateData(this)) +{ + setupActionsManager(m); +} + +SARibbonActionsManagerModel::~SARibbonActionsManagerModel() +{ +} + +int SARibbonActionsManagerModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { // 非顶层 + return (0); + } + // 顶层 + return (d_ptr->count()); +} + +QVariant SARibbonActionsManagerModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(section); + if (role != Qt::DisplayRole) { + return (QVariant()); + } + if (Qt::Horizontal == orientation) { + return (tr("action name")); + } + return (QVariant()); +} + +Qt::ItemFlags SARibbonActionsManagerModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) { + return (Qt::NoItemFlags); + } + return (Qt::ItemIsSelectable | Qt::ItemIsEnabled); +} + +QVariant SARibbonActionsManagerModel::data(const QModelIndex& index, int role) const +{ + QAction* act = indexToAction(index); + + if (nullptr == act) { + return (QVariant()); + } + + switch (role) { + case Qt::DisplayRole: + return (act->text()); + + case Qt::DecorationRole: + return (act->icon()); + + default: + break; + } + return (QVariant()); +} + +void SARibbonActionsManagerModel::setFilter(int tag) +{ + d_ptr->mTag = tag; + update(); +} + +void SARibbonActionsManagerModel::update() +{ + beginResetModel(); + d_ptr->updateRef(); + endResetModel(); +} + +void SARibbonActionsManagerModel::setupActionsManager(SARibbonActionsManager* m) +{ + d_ptr->mMgr = m; + d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; + d_ptr->mActions = m->filter(d_ptr->mTag); + connect(m, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); + update(); +} + +void SARibbonActionsManagerModel::uninstallActionsManager() +{ + if (!d_ptr->isNull()) { + disconnect(d_ptr->mMgr, &SARibbonActionsManager::actionTagChanged, this, &SARibbonActionsManagerModel::onActionTagChanged); + d_ptr->mMgr = nullptr; + d_ptr->mTag = SARibbonActionsManager::CommonlyUsedActionTag; + } + update(); +} + +QAction* SARibbonActionsManagerModel::indexToAction(QModelIndex index) const +{ + if (!index.isValid()) { + return (nullptr); + } + if (index.row() >= d_ptr->count()) { + return (nullptr); + } + return (d_ptr->at(index.row())); +} + +/** + * @brief 查找 + * @param text + */ +void SARibbonActionsManagerModel::search(const QString& text) +{ + d_ptr->mSeatchText = text; + update(); +} + +void SARibbonActionsManagerModel::onActionTagChanged(int tag, bool isdelete) +{ + if (isdelete && (tag == d_ptr->mTag)) { + d_ptr->mTag = SARibbonActionsManager::UnknowActionTag; + update(); + } else { + if (tag == d_ptr->mTag) { + update(); + } + } +} + +/*** End of inlined file: SARibbonActionsManager.cpp ***/ + +/*** Start of inlined file: SARibbonLineEdit.cpp ***/ +#include + +SARibbonLineEdit::SARibbonLineEdit(QWidget* parent) : QLineEdit(parent) +{ +} + +SARibbonLineEdit::SARibbonLineEdit(const QString& text, QWidget* parent) : QLineEdit(text, parent) +{ +} + +/*** End of inlined file: SARibbonLineEdit.cpp ***/ + +/*** Start of inlined file: SARibbonCheckBox.cpp ***/ +#include + +SARibbonCheckBox::SARibbonCheckBox(QWidget* parent) : QCheckBox(parent) +{ +} + +SARibbonCheckBox::SARibbonCheckBox(const QString& text, QWidget* parent) : QCheckBox(text, parent) +{ +} + +/*** End of inlined file: SARibbonCheckBox.cpp ***/ + +/*** Start of inlined file: SARibbonComboBox.cpp ***/ +SARibbonComboBox::SARibbonComboBox(QWidget* parent) : QComboBox(parent) +{ +} + +/*** End of inlined file: SARibbonComboBox.cpp ***/ + +/*** Start of inlined file: SARibbonButtonGroupWidget.cpp ***/ +#include +#include +#include +#include +#include +#include +#include + +//=================================================== +// SARibbonButtonGroupWidget::PrivateData +//=================================================== +class SARibbonButtonGroupWidget::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonButtonGroupWidget) +public: + PrivateData(SARibbonButtonGroupWidget* p); + void init(); + void removeAction(QAction* a); + +public: + QSize mIconSize { 20, 20 }; +}; + +SARibbonButtonGroupWidget::PrivateData::PrivateData(SARibbonButtonGroupWidget* p) : q_ptr(p) +{ +} + +void SARibbonButtonGroupWidget::PrivateData::init() +{ + QHBoxLayout* layout = new QHBoxLayout(q_ptr); + // 上下保留一点间隙 + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(1); + q_ptr->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); +} + +void SARibbonButtonGroupWidget::PrivateData::removeAction(QAction* a) +{ + QLayout* lay = q_ptr->layout(); + int c = lay->count(); + QList< QLayoutItem* > willRemoveItems; + for (int i = 0; i < c; ++i) { + QLayoutItem* item = lay->itemAt(i); + SARibbonControlButton* btn = qobject_cast< SARibbonControlButton* >(item->widget()); + if (nullptr == btn) { + continue; + } + if (a == btn->defaultAction()) { + willRemoveItems.push_back(item); + } + } + // 从尾部删除 + for (auto i = willRemoveItems.rbegin(); i != willRemoveItems.rend(); ++i) { + lay->removeItem(*i); + } +} + +//=================================================== +// SARibbonButtonGroupWidget +//=================================================== + +SARibbonButtonGroupWidget::SARibbonButtonGroupWidget(QWidget* parent) + : QFrame(parent), d_ptr(new SARibbonButtonGroupWidget::PrivateData(this)) +{ + d_ptr->init(); +} + +SARibbonButtonGroupWidget::~SARibbonButtonGroupWidget() +{ +} + +/** + * @brief 设置图标尺寸 + * @param iconSize + */ +void SARibbonButtonGroupWidget::setIconSize(const QSize& ic) +{ + d_ptr->mIconSize = ic; + iterate([ ic ](SARibbonControlButton* btn) -> bool { + btn->setIconSize(ic); + return true; + }); +} + +/** + * @brief 图标尺寸 + * @return + */ +QSize SARibbonButtonGroupWidget::iconSize() const +{ + return d_ptr->mIconSize; +} + +QAction* SARibbonButtonGroupWidget::addAction(QAction* a, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + SARibbonPannel::setActionToolButtonStyleProperty(a, buttonStyle); + SARibbonPannel::setActionToolButtonPopupModeProperty(a, popMode); + QWidget::addAction(a); + return (a); +} + +/** + * @brief 生成action + * @note action的所有权归SARibbonButtonGroupWidget + * @param text + * @param icon + * @param popMode + * @return + */ +QAction* SARibbonButtonGroupWidget::addAction(const QString& text, const QIcon& icon, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + QAction* a = new QAction(icon, text, this); + addAction(a, buttonStyle, popMode); + return (a); +} + +QAction* SARibbonButtonGroupWidget::addMenu(QMenu* menu, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + QAction* a = menu->menuAction(); + addAction(a, buttonStyle, popMode); + return (a); +} + +QAction* SARibbonButtonGroupWidget::addSeparator() +{ + QAction* a = new QAction(this); + + a->setSeparator(true); + addAction(a); + return (a); +} + +QAction* SARibbonButtonGroupWidget::addWidget(QWidget* w) +{ + QWidgetAction* a = new QWidgetAction(this); + + a->setDefaultWidget(w); + w->setAttribute(Qt::WA_Hover); + addAction(a); + return (a); +} + +SARibbonControlButton* SARibbonButtonGroupWidget::actionToRibbonControlToolButton(QAction* action) +{ + SARibbonControlButton* res = nullptr; + iterate([ &res, action ](SARibbonControlButton* btn) -> bool { + if (btn->defaultAction() == action) { + res = btn; + return false; // 返回false退出迭代 + } + return true; + }); + return (res); +} + +QSize SARibbonButtonGroupWidget::sizeHint() const +{ + return (layout()->sizeHint()); +} + +QSize SARibbonButtonGroupWidget::minimumSizeHint() const +{ + return (layout()->minimumSize()); +} + +/** + * @brief 此函数会遍历SARibbonButtonGroupWidget下的所有SARibbonControlButton,执行函数指针(bool(SARibbonControlButton*)),函数指针返回false则停止迭代 + * @param fp + * @return 中途迭代退出返回false + */ +bool SARibbonButtonGroupWidget::iterate(SARibbonButtonGroupWidget::FpButtonIterate fp) +{ + QLayout* lay = layout(); + int c = lay->count(); + for (int i = 0; i < c; ++i) { + auto item = lay->itemAt(i); + if (!item) { + continue; + } + SARibbonControlButton* btn = qobject_cast< SARibbonControlButton* >(item->widget()); + if (!btn) { + continue; + } + if (!fp(btn)) { + return false; + } + } + return true; +} + +/** + * @brief 处理action的事件 + * + * 这里处理了ActionAdded,ActionChanged,ActionRemoved三个事件 + * ActionAdded时会生成窗口 + * @param e + */ +void SARibbonButtonGroupWidget::actionEvent(QActionEvent* e) +{ + QAction* a = e->action(); + if (!a) { + return; + } + + switch (e->type()) { + case QEvent::ActionAdded: { + QWidget* w = nullptr; + if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(a)) { + widgetAction->setParent(this); + w = widgetAction->requestWidget(this); + if (w != nullptr) { + w->setAttribute(Qt::WA_LayoutUsesWidgetRect); + w->show(); + } + } else if (a->isSeparator()) { + SARibbonSeparatorWidget* sp = RibbonSubElementFactory->createRibbonSeparatorWidget(this); + w = sp; + } + // 不是widget,自动生成ButtonTyle + if (!w) { + SARibbonControlButton* button = RibbonSubElementFactory->createRibbonControlButton(this); + button->setAutoRaise(true); + button->setIconSize(d_ptr->mIconSize); + button->setFocusPolicy(Qt::NoFocus); + button->setDefaultAction(a); + // 属性设置 + QToolButton::ToolButtonPopupMode popMode = SARibbonPannel::getActionToolButtonPopupModeProperty(a); + button->setPopupMode(popMode); + Qt::ToolButtonStyle buttonStyle = SARibbonPannel::getActionToolButtonStyleProperty(a); + button->setToolButtonStyle(buttonStyle); + // 根据QAction的属性设置按钮的大小 + + connect(button, &SARibbonToolButton::triggered, this, &SARibbonButtonGroupWidget::actionTriggered); + w = button; + } + layout()->addWidget(w); + updateGeometry(); + } break; + + case QEvent::ActionChanged: { + // 让布局重新绘制 + layout()->invalidate(); + updateGeometry(); + } break; + + case QEvent::ActionRemoved: { + d_ptr->removeAction(e->action()); + updateGeometry(); + } break; + + default: + break; + } + QFrame::actionEvent(e); +} + +/*** End of inlined file: SARibbonButtonGroupWidget.cpp ***/ + +/*** Start of inlined file: SARibbonStackedWidget.cpp ***/ +#include +#include +#include +#include +#include + +/** + * @brief The SARibbonStackedWidgetPrivate class + */ +class SARibbonStackedWidget::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonStackedWidget) +public: + QEventLoop* eventLoop { nullptr }; + +public: + PrivateData(SARibbonStackedWidget* p) : q_ptr(p) + { + } + + void init() + { + // Parent->setFocusPolicy(Qt::StrongFocus); + } +}; + +SARibbonStackedWidget::SARibbonStackedWidget(QWidget* parent) + : QStackedWidget(parent), d_ptr(new SARibbonStackedWidget::PrivateData(this)) +{ + d_ptr->init(); + setNormalMode(); +} + +SARibbonStackedWidget::~SARibbonStackedWidget() +{ + if (d_ptr->eventLoop) { + d_ptr->eventLoop->exit(); + } +} + +void SARibbonStackedWidget::setPopupMode() +{ + setMouseTracking(true); + setWindowFlags(Qt::Popup | Qt::FramelessWindowHint); + setFrameShape(QFrame::Panel); +} + +bool SARibbonStackedWidget::isPopupMode() const +{ + return (windowFlags() & Qt::Popup); +} + +void SARibbonStackedWidget::setNormalMode() +{ + if (d_ptr->eventLoop) { + d_ptr->eventLoop->exit(); + d_ptr->eventLoop = nullptr; + } + setMouseTracking(false); + setWindowFlags(Qt::Widget | Qt::FramelessWindowHint); + setFrameShape(QFrame::NoFrame); +} + +bool SARibbonStackedWidget::isNormalMode() const +{ + return (!isPopupMode()); +} + +void SARibbonStackedWidget::exec() +{ + show(); + if (!isPopupMode()) { + d_ptr->eventLoop = nullptr; + return; + } + QEventLoop event; + + d_ptr->eventLoop = &event; + event.exec(); + d_ptr->eventLoop = nullptr; +} + +/** + * @brief 类似tabbar的moveTab函数,交换两个窗口的index + * @param from + * @param to + * @note 此操作会触发widgetRemoved(int index)信号 + */ +void SARibbonStackedWidget::moveWidget(int from, int to) +{ + QWidget* w = widget(from); + + removeWidget(w); + insertWidget(to, w); +} + +void SARibbonStackedWidget::hideEvent(QHideEvent* e) +{ + if (isPopupMode()) { + if (d_ptr->eventLoop) { + d_ptr->eventLoop->exit(); + } + } + setFocus(); + emit hidWindow(); + + QStackedWidget::hideEvent(e); +} + +void SARibbonStackedWidget::resizeEvent(QResizeEvent* e) +{ + QStackedWidget::resizeEvent(e); + for (int i = 0; i < count(); ++i) { + if (i == currentIndex()) { + continue; + } + QEvent* layE = new QEvent(QEvent::LayoutRequest); + QApplication::postEvent(widget(i), layE); + } +} + +/*** End of inlined file: SARibbonStackedWidget.cpp ***/ + +/*** Start of inlined file: SARibbonSeparatorWidget.cpp ***/ +#include +#include +#include + +SARibbonSeparatorWidget::SARibbonSeparatorWidget(QWidget* parent) : QFrame(parent) +{ + setFrameShape(QFrame::VLine); + setFrameShadow(QFrame::Plain); + setLineWidth(1); + setMidLineWidth(1); +} + +QSize SARibbonSeparatorWidget::sizeHint() const +{ + QSize sh = QFrame::sizeHint(); + sh.setWidth(1); + return sh; +} + +/*** End of inlined file: SARibbonSeparatorWidget.cpp ***/ + +/*** Start of inlined file: SARibbonCtrlContainer.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +/** + * @brief The SARibbonCtrlContainerPrivate class + */ +class SARibbonCtrlContainer::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCtrlContainer) +public: + QWidget* containerWidget { nullptr }; + QHBoxLayout* horizontalLayout { nullptr }; + QLabel* labelPixmap { nullptr }; + QLabel* labelText { nullptr }; + QSize iconSize { 24, 24 }; + QIcon icon; + PrivateData(SARibbonCtrlContainer* p) : q_ptr(p) + { + horizontalLayout = new QHBoxLayout(p); + horizontalLayout->setSpacing(0); + horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout")); + horizontalLayout->setContentsMargins(0, 0, 0, 0); + + labelPixmap = new QLabel(p); + labelPixmap->setObjectName(QString::fromUtf8("labelPixmap")); + labelPixmap->setAlignment(Qt::AlignCenter); + horizontalLayout->addWidget(labelPixmap); + + labelText = new QLabel(p); + labelText->setObjectName(QString::fromUtf8("labelText")); + horizontalLayout->addWidget(labelText); + } + + void setContainerWidget(QWidget* w) + { + if (containerWidget) { + // 原来有widget + QWidget* oldwidget = containerWidget; + takeContainerWidget(oldwidget); + oldwidget->deleteLater(); + } + containerWidget = w; + if (!w) { + return; + } + if (w->parent() != q_ptr) { + w->setParent(q_ptr); + } + // ContainerWidget永远在最右边 + horizontalLayout->insertWidget(horizontalLayout->count(), w); + QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + w->setSizePolicy(sizePolicy); + } + + void takeContainerWidget(QWidget* w) + { + int i = horizontalLayout->indexOf(w); + if (i >= 0) { + QLayoutItem* item = horizontalLayout->takeAt(i); + delete item; + } + } +}; + +//=================================================== +// SARibbonCtrlContainer +//=================================================== +SARibbonCtrlContainer::SARibbonCtrlContainer(QWidget* parent) + : QWidget(parent), d_ptr(new SARibbonCtrlContainer::PrivateData(this)) +{ +} + +SARibbonCtrlContainer::~SARibbonCtrlContainer() +{ +} + +QWidget* SARibbonCtrlContainer::containerWidget() +{ + return (d_ptr->containerWidget); +} + +const QWidget* SARibbonCtrlContainer::containerWidget() const +{ + return (d_ptr->containerWidget); +} + +void SARibbonCtrlContainer::setEnableShowIcon(bool b) +{ + d_ptr->labelPixmap->setVisible(b); +} + +void SARibbonCtrlContainer::setEnableShowTitle(bool b) +{ + d_ptr->labelText->setVisible(b); +} + +/** + * @brief 判断是否存在容器窗口 + * @return + */ +bool SARibbonCtrlContainer::hasContainerWidget() const +{ + return (d_ptr->containerWidget != nullptr); +} + +/** + * @brief 设置图标 + * @param i + */ +void SARibbonCtrlContainer::setIcon(const QIcon& i) +{ + d_ptr->icon = i; + QPixmap pixmap = i.pixmap(d_ptr->iconSize); + d_ptr->labelPixmap->setPixmap(pixmap); +} + +void SARibbonCtrlContainer::setIcon(const QPixmap& pixmap) +{ + d_ptr->labelPixmap->setPixmap(pixmap); +} + +/** + * @brief 获取图标 + * @return + */ +QIcon SARibbonCtrlContainer::icon() const +{ + return d_ptr->icon; +} + +/** + * @brief 设置文字 + * @param t + */ +void SARibbonCtrlContainer::setText(const QString& t) +{ + d_ptr->labelText->setText(t); +} + +/** + * @brief 获取文字 + * @return + */ +QString SARibbonCtrlContainer::text() const +{ + return d_ptr->labelText->text(); +} + +void SARibbonCtrlContainer::setContainerWidget(QWidget* w) +{ + d_ptr->setContainerWidget(w); +} + +/** + * @brief 获取显示icon的窗口 + * @return + */ +QWidget* SARibbonCtrlContainer::iconWidget() const +{ + return d_ptr->labelPixmap; +} + +/*** End of inlined file: SARibbonCtrlContainer.cpp ***/ + +/*** Start of inlined file: SARibbonQuickAccessBar.cpp ***/ +#include + +/** + * @brief The SARibbonQuickAccessBarPrivate class + */ +class SARibbonQuickAccessBar::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonQuickAccessBar) +public: + PrivateData(SARibbonQuickAccessBar* p); + +public: + SARibbonButtonGroupWidget* groupWidget { nullptr }; +}; + +SARibbonQuickAccessBar::PrivateData::PrivateData(SARibbonQuickAccessBar* p) : q_ptr(p) +{ +} + +//=================================================== +// SARibbonQuickAccessBar +//=================================================== +SARibbonQuickAccessBar::SARibbonQuickAccessBar(QWidget* parent) + : SARibbonCtrlContainer(parent), d_ptr(new SARibbonQuickAccessBar::PrivateData(this)) +{ + d_ptr->groupWidget = new SARibbonButtonGroupWidget(this); + d_ptr->groupWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + setContainerWidget(d_ptr->groupWidget); +} + +SARibbonQuickAccessBar::~SARibbonQuickAccessBar() +{ +} + +void SARibbonQuickAccessBar::addSeparator() +{ + d_ptr->groupWidget->addSeparator(); +} + +void SARibbonQuickAccessBar::addAction(QAction* act, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + d_ptr->groupWidget->addAction(act, buttonStyle, popMode); +} + +void SARibbonQuickAccessBar::addWidget(QWidget* w) +{ + d_ptr->groupWidget->addWidget(w); +} + +void SARibbonQuickAccessBar::addMenu(QMenu* m, Qt::ToolButtonStyle buttonStyle, QToolButton::ToolButtonPopupMode popMode) +{ + d_ptr->groupWidget->addMenu(m, buttonStyle, popMode); +} + +/** + * @brief 获取内部管理的ButtonGroupWidget + * @return + */ +SARibbonButtonGroupWidget* SARibbonQuickAccessBar::buttonGroupWidget() +{ + return d_ptr->groupWidget; +} + +/** + * @brief 获取内部管理的ButtonGroupWidget + * @return + */ +const SARibbonButtonGroupWidget* SARibbonQuickAccessBar::buttonGroupWidget() const +{ + return d_ptr->groupWidget; +} + +/** + * @brief 图标尺寸 + * @param s + */ +void SARibbonQuickAccessBar::setIconSize(const QSize& s) +{ + d_ptr->groupWidget->setIconSize(s); +} + +/** + * @brief SARibbonQuickAccessBar::iconSize + * @return + */ +QSize SARibbonQuickAccessBar::iconSize() const +{ + return d_ptr->groupWidget->iconSize(); +} + +/*** End of inlined file: SARibbonQuickAccessBar.cpp ***/ + +/*** Start of inlined file: SARibbonTabBar.cpp ***/ +SARibbonTabBar::SARibbonTabBar(QWidget* parent) : QTabBar(parent), m_tabMargin(6, 0, 0, 0) +{ + setExpanding(false); +} + +const QMargins& SARibbonTabBar::tabMargin() const +{ + return (m_tabMargin); +} + +void SARibbonTabBar::setTabMargin(const QMargins& tabMargin) +{ + m_tabMargin = tabMargin; +} + +/*** End of inlined file: SARibbonTabBar.cpp ***/ + +/*** Start of inlined file: SARibbonControlButton.cpp ***/ +SARibbonControlButton::SARibbonControlButton(QWidget* parent) : QToolButton(parent) +{ +} + +SARibbonControlToolButton::SARibbonControlToolButton(QWidget* parent) : QToolButton(parent) +{ +} + +/*** End of inlined file: SARibbonControlButton.cpp ***/ + +/*** Start of inlined file: SARibbonMenu.cpp ***/ +#include + +SARibbonMenu::SARibbonMenu(QWidget* parent) : QMenu(parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +SARibbonMenu::SARibbonMenu(const QString& title, QWidget* parent) : QMenu(title, parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +QAction* SARibbonMenu::addRibbonMenu(SARibbonMenu* menu) +{ + return (QMenu::addMenu(menu)); +} + +SARibbonMenu* SARibbonMenu::addRibbonMenu(const QString& title) +{ + SARibbonMenu* menu = new SARibbonMenu(title, this); + + return (menu); +} + +SARibbonMenu* SARibbonMenu::addRibbonMenu(const QIcon& icon, const QString& title) +{ + SARibbonMenu* menu = new SARibbonMenu(title, this); + + menu->setIcon(icon); + return (menu); +} + +QAction* SARibbonMenu::addWidget(QWidget* w) +{ + QWidgetAction* action = new QWidgetAction(this); + + action->setDefaultWidget(w); + addAction(action); + return (action); +} + +/*** End of inlined file: SARibbonMenu.cpp ***/ + +/*** Start of inlined file: SARibbonPannelOptionButton.cpp ***/ +#include + +SARibbonPannelOptionButton::SARibbonPannelOptionButton(QWidget* parent) : QToolButton(parent) +{ + setAutoRaise(true); + setCheckable(false); + setToolButtonStyle(Qt::ToolButtonIconOnly); + setIconSize(QSize(10, 10)); + setIcon(QIcon(":/image/resource/ribbonPannelOptionButton.png")); +} + +/*** End of inlined file: SARibbonPannelOptionButton.cpp ***/ + +/*** Start of inlined file: SARibbonPannelItem.cpp ***/ +SARibbonPannelItem::SARibbonPannelItem(QWidget* widget) + : QWidgetItem(widget), rowIndex(-1), columnIndex(-1), action(nullptr), customWidget(false), rowProportion(Large) +{ +} + +bool SARibbonPannelItem::isEmpty() const +{ + return (action == nullptr || !action->isVisible()); +} + +/*** End of inlined file: SARibbonPannelItem.cpp ***/ + +/*** Start of inlined file: SARibbonPannelLayout.cpp ***/ +#include +#include + +#define SARibbonPannelLayout_DEBUG_PRINT 1 +#define HELP_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + QPen _pen(Qt::DashLine); \ + _pen.setColor(Qt::blue); \ + p.setPen(_pen); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) + +SARibbonPannelLayout::SARibbonPannelLayout(QWidget* p) + : QLayout(p), m_columnCount(0), m_expandFlag(false), m_dirty(true) +{ + setSpacing(1); + SARibbonPannel* tb = qobject_cast< SARibbonPannel* >(p); + + if (!tb) { + return; + } +} + +SARibbonPannelLayout::~SARibbonPannelLayout() +{ + // 参考QToolBarLayout + while (!m_items.isEmpty()) { + SARibbonPannelItem* item = m_items.takeFirst(); + if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action)) { + if (item->customWidget) { + widgetAction->releaseWidget(item->widget()); + } + } + delete item; + } +} + +/** + * @brief 通过action查找索引,用于actionEvent添加action用 + * @param action + * @return 没有查到返回-1 + */ +int SARibbonPannelLayout::indexByAction(QAction* action) const +{ + for (int i = 0; i < m_items.count(); ++i) { + if (m_items.at(i)->action == action) { + return (i); + } + } + return (-1); +} + +/** + * @brief 获取ribbonpannel + * @return + */ +SARibbonPannel* SARibbonPannelLayout::ribbonPannel() const +{ + return qobject_cast< SARibbonPannel* >(parentWidget()); +} + +void SARibbonPannelLayout::addItem(QLayoutItem* item) +{ + Q_UNUSED(item); + qWarning("SARibbonPannelLayout::addItem(): please use addAction() instead"); + return; +} + +/** + * @brief SARibbonPannel主要通过此函数来添加action + * @param act + * @param rp 布局策略 + */ +void SARibbonPannelLayout::insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp) +{ + index = qMax(0, index); + index = qMin(m_items.count(), index); + SARibbonPannelItem* item = createItem(act, rp); + + if (item) { + m_items.insert(index, item); + // 标记需要重新计算尺寸 + invalidate(); + } +} + +/** + * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 + * @param action + * @note 要去除OptionAction直接传入nullptr即可 + * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction + */ +void SARibbonPannelLayout::setOptionAction(QAction* action) +{ + SARibbonPannel* p = ribbonPannel(); + if (!p) { + return; + } + if (action) { + // 创建option action + if (nullptr == m_optionActionBtn) { + m_optionActionBtn = RibbonSubElementFactory->createRibbonPannelOptionButton(p); + QObject::connect(m_optionActionBtn, &SARibbonToolButton::triggered, p, &SARibbonPannel::actionTriggered); + // 确保m_optionActionBtn在label之上 + if (m_titleLabel) { + m_titleLabel->stackUnder(m_optionActionBtn); + } + } + m_optionActionBtn->setDefaultAction(action); + if (action->icon().isNull()) { + m_optionActionBtn->setIcon(QIcon(":/image/resource/ribbonPannelOptionButton.png")); + } + // 标记需要重新计算尺寸 + invalidate(); + } else { + // 取消option action + if (m_optionActionBtn) { + m_optionActionBtn->hide(); + m_optionActionBtn->deleteLater(); + m_optionActionBtn = nullptr; + // 标记需要重新计算尺寸 + invalidate(); + } + } +} + +/** + * @brief 判断是否存在OptionAction + * @return 存在返回true + */ +bool SARibbonPannelLayout::isHaveOptionAction() const +{ + return (m_optionActionBtn != nullptr); +} + +QLayoutItem* SARibbonPannelLayout::itemAt(int index) const +{ + if ((index < 0) || (index >= m_items.count())) { + return (nullptr); + } + return (m_items.at(index)); +} + +QLayoutItem* SARibbonPannelLayout::takeAt(int index) +{ + if ((index < 0) || (index >= m_items.count())) { + return (nullptr); + } + SARibbonPannelItem* item = m_items.takeAt(index); + + QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(item->action); + + if ((widgetAction != 0) && item->customWidget) { + widgetAction->releaseWidget(item->widget()); + } else { + // destroy the QToolButton/QToolBarSeparator + item->widget()->hide(); + item->widget()->deleteLater(); + } + + invalidate(); + return (item); +} + +int SARibbonPannelLayout::count() const +{ + return (m_items.count()); +} + +bool SARibbonPannelLayout::isEmpty() const +{ + + return (m_items.isEmpty()); +} + +void SARibbonPannelLayout::invalidate() +{ + m_dirty = true; + QLayout::invalidate(); +} + +Qt::Orientations SARibbonPannelLayout::expandingDirections() const +{ + return (Qt::Horizontal); +} + +QSize SARibbonPannelLayout::minimumSize() const +{ + return (m_sizeHint); +} + +QSize SARibbonPannelLayout::sizeHint() const +{ +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + if (SARibbonPannel* pannel = ribbonPannel()) { + qDebug() << "| |-SARibbonPannelLayout sizeHint,sizeHint = " << m_sizeHint; + } +#endif + return (m_sizeHint); +} + +/** + * @brief 通过action获取SARibbonPannelItem + * @param action + * @return 如果没有返回nullptr + */ +SARibbonPannelItem* SARibbonPannelLayout::pannelItem(QAction* action) const +{ + int index = indexByAction(action); + + if (index >= 0) { + return (m_items[ index ]); + } + return (nullptr); +} + +/** + * @brief 获取最后一个添加的item + * @return 如果没有返回nullptr + */ +SARibbonPannelItem* SARibbonPannelLayout::lastItem() const +{ + if (m_items.isEmpty()) { + return (nullptr); + } + return (m_items.last()); +} + +/** + * @brief 获取最后生成的窗口 + * @return 如果无窗口或者item为空,返回nullptr + */ +QWidget* SARibbonPannelLayout::lastWidget() const +{ + SARibbonPannelItem* item = lastItem(); + + if (item) { + return (item->widget()); + } + return (nullptr); +} + +/** + * @brief 移动两个item + * @param from + * @param to + * @note 移动完后所有都失效,需要重新布局 + */ +void SARibbonPannelLayout::move(int from, int to) +{ + if (from == to) { + return; + } + if (to < 0) { + to = 0; + } + if (to >= count()) { + to = count() - 1; + } + m_items.move(from, to); + invalidate(); +} + +/** + * @brief 判断是否需要重新布局 + * @return + */ +bool SARibbonPannelLayout::isDirty() const +{ + return (m_dirty); +} + +void SARibbonPannelLayout::updateGeomArray() +{ + updateGeomArray(geometry()); +} + +/** + * @brief 布局所有action + */ +void SARibbonPannelLayout::doLayout() +{ +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + if (SARibbonPannel* pannel = ribbonPannel()) { + qDebug() << "| |-SARibbonPannelLayout layoutActions,pannel name = " << pannel->pannelName(); + } +#endif + if (isDirty()) { + updateGeomArray(); + } + QList< QWidget* > showWidgets, hideWidgets; + SARibbonPannel* pannel = ribbonPannel(); + for (SARibbonPannelItem* item : qAsConst(m_items)) { + if (item->isEmpty()) { + hideWidgets << item->widget(); + } else { + item->setGeometry(item->itemWillSetGeometry); + showWidgets << item->widget(); + } + } + + // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 + for (QWidget* w : qAsConst(showWidgets)) { + if (!w->isVisible()) + w->show(); + } + for (QWidget* w : qAsConst(hideWidgets)) { + if (w->isVisible()) + w->hide(); + } + // 布局label + if (m_titleLabel) { + if (isEnableShowPannelTitle()) { + m_titleLabel->setGeometry(m_titleLabelGeometry); + if (!m_titleLabel->isVisibleTo(pannel)) { + m_titleLabel->show(); + } + } else { + if (m_titleLabel->isVisibleTo(pannel)) { + m_titleLabel->hide(); + } + } + } + // 布局m_optionActionBtn + if (m_optionActionBtn) { + m_optionActionBtn->setGeometry(m_optionActionBtnGeometry); + m_optionActionBtn->setIconSize(QSize(m_optionActionBtnGeometry.width(), m_optionActionBtnGeometry.height())); + } +} + +/** + * @brief 把action转换为item + * + * 此函数参考QToolBarItem *QToolBarLayout::createItem(QAction *action) + * + * 对于普通QAction,此函数会创建SARibbonToolButton,SARibbonToolButton的类型参考SARibbonPannelItem::RowProportion, + * @param action + * @param rp 行高占比情况 + * @return 转换的SARibbonPannelItem + * @note 每个SARibbonPannelItem最终都会携带一个widget,传入的是QWidgetAction的话,会直接使用QWidgetAction带的widget, + * 否则会内部生成一个SARibbonToolButton + * + */ +SARibbonPannelItem* SARibbonPannelLayout::createItem(QAction* action, SARibbonPannelItem::RowProportion rp) +{ + bool customWidget = false; + QWidget* widget = nullptr; + SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); + + if (!pannel) { + // 在没有pannel这个函数会返回nullptr + return (nullptr); + } + if (QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action)) { + widget = widgetAction->requestWidget(pannel); + if (widget != nullptr) { + widget->setAttribute(Qt::WA_LayoutUsesWidgetRect); + customWidget = true; // 标记为true,在移除的时候是不会对这个窗口进行删除,false默认会进行删除如SARibbonSeparatorWidget和SARibbonToolButton + } + } else if (action->isSeparator()) { + SARibbonSeparatorWidget* sep = RibbonSubElementFactory->createRibbonSeparatorWidget(pannel); + widget = sep; + } + // 不是widget,自动生成SARibbonToolbutton + if (!widget) { + SARibbonToolButton::RibbonButtonType buttonType = ((rp == SARibbonPannelItem::Large) ? SARibbonToolButton::LargeButton + : SARibbonToolButton::SmallButton); + + SARibbonToolButton* button = RibbonSubElementFactory->createRibbonToolButton(pannel); + button->setFocusPolicy(Qt::NoFocus); + button->setButtonType(buttonType); + button->setDefaultAction(action); + // 属性设置 + QToolButton::ToolButtonPopupMode popMode = SARibbonPannel::getActionToolButtonPopupModeProperty(action); + button->setPopupMode(popMode); + // 根据QAction的属性设置按钮的大小 + + QObject::connect(button, &SARibbonToolButton::triggered, pannel, &SARibbonPannel::actionTriggered); + widget = button; + } + // 这时总会有widget + widget->hide(); + SARibbonPannelItem* result = new SARibbonPannelItem(widget); + + result->rowProportion = rp; + result->customWidget = customWidget; + result->action = action; + return (result); +} + +/** + * @brief 更新尺寸 + */ +void SARibbonPannelLayout::updateGeomArray(const QRect& setrect) +{ + SARibbonPannel* pannel = qobject_cast< SARibbonPannel* >(parentWidget()); + + if (!pannel) { + return; + } + + const int height = setrect.height(); + const QMargins& mag = contentsMargins(); + const int spacing = this->spacing(); + int x = mag.left(); + const int yBegin = mag.top(); + int titleH = (m_titleHeight >= 0) ? m_titleHeight : 0; // 防止负数影响 + int titleSpace = (m_titleHeight >= 0) ? m_titleSpace : 0; // 对于没有标题的情况,spacing就不生效 + if (!isEnableShowPannelTitle()) { + titleH = 0; + titleSpace = 0; + } + // 获取pannel的布局模式 3行或者2行 + // rowcount 是ribbon的行,有2行和3行两种 + const short rowCount = (pannel->pannelLayoutMode() == SARibbonPannel::ThreeRowMode) ? 3 : 2; + // largeHeight是对应large占比的高度 + const int largeHeight = height - mag.bottom() - mag.top() - titleH - titleSpace; + const int yTitleBegin = height - mag.bottom() - titleH; + m_largeHeight = largeHeight; + // 计算smallHeight的高度 + const int smallHeight = (largeHeight - (rowCount - 1) * spacing) / rowCount; + // Medium行的y位置 + const int yMediumRow0 = (2 == rowCount) ? yBegin : (yBegin + ((largeHeight - 2 * smallHeight) / 3)); + const int yMediumRow1 = (2 == rowCount) ? (yBegin + smallHeight + spacing) + : (yBegin + ((largeHeight - 2 * smallHeight) / 3) * 2 + smallHeight); + // Small行的y位置 + const int ySmallRow0 = yBegin; + const int ySmallRow1 = yBegin + smallHeight + spacing; + const int ySmallRow2 = yBegin + 2 * (smallHeight + spacing); + // row用于记录下个item应该属于第几行,item->rowIndex用于记录当前处于第几行, + // item->rowIndex主要用于SARibbonPannelItem::Medium + short row = 0; + int column = 0; + // 记录每列最大的宽度 + int columMaxWidth = 0; + // 记录总宽度 + int totalWidth = 0; + + int itemCount = m_items.count(); + +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + QString debug_print__log__; +#endif + // 本列第一、二行占比 + SARibbonPannelItem::RowProportion thisColumnRP0 = SARibbonPannelItem::None; + SARibbonPannelItem* lastGeomItem = nullptr; // 记录最后一个设置位置的item + for (int i = 0; i < itemCount; ++i) { + SARibbonPannelItem* item = m_items.at(i); + if (item->isEmpty()) { + // 如果是hide就直接跳过 + item->rowIndex = -1; + item->columnIndex = -1; + continue; + } + + QSize hint = item->sizeHint(); +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + if (SARibbonToolButton* tb = qobject_cast< SARibbonToolButton* >(item->widget())) { + auto ss__ = tb->sizeHint(); + debug_print__log__ += QString("| | |-[%1]SARibbonToolButton.sizeHint=(%2,%3),ButtonText=%4\n") + .arg(i) + .arg(ss__.width()) + .arg(ss__.height()) + .arg(tb->text()); + } +#endif + Qt::Orientations exp = item->expandingDirections(); + if (item->widget()) { + // 有窗口是水平扩展,则标记为扩展 + if ((item->widget()->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag)) { + m_expandFlag = true; + } + } + SARibbonPannelItem::RowProportion rp = item->rowProportion; + if (SARibbonPannelItem::None == rp) { + // 为定义行占比但是垂直扩展,就定义为Large占比,否则就是small占比 + if (exp & Qt::Vertical) { + rp = SARibbonPannelItem::Large; + } else { + rp = SARibbonPannelItem::Small; + } + } + // 开始根据占比和layoutmode来布局 + switch (rp) { + case SARibbonPannelItem::Large: { + // !!在Large,如果不是处于新列的第一行,就需要进行换列处理 + // 把large一直设置在下一列的开始 + if (row != 0) { + x += (columMaxWidth + spacing); + ++column; + } + // + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yBegin, hint.width(), largeHeight); + columMaxWidth = hint.width(); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } break; + + case SARibbonPannelItem::Medium: { + // 2行模式下Medium和small等价 + if (2 == rowCount) { + if (0 == row) { + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Medium; + columMaxWidth = hint.width(); + // 下个row为1 + row = 1; + // x不变 + } else { + item->rowIndex = 1; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); + // 和上个进行比较得到最长宽度 + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } + } else { + // 3行模式 + if (0 == row) { + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Medium; + columMaxWidth = hint.width(); + row = 1; + // x不变 + } else if (1 == row) { + item->rowIndex = 1; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } else { + // 这种模式一般情况会发生在当前列前两行是Small,添加了一个Medium + // 这时需要先换列 + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + ++column; + // 换列后此时等价于0 == row + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, yMediumRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Medium; + columMaxWidth = hint.width(); + row = 1; + } + } + } break; + + case SARibbonPannelItem::Small: { + if (0 == row) { + // 第一行 + item->rowIndex = 0; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, ySmallRow0, hint.width(), smallHeight); + thisColumnRP0 = SARibbonPannelItem::Small; + columMaxWidth = hint.width(); + // 下个row为1 + row = 1; + // x不变 + } else if (1 == row) { + // 第二行 + item->rowIndex = 1; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, ySmallRow1, hint.width(), smallHeight); + if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { + // 三行模式,并且第一行是Medium + item->itemWillSetGeometry = QRect(x, yMediumRow1, hint.width(), smallHeight); + } + // 和上个进行比较得到最长宽度 + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 这里要看两行还是三行,确定是否要换列 + if (2 == rowCount) { + // 两行模式,换列 + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } else { + // 三行模式,继续增加行数 + row = 2; + // x不变 + } + if ((3 == rowCount) && (SARibbonPannelItem::Medium == thisColumnRP0)) { + // 三行模式,并且第一行是Medium,换列 + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } + } else { + // 第三行 + item->rowIndex = 2; + item->columnIndex = column; + item->itemWillSetGeometry = QRect(x, ySmallRow2, hint.width(), smallHeight); + // 和上个进行比较得到最长宽度 + columMaxWidth = qMax(columMaxWidth, hint.width()); + // 换列,x自动递增到下个坐标,列数增加,行数归零,最大列宽归零 + x += (columMaxWidth + spacing); + row = 0; + columMaxWidth = 0; + ++column; + } + } break; + + default: + // 不可能出现 + break; + } + lastGeomItem = item; + } + // 最后一个元素,更新列数 + // 2022-06-20 此句本来在循环里面,如果最后一个元素隐藏,会导致无法到达此判断导致异常 + if (lastGeomItem) { // 最后一个元素,更新totalWidth + if (lastGeomItem->columnIndex != column) { + // 说明最后一个元素处于最后位置,触发了换列,此时真实列数需要减1,直接等于column索引 + m_columnCount = column; + // 由于最后一个元素触发了换列,x值是新一列的位置,直接作为totalWidth要减去已经加入的spacing + totalWidth = x - spacing + mag.right(); + } else { + // 说明最后一个元素处于非最后位置,没有触发下一个换列,此时真实列数等于column索引+1 + m_columnCount = column + 1; + // 由于最后一个元素未触发换列,需要计算totalWidth + totalWidth = x + columMaxWidth + mag.right(); + } + } + + // 在设置完所有窗口后,再设置扩展属性的窗口 + if (totalWidth < setrect.width() && (setrect.width() - totalWidth) > 10) { + // 说明可以设置扩展属性的窗口 + recalcExpandGeomArray(setrect); + } + // 布局label + bool isTitleWidthThanPannel = false; + if (isEnableShowPannelTitle()) { + m_titleLabelGeometry.setRect(mag.left(), yTitleBegin, setrect.width() - mag.left() - mag.right(), titleH); + // 这里要确认标题宽度是否大于totalWidth,如果大于,则要把标题的宽度作为totalwidth + QFontMetrics fm = m_titleLabel->fontMetrics(); + int textWidth = SA_FONTMETRICS_WIDTH(fm, pannel->pannelName()); + textWidth += 4; + if (totalWidth < textWidth) { + totalWidth = textWidth; + isTitleWidthThanPannel = true; // 说明标题的长度大于按钮布局的长度 + } + } + // 布局optionActionButton + + if (isHaveOptionAction()) { + QSize optBtnSize = optionActionButtonSize(); + if (isEnableShowPannelTitle()) { + // 有标题 + m_optionActionBtnGeometry.setRect(m_titleLabelGeometry.right() - m_titleLabelGeometry.height(), + m_titleLabelGeometry.y(), + m_titleLabelGeometry.height(), + m_titleLabelGeometry.height()); + + // 特殊情况,如果pannel的标题长度大于totalWidth,那么说明totalWidth比较短 + // 这时候,optionActionBtn的宽度要加上到标题宽度上 + if (isTitleWidthThanPannel) { + // 由于文字是居中对齐,因此要扩展2个按钮的宽度 + totalWidth += (2 * titleH); + } + } else { + // 无标题 + m_optionActionBtnGeometry.setRect(setrect.right() - optBtnSize.width() - mag.right(), + setrect.bottom() - optBtnSize.height() - mag.bottom(), + optBtnSize.width(), + optBtnSize.height()); + totalWidth += optBtnSize.width(); + } + } + // 刷新sizeHint + int heightHint = SARibbonPannel::pannelHeightHint(pannel->fontMetrics(), pannel->pannelLayoutMode(), titleH); + this->m_sizeHint = QSize(totalWidth, heightHint); +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| |-SARibbonPannelLayout updateGeomArray(" << setrect << "),pannel name = " << pannel->pannelName() + << "\n| | |-size hint =" << this->m_sizeHint // + << "\n| | |-totalWidth=" << totalWidth // + << "\n| | |-last x=" << x // + << "\n| | |-columMaxWidth=" << columMaxWidth // + << "\n| | |-spacing=" << spacing // + << "\n| | |-mag=" << mag // + << "\n| | |-largeHeight=" << largeHeight // + << "\n| | |-smallHeight=" << smallHeight // + ; + qDebug().noquote() << debug_print__log__; +#endif +} + +void SARibbonPannelLayout::recalcExpandGeomArray(const QRect& setrect) +{ + // 计算能扩展的尺寸 + int expandwidth = setrect.width() - this->m_sizeHint.width(); + + if (expandwidth <= 0) { + // 没有必要设置 + return; + } + // 列扩展信息 + struct _columnExpandInfo + { + int oldColumnWidth = 0; ///< 原来的列宽 + int columnMaximumWidth = -1; ///< 列的最大宽度 + int columnExpandedWidth = 0; ///< 扩展后列的宽度 + QList< SARibbonPannelItem* > expandItems; + }; + // 此变量用于记录可以水平扩展的列和控件,在布局结束后,如果还有空间,就把水平扩展的控件进行扩展 + QMap< int, _columnExpandInfo > columnExpandInfo; + + for (SARibbonPannelItem* item : qAsConst(m_items)) { + if ((!item->isEmpty()) && item->expandingDirections() & Qt::Horizontal) { + // 只获取可见的 + QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.find(item->columnIndex); + if (i == columnExpandInfo.end()) { + i = columnExpandInfo.insert(item->columnIndex, _columnExpandInfo()); + } + i.value().expandItems.append(item); + } + } + if (columnExpandInfo.size() <= 0) { + // 没有需要扩展的就退出 + return; + } + // 获取完可扩展的列和控件后,计算对应的列的尺寸 + // 计算能扩展的尺寸 + int oneColCanexpandWidth = expandwidth / columnExpandInfo.size(); + + for (QMap< int, _columnExpandInfo >::iterator i = columnExpandInfo.begin(); i != columnExpandInfo.end();) { + int& oldColumnWidth = i.value().oldColumnWidth; + int& columnMaximumWidth = i.value().columnMaximumWidth; + this->columnWidthInfo(i.key(), oldColumnWidth, columnMaximumWidth); + if ((oldColumnWidth <= 0) || (oldColumnWidth > columnMaximumWidth)) { + // 如果小于0说明没有这个列,这种属于异常,删除继续 + // oldColumnWidth > columnMaximumWidth也是异常 + i = columnExpandInfo.erase(i); + continue; + } + // 开始调整 + int colwidth = oneColCanexpandWidth + oldColumnWidth; // 先扩展了 + if (colwidth >= columnMaximumWidth) { + // 过最大宽度要求 + i.value().columnExpandedWidth = columnMaximumWidth; + } else { + i.value().columnExpandedWidth = colwidth; + } + ++i; + } + // 从新调整尺寸 + // 由于会涉及其他列的变更,因此需要所有都遍历一下 + for (auto i = columnExpandInfo.begin(); i != columnExpandInfo.end(); ++i) { + int moveXLen = i.value().columnExpandedWidth - i.value().oldColumnWidth; + for (SARibbonPannelItem* item : qAsConst(m_items)) { + if (item->isEmpty() || (item->columnIndex < i.key())) { + // 之前的列不用管 + continue; + } + if (item->columnIndex == i.key()) { + // 此列的扩展 + if (i.value().expandItems.contains(item)) { + // 此列需要扩展的item才扩展尺寸 + item->itemWillSetGeometry.setWidth(i.value().columnExpandedWidth); + } else { + // 此列不扩展的模块保持原来的尺寸 + continue; + } + } else { + // 后面的移动 + item->itemWillSetGeometry.moveLeft(item->itemWillSetGeometry.x() + moveXLen); + } + } + } +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| |-SARibbonPannelLayout recalcExpandGeomArray(" << setrect + << ") pannelName=" << ribbonPannel()->pannelName() // + << ",expandwidth=" << expandwidth // + ; +#endif +} + +/** + * @brief 根据列数,计算窗口的宽度,以及最大宽度 + * @param colindex + * @param width 如果传入没有这个列,返回-1 + * @param maximum 如果传入没有这个列,返回-1 + */ +void SARibbonPannelLayout::columnWidthInfo(int colindex, int& width, int& maximum) const +{ + width = -1; + maximum = -1; + for (SARibbonPannelItem* item : m_items) { + if (!item->isEmpty() && (item->columnIndex == colindex)) { + width = qMax(width, item->itemWillSetGeometry.width()); + maximum = qMax(maximum, item->widget()->maximumWidth()); + } + } +} + +SARibbonPannelLabel* SARibbonPannelLayout::pannelTitleLabel() const +{ + return m_titleLabel; +} + +/** + * @brief 获取optionAction 按钮尺寸 + * @return + */ +QSize SARibbonPannelLayout::optionActionButtonSize() const +{ + return (isEnableShowPannelTitle() ? QSize(12, 12) : QSize(m_titleHeight, m_titleHeight)); +} + +void SARibbonPannelLayout::setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel) +{ + m_titleLabel = newTitleLabel; + // 确保m_optionActionBtn在label之上 + if (m_optionActionBtn) { + if (m_titleLabel) { + m_titleLabel->stackUnder(m_optionActionBtn); + } + } +} + +/** + * @brief 标题区域和按钮的间隔 + * @return + */ +int SARibbonPannelLayout::pannelTitleSpace() const +{ + return m_titleSpace; +} + +/** + * @brief 设置标题区域和按钮的间隔 + * @param newTitleSpace + */ +void SARibbonPannelLayout::setPannelTitleSpace(int newTitleSpace) +{ + if (m_titleSpace == newTitleSpace) { + return; + } + m_titleSpace = newTitleSpace; + invalidate(); +} + +/** + * @brief 标题高度 + * @return + */ +int SARibbonPannelLayout::pannelTitleHeight() const +{ + return m_titleHeight; +} + +/** + * @brief 设置标题高度 + * @param newTitleHeight + */ +void SARibbonPannelLayout::setPannelTitleHeight(int newTitleHeight) +{ + if (m_titleHeight == newTitleHeight) { + return; + } + m_titleHeight = newTitleHeight; + invalidate(); +} + +/** + * @brief 判断是否存在标题 + * @return + */ +bool SARibbonPannelLayout::isEnableShowPannelTitle() const +{ + return m_enableShowTitle; +} + +/** + * @brief 设置显示标题 + * @param on + */ +void SARibbonPannelLayout::setEnableShowPannelTitle(bool on) +{ + if (m_enableShowTitle == on) { + return; + } + m_enableShowTitle = on; + invalidate(); +} + +/** + * @brief 大按钮的高度 + * @return + */ +int SARibbonPannelLayout::largeButtonHeight() const +{ + return m_largeHeight; +} + +void SARibbonPannelLayout::setGeometry(const QRect& rect) +{ + QRect old = geometry(); + if (old == rect) { + return; + } +#if SARibbonPannelLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "| |----->SARibbonPannelLayout.setGeometry(" << rect << "(" << ribbonPannel()->pannelName() << ")======="; +#endif + QLayout::setGeometry(rect); + m_dirty = false; + updateGeomArray(rect); + doLayout(); +} + +/*** End of inlined file: SARibbonPannelLayout.cpp ***/ + +/*** Start of inlined file: SARibbonPannel.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SARibbonPannel_DEBUG_PRINT +#define SARibbonPannel_DEBUG_PRINT 0 +#endif + +#define HELP_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + QPen _pen(Qt::DashLine); \ + _pen.setColor(Qt::blue); \ + p.setPen(_pen); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) +//=============================================================== +// SARibbonPannelLabel +//=============================================================== + +SARibbonPannelLabel::SARibbonPannelLabel(QWidget* parent) : QLabel(parent) +{ +} + +//=============================================================== +// SARibbonPannel::PrivateData +//=============================================================== + +class SARibbonPannel::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonPannel) +public: + PrivateData(SARibbonPannel* p); + // 根据m_pannelLayoutMode返回gridLayout应该增加的行数 + int rowadded(); + void createLayout(); + // 获取layout + SARibbonPannelLayout* pannelLayout() const; + // 返回最后一个添加的action对应的button,前提是最后一个是toolbutton,否则返回nullptr + SARibbonToolButton* lastAddActionButton(); + // 重置labe的字体,这个主要是为了让pannel的标题字体能适应标题高度 + void resetTitleLabelFont(); + // 标题 + QString pannelName() const; + void setPannelName(const QString& title); + +public: + bool m_isCanCustomize { true }; ///< 记录是否可自定义 + SARibbonPannel::PannelLayoutMode m_pannelLayoutMode { SARibbonPannel::ThreeRowMode }; ///< pannel的布局模式,默认为3行模式ThreeRowMode + SARibbonPannelOptionButton* m_optionActionButton { nullptr }; ///< 标题栏的y距离 + SARibbonPannelLabel* m_label { nullptr }; +}; + +SARibbonPannel::PrivateData::PrivateData(SARibbonPannel* p) : q_ptr(p) +{ + createLayout(); +} + +int SARibbonPannel::PrivateData::rowadded() +{ + switch (m_pannelLayoutMode) { + case SARibbonPannel::ThreeRowMode: + return (2); + + case SARibbonPannel::TwoRowMode: + return (3); + + default: + break; + } + return (2); +} + +void SARibbonPannel::PrivateData::createLayout() +{ + m_label = new SARibbonPannelLabel(q_ptr); + m_label->setAlignment(Qt::AlignCenter); + SARibbonPannelLayout* lay = new SARibbonPannelLayout(q_ptr); + lay->setPannelTitleLabel(m_label); + lay->setSpacing(2); + lay->setContentsMargins(2, 2, 2, 2); +} + +SARibbonPannelLayout* SARibbonPannel::PrivateData::pannelLayout() const +{ + return qobject_cast< SARibbonPannelLayout* >(q_ptr->layout()); +} + +SARibbonToolButton* SARibbonPannel::PrivateData::lastAddActionButton() +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return (qobject_cast< SARibbonToolButton* >(lay->lastWidget())); + } + return (nullptr); +} + +/** + * @brief 重置label的字体 + * + * @note 此函数必须在布局设置后调用 + */ +void SARibbonPannel::PrivateData::resetTitleLabelFont() +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + int h = lay->pannelTitleHeight(); + QFont f = q_ptr->font(); + f.setPixelSize(h * 0.8); + if (m_label) { + m_label->setFont(f); + } + } +} + +QString SARibbonPannel::PrivateData::pannelName() const +{ + if (m_label) { + return m_label->text(); + } + return (QString()); +} + +void SARibbonPannel::PrivateData::setPannelName(const QString& title) +{ + if (m_label) { + m_label->setText(title); + } +} + +//================================================== +// SARibbonPannel +//================================================== + +SARibbonPannel::SARibbonPannel(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) +{ + setPannelLayoutMode(ThreeRowMode); +} + +SARibbonPannel::SARibbonPannel(const QString& name, QWidget* parent) + : QFrame(parent), d_ptr(new SARibbonPannel::PrivateData(this)) +{ + setPannelLayoutMode(ThreeRowMode); + setPannelName(name); +} + +SARibbonPannel::~SARibbonPannel() +{ +} + +/** + * @brief 把action的行属性设置进action中,action自身携带了行属性 + * @param action + * @param rp + */ +void SARibbonPannel::setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp) +{ + Q_CHECK_PTR(action); + action->setProperty(SA_ActionPropertyName_RowProportion, static_cast< int >(rp)); +} + +/** + * @brief 获取action的行属性 + * @param action + * @return + */ +SARibbonPannelItem::RowProportion SARibbonPannel::getActionRowProportionProperty(QAction* action) +{ + bool isok = false; + int r = action->property(SA_ActionPropertyName_RowProportion).toInt(&isok); + + if (isok) { + return (static_cast< SARibbonPannelItem::RowProportion >(r)); + } + return (SARibbonPannelItem::Large); +} + +/** + * @brief 设置action的ToolButtonPopupMode属性 + * @param action + * @param popMode + */ +void SARibbonPannel::setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + Q_CHECK_PTR(action); + action->setProperty(SA_ActionPropertyName_ToolButtonPopupMode, static_cast< int >(popMode)); +} + +/** + * @brief 获取action的ToolButtonPopupMode属性 + * @param action + * @return + */ +QToolButton::ToolButtonPopupMode SARibbonPannel::getActionToolButtonPopupModeProperty(QAction* action) +{ + bool isok = false; + int r = action->property(SA_ActionPropertyName_ToolButtonPopupMode).toInt(&isok); + + if (isok) { + return (static_cast< QToolButton::ToolButtonPopupMode >(r)); + } + return (QToolButton::InstantPopup); +} + +/** + * @brief 设置action的ToolButtonStyle属性 + * @param action + * @param buttonStyle + */ +void SARibbonPannel::setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle) +{ + Q_CHECK_PTR(action); + action->setProperty(SA_ActionPropertyName_ToolButtonStyle, static_cast< int >(buttonStyle)); +} + +/** + * @brief 获取action的ToolButtonStyle属性 + * @param action + * @return + */ +Qt::ToolButtonStyle SARibbonPannel::getActionToolButtonStyleProperty(QAction* action) +{ + bool isok = false; + int r = action->property(SA_ActionPropertyName_ToolButtonStyle).toInt(&isok); + + if (isok) { + return (static_cast< Qt::ToolButtonStyle >(r)); + } + return (Qt::ToolButtonIconOnly); +} + +/** + * @brief 添加action + * + * action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + * @param action + * @param rp 指定action的行占比 + * @param popMode 菜单弹出样式 + */ +void SARibbonPannel::addAction(QAction* action, SARibbonPannelItem::RowProportion rp) +{ + Q_CHECK_PTR(action); + setActionRowProportionProperty(action, rp); + addAction(action); +} + +/** + * @brief 添加一个action + * @param act + * @param popMode 按钮的样式 + * @param rp action在pannel中的占位情况,默认是大图标 + */ +void SARibbonPannel::addAction(QAction* act, QToolButton::ToolButtonPopupMode popMode, SARibbonPannelItem::RowProportion rp) +{ + Q_CHECK_PTR(act); + setActionRowProportionProperty(act, rp); + setActionToolButtonPopupModeProperty(act, popMode); + addAction(act); +} + +/** + @brief 添加大图标 + + action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + @param action + */ +void SARibbonPannel::addLargeAction(QAction* action) +{ + addAction(action, SARibbonPannelItem::Large); +} + +/** + @brief 在三栏模式下,强制加为2栏action + @note 在两行模式下,Medium和Small等价 + 主要应用在ThreeRowMode下 + + action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + @param action + */ +void SARibbonPannel::addMediumAction(QAction* action) +{ + addAction(action, SARibbonPannelItem::Medium); +} + +/** + @brief 添加小图标 + + action实际对应了一个toolbutton,如果想找到对应的toolbutton,使用@ref actionToRibbonToolButton + @param action + */ +void SARibbonPannel::addSmallAction(QAction* action) +{ + addAction(action, SARibbonPannelItem::Small); +} + +void SARibbonPannel::addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + addAction(action, popMode, SARibbonPannelItem::Small); +} + +void SARibbonPannel::addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + addAction(action, popMode, SARibbonPannelItem::Large); +} + +void SARibbonPannel::addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode) +{ + addAction(action, popMode, SARibbonPannelItem::Medium); +} + +/** + * @brief 生成并添加一个action + * + * 如果不对此action做操作,SARibbonPannel将管理此action + * + * @note action的父对象将设置为SARibbonPannel,SARibbonPannel在删除时将会删除子对象,会把这个action也删除, + * 如果不想此action也删除,需要对action重新设置父对象 + * + * @param text action的文字 + * @param icon action的图标 + * @param popMode 按钮的样式 + * @param rp action在pannel中的占位情况,默认是大图标 + * @return 返回添加的action + */ +QAction* SARibbonPannel::addAction(const QString& text, const QIcon& icon, QToolButton::ToolButtonPopupMode popMode, SARibbonPannelItem::RowProportion rp) +{ + QAction* action = new QAction(icon, text, this); + addAction(action, popMode, rp); + return (action); +} + +/** + * @brief 添加一个普通菜单 + * @param menu + * @param rp + * @param popMode,菜单弹出模式,默认InstantPopup模式 + * @return + */ +void SARibbonPannel::addMenu(QMenu* menu, SARibbonPannelItem::RowProportion rp, QToolButton::ToolButtonPopupMode popMode) +{ + Q_CHECK_PTR(menu); + QAction* action = menu->menuAction(); + action->setIcon(menu->icon()); + action->setText(menu->title()); + action->setObjectName("action." + menu->objectName()); + addAction(action, popMode, rp); +} + +void SARibbonPannel::addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) +{ + addMenu(menu, SARibbonPannelItem::Large, popMode); +} + +void SARibbonPannel::addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode) +{ + addMenu(menu, SARibbonPannelItem::Small, popMode); +} + +/** + * @brief 添加窗口 + * + * @param w + * @param rp + * @return 返回action(QWidgetAction) + * @note SARibbonPannel并不会管理此窗口内存,在delete SARibbonPannel时,此窗口如果父对象不是SARibbonPannel将不会被删除 + */ +QAction* SARibbonPannel::addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp) +{ + QWidgetAction* action = new QWidgetAction(this); + + action->setDefaultWidget(w); + action->setIcon(w->windowIcon()); + action->setText(w->windowTitle()); + action->setObjectName("action." + w->objectName()); + w->setAttribute(Qt::WA_Hover); + setActionRowProportionProperty(action, rp); + addAction(action); + return (action); +} + +/** + * @brief 添加窗口,占用ribbon的一行 + * @param w + * @return 返回action(QWidgetAction) + */ +QAction* SARibbonPannel::addSmallWidget(QWidget* w) +{ + return (addWidget(w, SARibbonPannelItem::Small)); +} + +/** + * @brief 添加窗口,占用ribbon的一行 + * @param w + * @return + */ +QAction* SARibbonPannel::addMediumWidget(QWidget* w) +{ + return (addWidget(w, SARibbonPannelItem::Medium)); +} + +/** + * @brief 添加窗口,占用所有行 + * @param w + * @return 返回action(QWidgetAction) + */ +QAction* SARibbonPannel::addLargeWidget(QWidget* w) +{ + return (addWidget(w, SARibbonPannelItem::Large)); +} + +/** + * @brief SARibbonPannel::addGallery + * @return + * @note SARibbonPannel将拥有SARibbonGallery的管理权 + */ +SARibbonGallery* SARibbonPannel::addGallery(bool expanding) +{ + SARibbonGallery* gallery = RibbonSubElementFactory->createRibbonGallery(this); + + addWidget(gallery, SARibbonPannelItem::Large); + if (expanding) { + setExpanding(expanding); + } + return (gallery); +} + +/** + * @brief 添加分割线 + * @param top 上边距 @default 6 + * @param bottom 下边距 @default 6 + */ +QAction* SARibbonPannel::addSeparator() +{ + QAction* action = new QAction(this); + + action->setSeparator(true); + setActionRowProportionProperty(action, SARibbonPannelItem::Large); + addAction(action); + return (action); +} + +/** + * @brief 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr + * @param action + * @return 如果action没有对应的button,就返回nullptr + */ +SARibbonToolButton* SARibbonPannel::actionToRibbonToolButton(QAction* action) +{ +#if 0 + SARibbonPannelLayout* lay = qobject_cast< SARibbonPannelLayout* >(layout()); + + if (lay) { + int index = lay->indexOf(action); + if (index == -1) { + return (nullptr); + } + QLayoutItem* item = lay->takeAt(index); + SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(item ? item->widget() : nullptr); + return (btn); + } + return (nullptr); +#else + for (auto obj : qAsConst(children())) { + if (obj->isWidgetType()) { + if (SARibbonToolButton* btn = qobject_cast< SARibbonToolButton* >(obj)) { + if (btn->defaultAction() == action) { + return btn; + } + } + } + } + return (nullptr); +#endif +} + +/** + * @brief 获取pannel下面的所有toolbutton + * @return + */ +QList< SARibbonToolButton* > SARibbonPannel::ribbonToolButtons() const +{ + const QObjectList& objs = children(); + QList< SARibbonToolButton* > res; + + for (QObject* o : objs) { + SARibbonToolButton* b = qobject_cast< SARibbonToolButton* >(o); + if (b) { + res.append(b); + } + } + return (res); +} + +/** + * @brief 设置PannelLayoutMode + * @param mode + */ +void SARibbonPannel::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode mode) +{ + if (d_ptr->m_pannelLayoutMode == mode) { + return; + } + d_ptr->m_pannelLayoutMode = mode; + updateItemGeometry(); +} + +SARibbonPannel::PannelLayoutMode SARibbonPannel::pannelLayoutMode() const +{ + return (d_ptr->m_pannelLayoutMode); +} + +/** + * @brief 添加操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 + * @param action + * @note 要去除OptionAction直接传入nullptr即可 + * @note SARibbonPannel不对QAction的destroy进行关联,如果外部对action进行delete,需要先传入nullptr给addOptionAction + */ +void SARibbonPannel::setOptionAction(QAction* action) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->setOptionAction(action); + } +} + +/** + * @brief 判断是否存在OptionAction + * @return 存在返回true + */ +bool SARibbonPannel::isHaveOptionAction() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->isHaveOptionAction(); + } + return false; +} + +QSize SARibbonPannel::sizeHint() const +{ + int shWidth = 500; + int shHeight = 100; + if (QLayout* lay = layout()) { + QSize laySize = layout()->sizeHint(); + shWidth = laySize.width(); + shHeight = laySize.height(); + } + return QSize(shWidth, shHeight); +} + +QSize SARibbonPannel::minimumSizeHint() const +{ + return (layout()->minimumSize()); +} + +/** + * @brief 把pannel设置为扩展模式,此时会撑大水平区域 + * @param isExpanding + */ +void SARibbonPannel::setExpanding(bool isExpanding) +{ + setSizePolicy(isExpanding ? QSizePolicy::Expanding : QSizePolicy::Preferred, QSizePolicy::Fixed); +} + +/** + * @brief 判断此pannel是否为(水平)扩展模式 + * @return 是扩展模式返回true + */ +bool SARibbonPannel::isExpanding() const +{ + QSizePolicy sp = sizePolicy(); + + return (sp.horizontalPolicy() == QSizePolicy::Expanding); +} + +/** + @brief 设置标题栏的高度 + @param h + */ +void SARibbonPannel::setTitleHeight(int h) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->setPannelTitleHeight(h); + } +} + +/** + * @brief 标题栏高度 + * @return + */ +int SARibbonPannel::titleHeight() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->pannelTitleHeight(); + } + return -1; +} + +/** + * @brief 判断是否显示标题,只有标题的高度被设置,才会显示标题 + * @return + */ +bool SARibbonPannel::isEnableShowTitle() const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->isEnableShowPannelTitle(); + } + return false; +} + +/** + * @brief 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 + * @param on + */ +void SARibbonPannel::setEnableShowTitle(bool on) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return lay->setEnableShowPannelTitle(on); + } +} + +/** + * @brief action对应的布局index,此操作一般用于移动,其他意义不大 + * @param act + * @return 没有查到返回-1 + */ +int SARibbonPannel::actionIndex(QAction* act) const +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return (lay->indexByAction(act)); + } + return (-1); +} + +/** + * @brief 移动action + * @param from 要移动action的位置,通过@ref actionIndex 获取 + * @param to 要移动的位置 + */ +void SARibbonPannel::moveAction(int from, int to) +{ + if (SARibbonPannelLayout* lay = pannelLayout()) { + return (lay->move(from, to)); + } + updateGeometry(); // 通知layout进行重新布局 +} + +/** + * @brief 判断是否可以自定义 + * @return + */ +bool SARibbonPannel::isCanCustomize() const +{ + return (d_ptr->m_isCanCustomize); +} + +/** + * @brief 设置是否可以自定义 + * @param b + */ +void SARibbonPannel::setCanCustomize(bool b) +{ + d_ptr->m_isCanCustomize = b; +} + +/** + * @brief pannel名称 + * @return + */ +QString SARibbonPannel::pannelName() const +{ + return (d_ptr->pannelName()); +} + +/** + * @brief pannel的名称 + * @param title 标题 + * @note 此函数会触发@sa pannelNameChanged 信号 + */ +void SARibbonPannel::setPannelName(const QString& title) +{ + QString oldName = pannelName(); + if (oldName != title) { + d_ptr->setPannelName(title); + emit pannelNameChanged(title); + } +} + +/** + * @brief 获取大图标的高度 + * @return + */ +int SARibbonPannel::largeButtonHeight() const +{ + const QMargins& mag = contentsMargins(); + return height() - mag.top() - mag.bottom() - titleHeight(); +} + +/** + * @brief 获取pannel layout + * @return + */ +SARibbonPannelLayout* SARibbonPannel::pannelLayout() const +{ + return d_ptr->pannelLayout(); +} + +/** + * @brief 更新布局 + */ +void SARibbonPannel::updateItemGeometry() +{ +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonPannel updateItemGeometry,pannelName=" << pannelName(); +#endif + // 此函数需要添加,否则SARibbonBar::setEnableWordWrap无法刷新按钮 + resetToolButtonSize(); + if (SARibbonPannelLayout* lay = pannelLayout()) { + lay->updateGeomArray(); + } +} + +/** + @brief 获取category指针,如果没有parent,或者不在category管理,返回nullptr + @return + */ +SARibbonCategory* SARibbonPannel::category() const +{ + return qobject_cast< SARibbonCategory* >(parent()); +} + +/** + @brief 获取ribbonBar指针,如果没有返回nullptr + @return + */ +SARibbonBar* SARibbonPannel::ribbonBar() const +{ + if (SARibbonCategory* c = category()) { + return c->ribbonBar(); + } + return nullptr; +} + +/** + * @brief pannel高度推荐 + * @param fm + * @param layMode + * @param pannelTitleHeight + * @return + */ +int SARibbonPannel::pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight) +{ + int textH = fm.lineSpacing(); // 这里用linespace,因为在换行的情况下,行距是不可忽略的,ribbon的大按钮默认是2行 + switch (layMode) { + case SARibbonPannel::ThreeRowMode: { + // 5.5=(3*1.6+1) (三行),1是给panneltitle预留的 + return textH * 4.8 + pannelTitleHeight; + } break; + case SARibbonPannel::TwoRowMode: { + // 3=2*1.6 + return textH * 3.2 + pannelTitleHeight; + } break; + default: { + qWarning() << "unknow SARibbonPannel::PannelLayoutMode:" << layMode; + } + } + return (textH * 4.8 + pannelTitleHeight); +} + +/** + * @brief 重置按钮的尺寸,在布局改变后(尤其高度变更),按钮的尺寸需要手动变更 + */ +void SARibbonPannel::resetToolButtonSize() +{ + QList< SARibbonToolButton* > btns = ribbonToolButtons(); + + for (SARibbonToolButton* b : qAsConst(btns)) { + if ((nullptr == b)) { + continue; + } + b->updateRect(); + } +} + +bool SARibbonPannel::event(QEvent* e) +{ +#if SA_DEBUG_PRINT_EVENT + if (e->type() != QEvent::Paint) { + qDebug() << "SARibbonPannel event(" << e->type() << "),name=" << pannelName(); + } +#endif + // if (SARibbonPannelLayout* lay = pannelLayout()) { + // if (lay->isDirty() && e->type() == QEvent::LayoutRequest) { + // if (QWidget* parw = parentWidget()) { + // if (QLayout* pl = parw->layout()) { + // pl->invalidate(); + // } + // } + // lay->m_dirty = false; + // } + // } + return QWidget::event(e); +} + +/** + * @brief 处理action的事件 + * + * 这里处理了ActionAdded,ActionChanged,ActionRemoved三个事件 + * + * ActionAdded时向布局请求,添加action,布局中同时触发了@ref SARibbonPannelLayout::createItem 函数 + * 此函数用于生成窗口,例如QRibbonToolButton + * + * ActionChanged时会让布局重新计算尺寸,并向category请求重新布局,有可能category的所有要重新调整尺寸 + * + * ActionRemoved会移除布局管理的QLayoutItem,并进行内存清理,这时窗口也会隐藏,同时销毁 + * + * @param e + * @note 所有action事件都会向category请求重新布局 + * + */ +void SARibbonPannel::actionEvent(QActionEvent* e) +{ + QAction* action = e->action(); + QWidgetAction* widgetAction = qobject_cast< QWidgetAction* >(action); + + switch (e->type()) { + case QEvent::ActionAdded: { + SARibbonPannelLayout* lay = pannelLayout(); + if (nullptr != widgetAction) { + if (widgetAction->parent() != this) { + widgetAction->setParent(this); + } + } + int index = layout()->count(); + if (e->before()) { + // 说明是插入 + index = lay->indexByAction(e->before()); + if (-1 == index) { + index = layout()->count(); // 找不到的时候就插入到最后 + } + } + lay->insertAction(index, action, getActionRowProportionProperty(action)); + // 由于pannel的尺寸发生变化,需要让category也调整 + // if (QWidget* parw = parentWidget()) { + // if (QLayout* pl = parw->layout()) { + // pl->invalidate(); + // } + // } + } break; + + case QEvent::ActionChanged: { + // 让布局重新绘制 + layout()->invalidate(); + + // updateGeometry(); + // 由于pannel的尺寸发生变化,需要让category也调整 + if (QWidget* parw = parentWidget()) { + if (QLayout* pl = parw->layout()) { + pl->invalidate(); + } + //! 强制发送一个resizeevent,让Category能重绘,如果没有这个函数,发现Category的layout虽然设置了invalidate(标记缓存失效) + //! 但并没有按顺序在pannel尺寸更新后更新Category的尺寸,导致有些pannel的尺寸识别出现异常 + //! 重打印信息上看,pannel的尺寸有进行更新,category的尺寸也进行了更新,但更新的次数和调用invalidate的次数不一样,需要手动触发ResizeEvent + //! 尝试过调用QEvent::LayoutRequest没有效果: + //! @code + //! QEvent* el = new QEvent(QEvent::LayoutRequest); + //! QApplication::postEvent(parw, el); + //! @endcode + //! + //! 调用parw->updateGeometry();也没有效果,目前看使用resizeevent是最有效果的 + //! + QResizeEvent* ersize = new QResizeEvent(parw->size(), QSize()); + QApplication::postEvent(parw, ersize); + } + } break; + + case QEvent::ActionRemoved: { + SARibbonPannelLayout* lay = pannelLayout(); + action->disconnect(this); + int index = lay->indexByAction(action); + if (index != -1) { + QLayoutItem* item = lay->takeAt(index); + delete item; + } + // 由于pannel的尺寸发生变化,需要让category也调整 + // if (QWidget* parw = parentWidget()) { + // if (QLayout* pl = parw->layout()) { + // pl->invalidate(); + // } + // } + } break; + + default: + break; + } +} + +/** + * @brief 追踪字体改变事件,把新字体设置到childwidget中 + * @param e + */ +void SARibbonPannel::changeEvent(QEvent* e) +{ + if (nullptr == e) { + return; + } + if (e->type() == QEvent::FontChange) { + QFont f = font(); + QList< QWidget* > listWidgets = findChildren< QWidget* >(); + for (QWidget* w : listWidgets) { + w->setFont(f); + } + if (QLayout* lay = layout()) { + lay->invalidate(); + } + d_ptr->resetTitleLabelFont(); + } + QWidget::changeEvent(e); +} + +/** + * @brief 获取布局item + * @return + */ +const QList< SARibbonPannelItem* >& SARibbonPannel::ribbonPannelItem() const +{ + return (pannelLayout()->m_items); +} + +/*** End of inlined file: SARibbonPannel.cpp ***/ + +/*** Start of inlined file: SARibbonCategory.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// +/// \brief ribbon页的代理类 +/// 如果需要修改重绘SARibbonCategory,可以通过设置SARibbonCategory::setProxy +/// +class SARibbonCategory::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCategory) +public: + PrivateData(SARibbonCategory* p); + + SARibbonPannel* addPannel(const QString& title); + SARibbonPannel* insertPannel(const QString& title, int index); + void addPannel(SARibbonPannel* pannel); + void insertPannel(int index, SARibbonPannel* pannel); + + // 把pannel从Category中移除,不会销毁,此时pannel的所有权归还操作者 + bool takePannel(SARibbonPannel* pannel); + + // 移除Pannel,Category会直接回收SARibbonPannel内存 + bool removePannel(SARibbonPannel* pannel); + SARibbonCategory* ribbonCategory(); + const SARibbonCategory* ribbonCategory() const; + + // 返回所有的Pannel + QList< SARibbonPannel* > pannelList(); + + // 更新item的布局,此函数会调用doItemLayout + void updateItemGeometry(); + + void doWheelEvent(QWheelEvent* event); + // 初始化 + void init(SARibbonCategory* c); + +public: + bool mEnableShowPannelTitle { true }; ///< 是否运行pannel的标题栏显示 + int mPannelTitleHeight { 15 }; ///< pannel的标题栏默认高度 + bool mIsContextCategory { false }; ///< 标记是否是上下文标签 + bool mIsCanCustomize { true }; ///< 标记是否可以自定义 + SARibbonPannel::PannelLayoutMode mDefaultPannelLayoutMode { SARibbonPannel::ThreeRowMode }; +}; +SARibbonCategory::PrivateData::PrivateData(SARibbonCategory* p) : q_ptr(p) +{ +} + +SARibbonPannel* SARibbonCategory::PrivateData::addPannel(const QString& title) +{ + if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) { + SARibbonPannel* p = insertPannel(title, lay->pannelCount()); + return p; + } + return nullptr; +} + +SARibbonPannel* SARibbonCategory::PrivateData::insertPannel(const QString& title, int index) +{ + SARibbonPannel* pannel = RibbonSubElementFactory->createRibbonPannel(ribbonCategory()); + + pannel->setPannelName(title); + pannel->setObjectName(title); + insertPannel(index, pannel); + return (pannel); +} + +void SARibbonCategory::PrivateData::addPannel(SARibbonPannel* pannel) +{ + if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) { + insertPannel(lay->pannelCount(), pannel); + } +} + +/** + * @brief 插入pannel到layout + * + * 所有的添加操作最终会调用此函数 + * @param index + * @param pannel + */ +void SARibbonCategory::PrivateData::insertPannel(int index, SARibbonPannel* pannel) +{ + if (nullptr == pannel) { + return; + } + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout()); + if (nullptr == lay) { + return; + } + if (pannel->parentWidget() != q_ptr) { + pannel->setParent(q_ptr); + } + // 同步一些状态 + pannel->setEnableShowTitle(mEnableShowPannelTitle); + pannel->setTitleHeight(mPannelTitleHeight); + pannel->setPannelLayoutMode(mDefaultPannelLayoutMode); + index = qMax(0, index); + index = qMin(lay->pannelCount(), index); + lay->insertPannel(index, pannel); + pannel->setVisible(true); +} + +bool SARibbonCategory::PrivateData::takePannel(SARibbonPannel* pannel) +{ + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout()); + if (nullptr == lay) { + return false; + } + return lay->takePannel(pannel); +} + +bool SARibbonCategory::PrivateData::removePannel(SARibbonPannel* pannel) +{ + if (takePannel(pannel)) { + pannel->hide(); + pannel->deleteLater(); + return (true); + } + return (false); +} + +QList< SARibbonPannel* > SARibbonCategory::PrivateData::pannelList() +{ + if (SARibbonCategoryLayout* lay = q_ptr->categoryLayout()) { + return lay->pannelList(); + } + return QList< SARibbonPannel* >(); +} + +SARibbonCategory* SARibbonCategory::PrivateData::ribbonCategory() +{ + return (q_ptr); +} + +const SARibbonCategory* SARibbonCategory::PrivateData::ribbonCategory() const +{ + return (q_ptr); +} + +void SARibbonCategory::PrivateData::updateItemGeometry() +{ +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategory::PrivateData::updateItemGeometry,categoryName=" << q_ptr->categoryName(); +#endif + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(q_ptr->layout()); + if (!lay) { + return; + } + const QList< SARibbonPannel* > pannels = lay->pannelList(); + for (auto pannel : pannels) { + pannel->updateItemGeometry(); + } + lay->updateGeometryArr(); + return; +} + +void SARibbonCategory::PrivateData::doWheelEvent(QWheelEvent* event) +{ + SARibbonCategoryLayout* lay = q_ptr->categoryLayout(); + if (nullptr == lay) { + return; + } + QSize contentSize = lay->categoryContentSize(); + // 求总宽 + int totalWidth = lay->categoryTotalWidth(); + + if (totalWidth > contentSize.width()) { + // 这个时候滚动有效 + int scrollpix = 40; + // Qt6 取消了QWheelEvent::delta函数 + // 是要下面方法可兼容qt5/6 + QPoint numPixels = event->pixelDelta(); + QPoint numDegrees = event->angleDelta() / 8; + if (!numPixels.isNull()) { + if (numDegrees.y() < 0) { + scrollpix = -scrollpix; + } + } else if (!numDegrees.isNull()) { + if (numDegrees.y() < 0) { + scrollpix = -scrollpix; + } + } + lay->scroll(scrollpix); + } else { + // 这时候无需处理事件,把滚动事件上发让父级也能接收 + event->ignore(); + // 如滚动过就还原 + if (lay->isScrolled()) { + lay->scroll(0); + } + } +} + +void SARibbonCategory::PrivateData::init(SARibbonCategory* c) +{ + c->setLayout(new SARibbonCategoryLayout(c)); + c->connect(c, &SARibbonCategory::windowTitleChanged, c, &SARibbonCategory::categoryNameChanged); +} + +//---------------------------------------------------- +// SARibbonCategory +//---------------------------------------------------- + +SARibbonCategory::SARibbonCategory(QWidget* p) : QFrame(p), d_ptr(new SARibbonCategory::PrivateData(this)) +{ + d_ptr->init(this); +} + +SARibbonCategory::SARibbonCategory(const QString& name, QWidget* p) + : QFrame(p), d_ptr(new SARibbonCategory::PrivateData(this)) +{ + setCategoryName(name); + d_ptr->init(this); +} + +SARibbonCategory::~SARibbonCategory() +{ +} + +/** + * @brief category的名字,等同windowTitle函数 + * @return + */ +QString SARibbonCategory::categoryName() const +{ + return (windowTitle()); +} + +/** + * @brief 设置category名字,等同setWindowTitle + * @param title + */ +void SARibbonCategory::setCategoryName(const QString& title) +{ + setWindowTitle(title); +} + +bool SARibbonCategory::event(QEvent* e) +{ +#if SA_DEBUG_PRINT_EVENT + if (e->type() != QEvent::Paint) { + qDebug() << "SARibbonCategory event(" << e->type() << "),name=" << categoryName(); + } +#endif + return QWidget::event(e); +} + +/** + * @brief pannel的模式 + * @return + */ +SARibbonPannel::PannelLayoutMode SARibbonCategory::pannelLayoutMode() const +{ + return (d_ptr->mDefaultPannelLayoutMode); +} + +/** + * @brief 设置pannel的模式 + * + * 在@ref SARibbonBar 调用@ref SARibbonBar::setRibbonStyle 函数时,会对所有的SARibbonCategory调用此函数 + * 把新的SARibbonPannel::PannelLayoutMode设置进去 + * @param m + */ +void SARibbonCategory::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m) +{ + d_ptr->mDefaultPannelLayoutMode = m; + iterate([ m ](SARibbonPannel* p) -> bool { + p->setPannelLayoutMode(m); + return true; + }); + updateItemGeometry(); +} + +/** + * @brief 添加pannel + * + * @note pannel的所有权由SARibbonCategory来管理,请不要在外部对其进行销毁 + * @param title pannel的标题,在office/wps的三行模式下会显示在pannel的下方 + * @return 返回生成的@ref SARibbonPannel 指针 + * @see 对SARibbonPannel的其他操作,参考 @ref SARibbonCategory::takePannel + */ +SARibbonPannel* SARibbonCategory::addPannel(const QString& title) +{ + return (d_ptr->addPannel(title)); +} + +/** + * @brief 添加pannel + * @param pannel pannel的所有权SARibbonCategory来管理 + */ +void SARibbonCategory::addPannel(SARibbonPannel* pannel) +{ + d_ptr->addPannel(pannel); +} + +/** + * @brief qt designer专用 + * @param pannel + */ +void SARibbonCategory::addPannel(QWidget* pannel) +{ + SARibbonPannel* p = qobject_cast< SARibbonPannel* >(pannel); + if (p) { + addPannel(p); + } +} + +/** + * @brief 新建一个pannel,并插入到index位置 + * @param title pannel的title + * @param index 插入的位置,如果index超出category里pannel的个数,将插入到最后 + * @return 返回生成的@ref SARibbonPannel 指针 + * @note 如果 + */ +SARibbonPannel* SARibbonCategory::insertPannel(const QString& title, int index) +{ + return (d_ptr->insertPannel(title, index)); +} + +/** + * @brief 通过名字查找pannel + * @param title + * @return 如果有重名,只会返回第一个符合条件的 + */ +SARibbonPannel* SARibbonCategory::pannelByName(const QString& title) const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelByName(title); + } + return nullptr; +} + +/** + * @brief 通过ObjectName查找pannel + * @param objname + * @return + */ +SARibbonPannel* SARibbonCategory::pannelByObjectName(const QString& objname) const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelByObjectName(objname); + } + return nullptr; +} + +/** + * @brief 通过索引找到pannel,如果超过索引范围,会返回nullptr + * @param index + * @return 如果超过索引范围,会返回nullptr + */ +SARibbonPannel* SARibbonCategory::pannelByIndex(int index) const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelByIndex(index); + } + return nullptr; +} + +/** + * @brief 查找pannel对应的索引 + * @param p + * @return 如果找不到,返回-1 + */ +int SARibbonCategory::pannelIndex(SARibbonPannel* p) const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelIndex(p); + } + return -1; +} + +/** + * @brief 移动一个Pannel从from index到to index + * @param from 要移动pannel的index + * @param to 要移动到的位置 + */ +void SARibbonCategory::movePannel(int from, int to) +{ + if (from == to) { + return; + } + if (to < 0) { + to = 0; + } + if (to >= pannelCount()) { + to = pannelCount() - 1; + } + if (SARibbonCategoryLayout* lay = categoryLayout()) { + lay->movePannel(from, to); + } +} + +/** + * @brief 把pannel脱离SARibbonCategory的管理 + * @param pannel 需要提取的pannel + * @return 成功返回true,否则返回false + */ +bool SARibbonCategory::takePannel(SARibbonPannel* pannel) +{ + return (d_ptr->takePannel(pannel)); +} + +/** + * @brief 移除Pannel,Category会直接回收SARibbonPannel内存 + * @param pannel 需要移除的pannel + * @note 移除后pannel为野指针,一般操作完建议把pannel指针设置为nullptr + * + * 此操作等同于: + * + * @code + * SARibbonPannel* pannel; + * ... + * category->takePannel(pannel); + * pannel->hide(); + * pannel->deleteLater(); + * @endcode + */ +bool SARibbonCategory::removePannel(SARibbonPannel* pannel) +{ + return (d_ptr->removePannel(pannel)); +} + +/** + * @brief 移除pannel + * @param index pannel的索引,如果超出会返回false + * @return 成功返回true + */ +bool SARibbonCategory::removePannel(int index) +{ + SARibbonPannel* p = pannelByIndex(index); + + if (nullptr == p) { + return (false); + } + return (removePannel(p)); +} + +/** + * @brief 返回Category下的所有pannel + * @return + */ +QList< SARibbonPannel* > SARibbonCategory::pannelList() const +{ + return (d_ptr->pannelList()); +} + +QSize SARibbonCategory::sizeHint() const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->sizeHint(); + } + return QSize(1000, 100); +} + +/** + * @brief 如果是ContextCategory,此函数返回true + * @return + */ +bool SARibbonCategory::isContextCategory() const +{ + return (d_ptr->mIsContextCategory); +} + +/** + * @brief 返回pannel的个数 + * @return + */ +int SARibbonCategory::pannelCount() const +{ + if (SARibbonCategoryLayout* lay = categoryLayout()) { + return lay->pannelCount(); + } + return -1; +} + +/** + * @brief 判断是否可以自定义 + * @return + */ +bool SARibbonCategory::isCanCustomize() const +{ + return (d_ptr->mIsCanCustomize); +} + +/** + * @brief 设置是否可以自定义 + * @param b + */ +void SARibbonCategory::setCanCustomize(bool b) +{ + d_ptr->mIsCanCustomize = b; +} + +/** + * @brief pannel标题栏的高度 + * @return + */ +int SARibbonCategory::pannelTitleHeight() const +{ + return d_ptr->mPannelTitleHeight; +} +/** + * @brief 设置pannel的高度 + * @param h + */ +void SARibbonCategory::setPannelTitleHeight(int h) +{ + d_ptr->mPannelTitleHeight = h; + iterate([ h ](SARibbonPannel* p) -> bool { + p->setTitleHeight(h); + return true; + }); +} + +/** + * @brief 是否pannel显示标题栏 + * @return + */ +bool SARibbonCategory::isEnableShowPannelTitle() const +{ + return d_ptr->mEnableShowPannelTitle; +} + +/** + * @brief 设置显示pannel标题 + * @param on + */ +void SARibbonCategory::setEnableShowPannelTitle(bool on) +{ + d_ptr->mEnableShowPannelTitle = on; + iterate([ on ](SARibbonPannel* p) -> bool { + p->setEnableShowTitle(on); + return true; + }); +} + +/** + * @brief 获取对应的ribbonbar + * @return 如果没有加入ribbonbar的管理,此值为null + */ +SARibbonBar* SARibbonCategory::ribbonBar() const +{ + // 第一个par是stackwidget + if (QWidget* par = parentWidget()) { + // 理论此时是ribbonbar + par = par->parentWidget(); + while (par) { + if (SARibbonBar* ribbon = qobject_cast< SARibbonBar* >(par)) { + return ribbon; + } + par = par->parentWidget(); + } + } + return nullptr; +} + +/** + * @brief 刷新category的布局,适用于改变ribbon的模式之后调用 + */ +void SARibbonCategory::updateItemGeometry() +{ +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategory name=" << categoryName() << " updateItemGeometry"; +#endif + d_ptr->updateItemGeometry(); +} + +/** + * @brief 此函数会遍历Category下的所有pannel,执行函数指针 + * @param fp 函数指针返回false则停止迭代 + * @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代 + */ +bool SARibbonCategory::iterate(FpPannelIterate fp) +{ + const QList< SARibbonPannel* > ps = pannelList(); + for (SARibbonPannel* p : ps) { + if (!fp(p)) { + return false; + } + } + return true; +} + +/** + @brief 设置Category的对齐方式 + @param al + */ +void SARibbonCategory::setCategoryAlignment(SARibbonAlignment al) +{ + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(layout()); + if (lay) { + lay->setCategoryAlignment(al); + } +} + +/** + @brief Category的对齐方式 + @return + */ +SARibbonAlignment SARibbonCategory::categoryAlignment() const +{ + SARibbonCategoryLayout* lay = qobject_cast< SARibbonCategoryLayout* >(layout()); + if (lay) { + return lay->categoryAlignment(); + } + return SARibbonAlignment::AlignLeft; +} + +/** + * @brief 在超出边界情况下,滚轮可滚动pannel + * @param event + */ +void SARibbonCategory::wheelEvent(QWheelEvent* event) +{ + d_ptr->doWheelEvent(event); +} + +void SARibbonCategory::changeEvent(QEvent* event) +{ + switch (event->type()) { + case QEvent::StyleChange: { + if (layout()) { +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategory changeEvent(StyleChange),categoryName=" << categoryName(); +#endif + layout()->invalidate(); + } + } break; + case QEvent::FontChange: { +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategory changeEvent(FontChange),categoryName=" << categoryName(); +#endif + QFont f = font(); + iterate([ f ](SARibbonPannel* p) -> bool { + p->setFont(f); + return true; + }); + if (layout()) { + layout()->invalidate(); + } + } break; + default: + break; + }; + return QWidget::changeEvent(event); +} + +/** + * @brief 标记这个是上下文标签 + * @param isContextCategory + */ +void SARibbonCategory::markIsContextCategory(bool isContextCategory) +{ + d_ptr->mIsContextCategory = isContextCategory; +} + +/** + * @brief 获取SARibbonCategoryLayoutlayout + * @return + */ +SARibbonCategoryLayout* SARibbonCategory::categoryLayout() const +{ + return qobject_cast< SARibbonCategoryLayout* >(layout()); +} + +//=================================================== +// SARibbonCategoryScrollButton +//=================================================== +SARibbonCategoryScrollButton::SARibbonCategoryScrollButton(Qt::ArrowType arr, QWidget* p) : QToolButton(p) +{ + setArrowType(arr); +} + +/*** End of inlined file: SARibbonCategory.cpp ***/ + +/*** Start of inlined file: SARibbonCategoryLayout.cpp ***/ +#include + +#include +#include + +#ifndef SARibbonCategoryLayout_DEBUG_PRINT +#define SARibbonCategoryLayout_DEBUG_PRINT 1 +#endif +/** + * @brief The SARibbonCategoryLayoutPrivate class + */ +class SARibbonCategoryLayout::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCategoryLayout) +public: + PrivateData(SARibbonCategoryLayout* p); + // 计算所有元素的sizehint总宽度 + int totalSizeHintWidth() const; + +public: + bool mDirty { true }; + bool mIsRightScrollBtnShow { false }; ///< 标记右滚动按钮是否需要显示 + bool mIsLeftScrollBtnShow { false }; ///< 标记左滚动按钮是否需要显示 + SARibbonCategoryScrollButton* mLeftScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 + SARibbonCategoryScrollButton* mRightScrollBtn { nullptr }; ///< 在区域无法显示时显示的按钮 + int mTotalWidth { 0 }; + int mXBase { 0 }; + QSize mSizeHint; + QSize mMinSizeHint; + QList< SARibbonCategoryLayoutItem* > mItemList; + SARibbonAlignment mCategoryAlignment { SARibbonAlignment::AlignLeft }; ///< 对齐方式 +}; + +//============================================================= +// SARibbonCategoryLayoutPrivate +//============================================================= + +SARibbonCategoryLayout::PrivateData::PrivateData(SARibbonCategoryLayout* p) : q_ptr(p) +{ +} + +/** + * @brief 计算所有元素的SizeHint宽度总和 + * @return + */ +int SARibbonCategoryLayout::PrivateData::totalSizeHintWidth() const +{ + int total = 0; + QMargins mag = q_ptr->contentsMargins(); +#if SA_DEBUG_PRINT_SIZE_HINT + int debug_i__ = 0; + QString debug_totalSizeHintWidth__; +#endif + if (!mag.isNull()) { + total += (mag.left() + mag.right()); + } + // 先计算总长 + for (SARibbonCategoryLayoutItem* item : qAsConst(mItemList)) { + if (item->isEmpty()) { +// 如果是hide就直接跳过 +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + ++debug_i__; + debug_totalSizeHintWidth__ += QString(" [%1](%2)is empty skip\n") + .arg(debug_i__) + .arg(item->toPannelWidget()->pannelName()); +#endif + continue; + } + // 这里要使用widget()->sizeHint(),因为pannel的标题会影总体布局,此处需要修改 + // TODO + QSize pannelSize = item->widget()->sizeHint(); + QSize SeparatorSize(0, 0); + if (item->separatorWidget) { + SeparatorSize = item->separatorWidget->sizeHint(); + } + total += pannelSize.width(); + total += SeparatorSize.width(); +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + ++debug_i__; + debug_totalSizeHintWidth__ += QString("|-[%1]pannelSize=(%2,%3),SeparatorSize=(%4,%5),name=(%6) \n") + .arg(debug_i__) + .arg(pannelSize.width()) + .arg(pannelSize.height()) + .arg(SeparatorSize.width()) + .arg(SeparatorSize.height()) + .arg(item->toPannelWidget()->pannelName()); +#endif + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategoryLayout.totalSizeHintWidth=" << total; + qDebug().noquote() << debug_totalSizeHintWidth__; +#endif + return (total); +} + +//============================================================= +// SARibbonCategoryLayout +//============================================================= + +SARibbonCategoryLayout::SARibbonCategoryLayout(SARibbonCategory* parent) + : QLayout(parent), d_ptr(new SARibbonCategoryLayout::PrivateData(this)) +{ + setContentsMargins(1, 1, 1, 1); + d_ptr->mLeftScrollBtn = new SARibbonCategoryScrollButton(Qt::LeftArrow, parent); + d_ptr->mRightScrollBtn = new SARibbonCategoryScrollButton(Qt::RightArrow, parent); + d_ptr->mLeftScrollBtn->setVisible(false); + d_ptr->mRightScrollBtn->setVisible(false); + connect(d_ptr->mLeftScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onLeftScrollButtonClicked); + connect(d_ptr->mRightScrollBtn, &QToolButton::clicked, this, &SARibbonCategoryLayout::onRightScrollButtonClicked); +} + +SARibbonCategoryLayout::~SARibbonCategoryLayout() +{ + while (auto item = takePannelItem(0)) { + delete item; + } +} + +SARibbonCategory* SARibbonCategoryLayout::ribbonCategory() const +{ + return (qobject_cast< SARibbonCategory* >(parentWidget())); +} + +void SARibbonCategoryLayout::addItem(QLayoutItem* item) +{ + Q_UNUSED(item); + qWarning() << tr("in SARibbonCategoryLayout cannot addItem,use addPannel instead"); + invalidate(); +} + +/** + * @brief 返回pannel的layout + * @param index 索引 + * @return + * @note 注意,pannel是和分割线一起的,但这个只返回一个pannel对应的layout + */ +QLayoutItem* SARibbonCategoryLayout::itemAt(int index) const +{ + SARibbonCategoryLayoutItem* item = d_ptr->mItemList.value(index, nullptr); + + return (item); +} + +/** + * @brief 提取layout + * @param index + * @return + */ +QLayoutItem* SARibbonCategoryLayout::takeAt(int index) +{ + QLayoutItem* r = takePannelItem(index); + invalidate(); + return r; +} + +SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(int index) +{ + if ((index >= 0) && (index < d_ptr->mItemList.size())) { + SARibbonCategoryLayoutItem* item = d_ptr->mItemList.takeAt(index); + if (item->widget()) { + item->widget()->hide(); + } + if (item->separatorWidget) { + item->separatorWidget->hide(); + } + return (item); + } + return (nullptr); +} + +SARibbonCategoryLayoutItem* SARibbonCategoryLayout::takePannelItem(SARibbonPannel* pannel) +{ + for (int i = 0; i < d_ptr->mItemList.size(); ++i) { + SARibbonCategoryLayoutItem* item = d_ptr->mItemList[ i ]; + if (item->widget() == pannel) { + return (takePannelItem(i)); + } + } + return (nullptr); +} + +/** + * @brief 移除pannel,对应的分割线也会删除 + * @param pannel + * @return + */ +bool SARibbonCategoryLayout::takePannel(SARibbonPannel* pannel) +{ + SARibbonCategoryLayoutItem* i = takePannelItem(pannel); + if (i) { + SARibbonSeparatorWidget* sp = i->separatorWidget; + if (sp) { + sp->deleteLater(); + } + delete i; + invalidate(); + return true; + } + return false; +} + +int SARibbonCategoryLayout::count() const +{ + return (d_ptr->mItemList.size()); +} + +void SARibbonCategoryLayout::addPannel(SARibbonPannel* pannel) +{ + insertPannel(d_ptr->mItemList.count(), pannel); +} + +/** + * @brief 插入一个pannel + * @param index 索引 + * @param pannel + * @return 返回对应的分割线SARibbonSeparatorWidget + * @note 在SARibbonCategoryLayout的布局中,一个pannel会携带一个分割线 + */ +void SARibbonCategoryLayout::insertPannel(int index, SARibbonPannel* pannel) +{ + index = qMax(0, index); + index = qMin(d_ptr->mItemList.count(), index); + SARibbonCategoryLayoutItem* item = new SARibbonCategoryLayoutItem(pannel); + + // 分割线 + item->separatorWidget = RibbonSubElementFactory->createRibbonSeparatorWidget(parentWidget()); + // 插入list中 + d_ptr->mItemList.insert(index, item); + // 标记需要重新计算尺寸 + invalidate(); +} + +QSize SARibbonCategoryLayout::sizeHint() const +{ + if (d_ptr->mSizeHint.isNull()) { + SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); + that->updateGeometryArr(); + } + return (d_ptr->mSizeHint); +} + +QSize SARibbonCategoryLayout::minimumSize() const +{ + if (d_ptr->mMinSizeHint.isNull()) { + SARibbonCategoryLayout* that = const_cast< SARibbonCategoryLayout* >(this); + that->updateGeometryArr(); + } + return (d_ptr->mMinSizeHint); +} + +/** + * @brief SARibbonCategory充满整个stacked widget + * @return + */ +Qt::Orientations SARibbonCategoryLayout::expandingDirections() const +{ + return (Qt::Horizontal | Qt::Vertical); +} + +void SARibbonCategoryLayout::invalidate() +{ + d_ptr->mDirty = true; + QLayout::invalidate(); +} +/** + * @brief category的内容尺寸(把margins减去) + * @return + */ +QSize SARibbonCategoryLayout::categoryContentSize() const +{ + SARibbonCategory* category = ribbonCategory(); + QSize s = category->size(); + QMargins mag = contentsMargins(); + if (!mag.isNull()) { + s.rheight() -= (mag.top() + mag.bottom()); + s.rwidth() -= (mag.right() + mag.left()); + } + return (s); +} + +/** + * @brief 更新尺寸 + */ +void SARibbonCategoryLayout::updateGeometryArr() +{ + SARibbonCategory* category = ribbonCategory(); + if (nullptr == category) { + return; + } + int categoryWidth = category->width(); + QMargins mag = contentsMargins(); + int height = category->height(); + int y = 0; + + if (!mag.isNull()) { + y = mag.top(); + height -= (mag.top() + mag.bottom()); + categoryWidth -= (mag.right() + mag.left()); + } + // total 是总宽,不是x坐标系,x才是坐标系 + int total = d_ptr->totalSizeHintWidth(); + + // 扩展的宽度 + int expandWidth = 0; + +// 如果total < categoryWidth,m_d->mXBase可以设置为0 +// 判断是否超过总长度 +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategoryLayout::updateGeometryArr" + << "\n|-category name=" << category->categoryName() // + << "\n|-category height=" << height // + << "\n|-totalSizeHintWidth=" << total // + << "\n|-y=" << y // + << "\n|-expandWidth:" << expandWidth // + << "\n|-mag=" << mag; +#endif + if (total > categoryWidth) { + // 超过总长度,需要显示滚动按钮 + if (0 == d_ptr->mXBase) { + // 已经移动到最左,需要可以向右移动 + d_ptr->mIsRightScrollBtnShow = true; + d_ptr->mIsLeftScrollBtnShow = false; + } else if (d_ptr->mXBase <= (categoryWidth - total)) { + // 已经移动到最右,需要可以向左移动 + d_ptr->mIsRightScrollBtnShow = false; + d_ptr->mIsLeftScrollBtnShow = true; + } else { + // 移动到中间两边都可以动 + d_ptr->mIsRightScrollBtnShow = true; + d_ptr->mIsLeftScrollBtnShow = true; + } + } else { + // 说明total 小于 categoryWidth + // 记录可以扩展的数量 + int canExpandingCount = 0; + d_ptr->mIsRightScrollBtnShow = false; + d_ptr->mIsLeftScrollBtnShow = false; + // 这个是避免一开始totalWidth > categorySize.width(),通过滚动按钮调整了m_d->mBaseX + // 随之调整了窗体尺寸,调整后totalWidth < categorySize.width()导致category在原来位置 + // 无法显示,必须这里把mBaseX设置为0 + + d_ptr->mXBase = 0; + // + + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (SARibbonPannel* p = qobject_cast< SARibbonPannel* >(item->widget())) { + if (p->isExpanding()) { + // pannel可扩展 + ++canExpandingCount; + } + } + } + // 计算可扩展的宽度 + if (canExpandingCount > 0) { + expandWidth = (categoryWidth - total) / canExpandingCount; + } else { + expandWidth = 0; + } + } + int x = d_ptr->mXBase; + if ((categoryAlignment() == SARibbonAlignment::AlignCenter) && (total < categoryWidth) && (0 == expandWidth)) { + // 如果是居中对齐,同时没有伸缩的pannel,同时总宽度没有超过category的宽度 + x = (categoryWidth - total) / 2; + } + total = 0; // total重新计算 + // 先按照sizeHint设置所有的尺寸 + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (item->isEmpty()) { + // 如果是hide就直接跳过 + if (item->separatorWidget) { + // pannel hide分割线也要hide + item->separatorWidget->hide(); + } + item->mWillSetGeometry = QRect(0, 0, 0, 0); + item->mWillSetSeparatorGeometry = QRect(0, 0, 0, 0); + continue; + } + SARibbonPannel* p = item->toPannelWidget(); + if (nullptr == p) { + qDebug() << "unknow widget in SARibbonCategoryLayout"; + continue; + } + // p->layout()->update(); + QSize pannelSize = p->sizeHint(); + QSize SeparatorSize(0, 0); + if (item->separatorWidget) { + SeparatorSize = item->separatorWidget->sizeHint(); + } + if (p->isExpanding()) { + // 可扩展,就把pannel扩展到最大 + pannelSize.setWidth(pannelSize.width() + expandWidth); + } + int w = pannelSize.width(); + + item->mWillSetGeometry = QRect(x, y, w, height); + x += w; + total += w; + w = SeparatorSize.width(); + item->mWillSetSeparatorGeometry = QRect(x, y, w, height); + x += w; + total += w; + } + d_ptr->mTotalWidth = total; + d_ptr->mSizeHint = QSize(d_ptr->mTotalWidth, height); + d_ptr->mMinSizeHint = QSize(categoryWidth, height); +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "SARibbonCategoryLayout updateGeometryArr,SizeHint=" << d_ptr->mSizeHint + << ",Category name=" << category->categoryName(); +#endif +} + +/** + * @brief 执行layout调整 + */ +void SARibbonCategoryLayout::doLayout() +{ + if (d_ptr->mDirty) { + updateGeometryArr(); + } + SARibbonCategory* category = ribbonCategory(); + // 两个滚动按钮的位置永远不变 + d_ptr->mLeftScrollBtn->setGeometry(0, 0, 12, category->height()); + d_ptr->mRightScrollBtn->setGeometry(category->width() - 12, 0, 12, category->height()); + QList< QWidget* > showWidgets, hideWidgets; +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + int debug_i__(0); + qDebug() << "SARibbonCategoryLayout::doLayout(),name=" << category->categoryName(); +#endif + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (item->isEmpty()) { + hideWidgets << item->widget(); + if (item->separatorWidget) { + hideWidgets << item->separatorWidget; + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() << ",will hide"; + ++debug_i__; +#endif + } else { + // item->widget()->setFixedSize(item->mWillSetGeometry.size()); + // item->widget()->move(item->mWillSetGeometry.topLeft()); + item->setGeometry(item->mWillSetGeometry); + showWidgets << item->widget(); + if (item->separatorWidget) { + item->separatorWidget->setGeometry(item->mWillSetSeparatorGeometry); + showWidgets << item->separatorWidget; + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "|-[" << debug_i__ << "]pannelName(" << item->toPannelWidget()->pannelName() + << "),willSetGeometry:" << item->mWillSetGeometry + << ",WillSetSeparatorGeometry:" << item->mWillSetSeparatorGeometry; + ++debug_i__; +#endif + } + } + + d_ptr->mRightScrollBtn->setVisible(d_ptr->mIsRightScrollBtnShow); + d_ptr->mLeftScrollBtn->setVisible(d_ptr->mIsLeftScrollBtnShow); + if (d_ptr->mIsRightScrollBtnShow) { + d_ptr->mRightScrollBtn->raise(); + } + if (d_ptr->mIsLeftScrollBtnShow) { + d_ptr->mLeftScrollBtn->raise(); + } + // 不在上面那里进行show和hide因为这会触发SARibbonPannelLayout的重绘,导致循环绘制,非常影响效率 + for (QWidget* w : qAsConst(showWidgets)) { + if (!w->isVisible()) { + w->show(); + } + } + for (QWidget* w : qAsConst(hideWidgets)) { + if (w->isVisible()) { + w->hide(); + } + } +} + +/** + * @brief 返回所有pannels + * @return + */ +QList< SARibbonPannel* > SARibbonCategoryLayout::pannels() const +{ + QList< SARibbonPannel* > res; + + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + SARibbonPannel* p = item->toPannelWidget(); + res.append(p); + } + return (res); +} + +/** + * @brief 通过ObjectName查找pannel + * @param objname + * @return + */ +SARibbonPannel* SARibbonCategoryLayout::pannelByObjectName(const QString& objname) const +{ + for (SARibbonCategoryLayoutItem* item : d_ptr->mItemList) { + if (SARibbonPannel* pannel = item->toPannelWidget()) { + if (pannel->objectName() == objname) { + return pannel; + } + } + } + return nullptr; +} + +/** + * @brief 通过名字查找pannel + * @param title + * @return 如果有重名,只会返回第一个符合条件的 + */ +SARibbonPannel* SARibbonCategoryLayout::pannelByName(const QString& pannelname) const +{ + for (SARibbonCategoryLayoutItem* item : qAsConst(d_ptr->mItemList)) { + if (SARibbonPannel* pannel = item->toPannelWidget()) { + if (pannel->pannelName() == pannelname) { + return (pannel); + } + } + } + return (nullptr); +} + +/** + * @brief 通过索引找到pannel,如果超过索引范围,会返回nullptr + * @param i + * @return + */ +SARibbonPannel* SARibbonCategoryLayout::pannelByIndex(int i) const +{ + if (i >= 0 && i < d_ptr->mItemList.size()) { + return d_ptr->mItemList[ i ]->toPannelWidget(); + } + return nullptr; +} + +/** + * @brief 移动pannel + * @param from + * @param to + */ +void SARibbonCategoryLayout::movePannel(int from, int to) +{ + d_ptr->mItemList.move(from, to); + doLayout(); +} + +/** + * @brief 返回pannel的个数 + * @return + */ +int SARibbonCategoryLayout::pannelCount() const +{ + return d_ptr->mItemList.size(); +} + +/** + * @brief 查找pannel对应的索引 + * @param p + * @return 如果找不到,返回-1 + */ +int SARibbonCategoryLayout::pannelIndex(SARibbonPannel* p) const +{ + int c = pannelCount(); + + for (int i = 0; i < c; ++i) { + if (d_ptr->mItemList[ i ]->toPannelWidget() == p) { + return (i); + } + } + return (-1); +} + +/** + * @brief 获取所有的pannel + * @return + */ +QList< SARibbonPannel* > SARibbonCategoryLayout::pannelList() const +{ + QList< SARibbonPannel* > res; + + for (SARibbonCategoryLayoutItem* i : qAsConst(d_ptr->mItemList)) { + if (SARibbonPannel* p = i->toPannelWidget()) { + res.append(p); + } + } + return (res); +} + +/** + * @brief 执行滚动 + * @param px + */ +void SARibbonCategoryLayout::scroll(int px) +{ + QSize contentSize = categoryContentSize(); + d_ptr->mXBase += px; + if (d_ptr->mXBase > 0) { + d_ptr->mXBase = 0; + } else if ((d_ptr->mXBase + d_ptr->mTotalWidth) < contentSize.width()) { + d_ptr->mXBase = contentSize.width() - d_ptr->mTotalWidth; + } + invalidate(); +} + +/** + * @brief 判断是否滚动过 + * @return + */ +bool SARibbonCategoryLayout::isScrolled() const +{ + return (d_ptr->mXBase != 0); +} + +/** + * @brief 这个宽度是实际内容的宽度,有可能大于size.width,也有可能小于 + * @return + */ +int SARibbonCategoryLayout::categoryTotalWidth() const +{ + return d_ptr->mTotalWidth; +} + +/** + @brief 设置Category的对齐方式 + + 居中对齐会让pannel以居中进行对齐 + @param al + */ +void SARibbonCategoryLayout::setCategoryAlignment(SARibbonAlignment al) +{ + d_ptr->mCategoryAlignment = al; +} + +/** + @brief Category的对齐方式 + @return + */ +SARibbonAlignment SARibbonCategoryLayout::categoryAlignment() const +{ + return d_ptr->mCategoryAlignment; +} + +void SARibbonCategoryLayout::onLeftScrollButtonClicked() +{ + SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); + int width = category->width(); + // 求总宽 + int totalWidth = d_ptr->mTotalWidth; + + if (totalWidth > width) { + int tmp = d_ptr->mXBase + width; + if (tmp > 0) { + tmp = 0; + } + d_ptr->mXBase = tmp; + } else { + d_ptr->mXBase = 0; + } + invalidate(); +} + +void SARibbonCategoryLayout::onRightScrollButtonClicked() +{ + SARibbonCategory* category = qobject_cast< SARibbonCategory* >(parentWidget()); + int width = category->width(); + // 求总宽 + int totalWidth = d_ptr->mTotalWidth; + + if (totalWidth > width) { + int tmp = d_ptr->mXBase - width; + if (tmp < (width - totalWidth)) { + tmp = width - totalWidth; + } + d_ptr->mXBase = tmp; + } else { + d_ptr->mXBase = 0; + } + invalidate(); +} + +void SARibbonCategoryLayout::setGeometry(const QRect& rect) +{ + QRect old = geometry(); + if (old == rect) { + return; + } +#if SARibbonCategoryLayout_DEBUG_PRINT && SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "===========SARibbonCategoryLayout.setGeometry(" << rect << "(" << ribbonCategory()->categoryName() << ")======="; +#endif + QLayout::setGeometry(rect); + d_ptr->mDirty = false; + updateGeometryArr(); + doLayout(); +} +//============================================================= +// SARibbonCategoryLayoutItem +//============================================================= + +SARibbonCategoryLayoutItem::SARibbonCategoryLayoutItem(SARibbonPannel* w) : QWidgetItem(w) +{ + separatorWidget = nullptr; +} + +SARibbonPannel* SARibbonCategoryLayoutItem::toPannelWidget() +{ + return qobject_cast< SARibbonPannel* >(widget()); +} + +/*** End of inlined file: SARibbonCategoryLayout.cpp ***/ + +/*** Start of inlined file: SARibbonContextCategory.cpp ***/ +#include +#include + +#include + +/** + * @brief The SARibbonCategoryData class + */ +class SAPrivateRibbonCategoryData +{ +public: + SARibbonCategory* categoryPage; +}; + +/** + * @brief The SARibbonContextCategoryPrivate class + */ +class SARibbonContextCategory::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonContextCategory) +public: + PrivateData(SARibbonContextCategory* p); + +public: + QList< SAPrivateRibbonCategoryData > categoryDataList; + QVariant contextID; + QColor contextColor; + QString contextTitle; +}; +SARibbonContextCategory::PrivateData::PrivateData(SARibbonContextCategory* p) : q_ptr(p) +{ +} + +//=================================================== +// SARibbonContextCategory +//=================================================== +SARibbonContextCategory::SARibbonContextCategory(QWidget* parent) + : QObject(parent), d_ptr(new SARibbonContextCategory::PrivateData(this)) +{ +} + +SARibbonContextCategory::~SARibbonContextCategory() +{ +} + +/** + * @brief 添加标签 + * @param title 标签名字 + */ +SARibbonCategory* SARibbonContextCategory::addCategoryPage(const QString& title) +{ + SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(parentWidget()); + category->setCategoryName(title); + addCategoryPage(category); + return (category); +} + +/** + * @brief 添加标签 + * @param page + */ +void SARibbonContextCategory::addCategoryPage(SARibbonCategory* category) +{ + if (isHaveCategory(category)) { + // cn:SARibbonContextCategory已经持有标签:%1,将跳过 + qWarning() << tr("SARibbonContextCategory have category %1,will skip").arg(category->categoryName()); + return; + } + category->markIsContextCategory(true); + connect(category, &SARibbonCategory::categoryNameChanged, this, &SARibbonContextCategory::onCategoryTitleChanged); + SAPrivateRibbonCategoryData catData; + catData.categoryPage = category; + d_ptr->categoryDataList.append(catData); + category->installEventFilter(this); + emit categoryPageAdded(category); +} + +int SARibbonContextCategory::categoryCount() const +{ + return (d_ptr->categoryDataList.count()); +} + +void SARibbonContextCategory::setId(const QVariant& id) +{ + d_ptr->contextID = id; +} + +QVariant SARibbonContextCategory::id() const +{ + return (d_ptr->contextID); +} + +void SARibbonContextCategory::setContextColor(const QColor color) +{ + d_ptr->contextColor = color; +} + +QColor SARibbonContextCategory::contextColor() const +{ + return (d_ptr->contextColor); +} + +QWidget* SARibbonContextCategory::parentWidget() const +{ + return (qobject_cast< QWidget* >(parent())); +} + +bool SARibbonContextCategory::eventFilter(QObject* watched, QEvent* e) +{ + if (nullptr == watched) { + return (false); + } + switch (e->type()) { + case QEvent::Close: { + SARibbonCategory* c = qobject_cast< SARibbonCategory* >(watched); + if (c) { +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + qDebug() << " -----------> close event"; +#endif + takeCategory(c); + } + } break; + + default: + break; + } + return (false); +} + +/** + * @brief 获取上下文标签的标题 + * @return + */ +QString SARibbonContextCategory::contextTitle() const +{ + return (d_ptr->contextTitle); +} + +/** + * @brief 设置上下文标签的标题,标题仅在office模式下显示 @ref SARibbonBar::RibbonStyle + * @param contextTitle + * @note 此函数会触发信号@sa contextTitleChanged + */ +void SARibbonContextCategory::setContextTitle(const QString& contextTitle) +{ + d_ptr->contextTitle = contextTitle; + emit contextTitleChanged(contextTitle); +} + +/** + * @brief 获取对应的tab页 + * @param index + * @return + */ +SARibbonCategory* SARibbonContextCategory::categoryPage(int index) +{ + return (d_ptr->categoryDataList[ index ].categoryPage); +} + +/** + * @brief 获取所有的SARibbonCategory* + * @return + */ +QList< SARibbonCategory* > SARibbonContextCategory::categoryList() const +{ + QList< SARibbonCategory* > res; + + for (const SAPrivateRibbonCategoryData& c : qAsConst(d_ptr->categoryDataList)) { + res.append(c.categoryPage); + } + return (res); +} + +/** + * @brief 移除这个category,这时SARibbonContextCategory不再管理这个category + * @param category + * @return 成功移除返回true + */ +bool SARibbonContextCategory::takeCategory(SARibbonCategory* category) +{ + for (int i = 0; i < d_ptr->categoryDataList.size(); ++i) { + if (d_ptr->categoryDataList[ i ].categoryPage == category) { + d_ptr->categoryDataList.takeAt(i); + return (true); + } + } + return (false); +} + +/** + * @brief 判断上下文是否维护了此SARibbonCategory + * @param category + * @return + */ +bool SARibbonContextCategory::isHaveCategory(SARibbonCategory* category) const +{ + for (int i = 0; i < d_ptr->categoryDataList.size(); ++i) { + if (d_ptr->categoryDataList[ i ].categoryPage == category) { + return (true); + } + } + return (false); +} + +void SARibbonContextCategory::onCategoryTitleChanged(const QString& title) +{ + SARibbonCategory* category = qobject_cast< SARibbonCategory* >(sender()); + if (category) { + emit categoryTitleChanged(category, title); + } +} + +/*** End of inlined file: SARibbonContextCategory.cpp ***/ + +/*** Start of inlined file: SARibbonGalleryItem.cpp ***/ +SARibbonGalleryItem::SARibbonGalleryItem() : m_flags(Qt::ItemIsEnabled | Qt::ItemIsSelectable), m_action(nullptr) +{ +} + +SARibbonGalleryItem::SARibbonGalleryItem(const QString& text, const QIcon& icon) + : m_flags(Qt::ItemIsEnabled | Qt::ItemIsSelectable), m_action(nullptr) +{ + setText(text); + setIcon(icon); + setTextAlignment(Qt::AlignTop | Qt::AlignHCenter); +} + +SARibbonGalleryItem::SARibbonGalleryItem(QAction* act) : m_flags(Qt::ItemIsEnabled | Qt::ItemIsSelectable) +{ + setTextAlignment(Qt::AlignTop | Qt::AlignHCenter); + setAction(act); +} + +SARibbonGalleryItem::~SARibbonGalleryItem() +{ +} + +void SARibbonGalleryItem::setData(int role, const QVariant& data) +{ + m_datas[ role ] = data; +} + +QVariant SARibbonGalleryItem::data(int role) const +{ + if (m_action) { + switch (role) { + case Qt::DisplayRole: + return (m_action->text()); + + case Qt::ToolTipRole: + return (m_action->toolTip()); + + case Qt::DecorationRole: + return (m_action->icon()); + default: + break; + } + } + return (m_datas.value(role)); +} + +void SARibbonGalleryItem::setText(const QString& text) +{ + setData(Qt::DisplayRole, text); +} + +QString SARibbonGalleryItem::text() const +{ + if (m_action) { + return (m_action->text()); + } + return (data(Qt::DisplayRole).toString()); +} + +void SARibbonGalleryItem::setToolTip(const QString& text) +{ + setData(Qt::ToolTipRole, text); +} + +QString SARibbonGalleryItem::toolTip() const +{ + if (m_action) { + return (m_action->toolTip()); + } + return (data(Qt::ToolTipRole).toString()); +} + +void SARibbonGalleryItem::setIcon(const QIcon& ico) +{ + setData(Qt::DecorationRole, ico); +} + +QIcon SARibbonGalleryItem::icon() const +{ + if (m_action) { + return (m_action->icon()); + } + return (qvariant_cast< QIcon >(data(Qt::DecorationRole))); +} + +bool SARibbonGalleryItem::isSelectable() const +{ + return (m_flags & Qt::ItemIsSelectable); +} + +void SARibbonGalleryItem::setSelectable(bool isSelectable) +{ + if (isSelectable) { + m_flags |= Qt::ItemIsSelectable; + } else { + m_flags = (m_flags & (~Qt::ItemIsSelectable)); + } +} + +bool SARibbonGalleryItem::isEnable() const +{ + if (m_action) { + return (m_action->isEnabled()); + } + return (m_flags & Qt::ItemIsEnabled); +} + +void SARibbonGalleryItem::setEnable(bool isEnable) +{ + if (m_action) { + m_action->setEnabled(isEnable); + } + + if (isEnable) { + m_flags |= Qt::ItemIsEnabled; + } else { + m_flags = (m_flags & (~Qt::ItemIsEnabled)); + } +} + +void SARibbonGalleryItem::setFlags(Qt::ItemFlags flag) +{ + m_flags = flag; + if (m_action) { + m_action->setEnabled(flag & Qt::ItemIsEnabled); + } +} + +Qt::ItemFlags SARibbonGalleryItem::flags() const +{ + return (m_flags); +} + +void SARibbonGalleryItem::setAction(QAction* act) +{ + m_action = act; + if (nullptr == m_action) { + return; + } + if (act->isEnabled()) { + m_flags |= Qt::ItemIsEnabled; + } else { + m_flags = (m_flags & (~Qt::ItemIsEnabled)); + } +} + +QAction* SARibbonGalleryItem::action() +{ + return (m_action); +} + +void SARibbonGalleryItem::setTextAlignment(Qt::Alignment a) +{ + setData(Qt::TextAlignmentRole, (int)a); +} + +Qt::Alignment SARibbonGalleryItem::textAlignment() const +{ + return qvariant_cast< Qt::Alignment >(data(Qt::TextAlignmentRole)); +} + +/*** End of inlined file: SARibbonGalleryItem.cpp ***/ + +/*** Start of inlined file: SARibbonGalleryGroup.cpp ***/ +#include +#include +#include +#include + +/** + * @brief The SARibbonGalleryGroupPrivate class + */ +class SARibbonGalleryGroup::PrivateData +{ +public: + SARibbonGalleryGroup* q_ptr; + QString mGroupTitle; + SARibbonGalleryGroup::GalleryGroupStyle mPreStyle { SARibbonGalleryGroup::IconWithText }; + SARibbonGalleryGroup::DisplayRow mDisplayRow { SARibbonGalleryGroup::DisplayOneRow }; + bool mBlockRecalc { false }; + int mGridMinimumWidth { 0 }; ///< grid最小宽度 + int mGridMaximumWidth { 0 }; ///< grid最大宽度 + QActionGroup* mActionGroup { nullptr }; ///< 所有GalleryGroup管理的actions都由这个actiongroup管理 +public: + PrivateData(SARibbonGalleryGroup* p) : q_ptr(p) + { + mActionGroup = new QActionGroup(p); + p->connect(mActionGroup, &QActionGroup::triggered, p, &SARibbonGalleryGroup::triggered); + p->connect(mActionGroup, &QActionGroup::hovered, p, &SARibbonGalleryGroup::hovered); + } +}; + +//=================================================== +// SARibbonGalleryGroupItemDelegate +//=================================================== + +SARibbonGalleryGroupItemDelegate::SARibbonGalleryGroupItemDelegate(SARibbonGalleryGroup* group, QObject* parent) + : QStyledItemDelegate(parent), m_group(group) +{ +} + +void SARibbonGalleryGroupItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + if (nullptr == m_group) { + return; + } + switch (m_group->galleryGroupStyle()) { + case SARibbonGalleryGroup::IconWithText: + paintIconWithText(painter, option, index); + break; + case SARibbonGalleryGroup::IconWithWordWrapText: + paintIconWithTextWordWrap(painter, option, index); + break; + case SARibbonGalleryGroup::IconOnly: + paintIconOnly(painter, option, index); + break; + default: + paintIconWithText(painter, option, index); + break; + } +} + +void SARibbonGalleryGroupItemDelegate::paintIconOnly(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QStyle* style = m_group->style(); + int sp = m_group->spacing(); + sp += 3; + painter->save(); + painter->setClipRect(option.rect); + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, m_group); + // draw the icon + QRect iconRect = option.rect; + + iconRect.adjust(sp, sp, -sp, -sp); + QIcon ico = qvariant_cast< QIcon >(index.data(Qt::DecorationRole)); + + ico.paint(painter, iconRect, Qt::AlignCenter, QIcon::Normal, QIcon::On); + painter->restore(); +} + +void SARibbonGalleryGroupItemDelegate::paintIconWithText(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QStyledItemDelegate::paint(painter, option, index); +} + +void SARibbonGalleryGroupItemDelegate::paintIconWithTextWordWrap(QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QStyledItemDelegate::paint(painter, option, index); +} + +QSize SARibbonGalleryGroupItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + Q_UNUSED(index); + Q_UNUSED(option); + return m_group->gridSize(); +} + +//=================================================== +// SARibbonGalleryGroupModel +//=================================================== + +SARibbonGalleryGroupModel::SARibbonGalleryGroupModel(QObject* parent) : QAbstractListModel(parent) +{ +} + +SARibbonGalleryGroupModel::~SARibbonGalleryGroupModel() +{ + clear(); +} + +int SARibbonGalleryGroupModel::rowCount(const QModelIndex& parent) const +{ + return (parent.isValid() ? 0 : m_items.size()); +} + +Qt::ItemFlags SARibbonGalleryGroupModel::flags(const QModelIndex& index) const +{ + if (!index.isValid() || (index.row() >= m_items.size())) { + return (Qt::NoItemFlags); + } + return (m_items.at(index.row())->flags()); +} + +QVariant SARibbonGalleryGroupModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid() || (index.row() >= m_items.count())) { + return (QVariant()); + } + return (m_items.at(index.row())->data(role)); +} + +QModelIndex SARibbonGalleryGroupModel::index(int row, int column, const QModelIndex& parent) const +{ + if (hasIndex(row, column, parent)) { + return (createIndex(row, column, m_items.at(row))); + } + return (QModelIndex()); +} + +bool SARibbonGalleryGroupModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (!index.isValid() || (index.row() >= m_items.count())) { + return (false); + } + + m_items.at(index.row())->setData(role, value); + return (true); +} + +void SARibbonGalleryGroupModel::clear() +{ + beginResetModel(); + for (int i = 0; i < m_items.count(); ++i) { + if (m_items.at(i)) { + delete m_items.at(i); + } + } + m_items.clear(); + endResetModel(); +} + +SARibbonGalleryItem* SARibbonGalleryGroupModel::at(int row) const +{ + return (m_items.value(row)); +} + +void SARibbonGalleryGroupModel::insert(int row, SARibbonGalleryItem* item) +{ + beginInsertRows(QModelIndex(), row, row); + m_items.insert(row, item); + endInsertRows(); +} + +SARibbonGalleryItem* SARibbonGalleryGroupModel::take(int row) +{ + if ((row < 0) || (row >= m_items.count())) { + return (0); + } + + beginRemoveRows(QModelIndex(), row, row); + SARibbonGalleryItem* item = m_items.takeAt(row); + + endRemoveRows(); + return (item); +} + +void SARibbonGalleryGroupModel::append(SARibbonGalleryItem* item) +{ + beginInsertRows(QModelIndex(), m_items.count(), m_items.count() + 1); + m_items.append(item); + endInsertRows(); +} + +//=================================================== +// SARibbonGalleryGroup +//=================================================== +SARibbonGalleryGroup::SARibbonGalleryGroup(QWidget* w) + : QListView(w), d_ptr(new SARibbonGalleryGroup::PrivateData(this)) +{ + setViewMode(QListView::IconMode); + setResizeMode(QListView::Adjust); + setSelectionRectVisible(true); + setUniformItemSizes(true); + setSpacing(1); + setItemDelegate(new SARibbonGalleryGroupItemDelegate(this, this)); + connect(this, &QAbstractItemView::clicked, this, &SARibbonGalleryGroup::onItemClicked); + SARibbonGalleryGroupModel* m = new SARibbonGalleryGroupModel(this); + setModel(m); +} + +SARibbonGalleryGroup::~SARibbonGalleryGroup() +{ +} + +/** + * @brief 是否禁止计算 + * @param on + */ +void SARibbonGalleryGroup::setRecalcGridSizeBlock(bool on) +{ + d_ptr->mBlockRecalc = on; +} + +bool SARibbonGalleryGroup::isRecalcGridSizeBlock() const +{ + return d_ptr->mBlockRecalc; +} + +/** + * @brief 重新计算grid和icon的尺寸 + */ +void SARibbonGalleryGroup::recalcGridSize() +{ + recalcGridSize(height()); +} + +void SARibbonGalleryGroup::recalcGridSize(int galleryHeight) +{ + if (isRecalcGridSizeBlock()) { + return; + } + // 首先通过DisplayRow计算GridSize + int dr = static_cast< int >(displayRow()); + if (dr < 1) { + dr = 1; + } else if (dr > 3) { + dr = 3; + } + int h = galleryHeight / dr; + if (h <= 1) { + h = galleryHeight; + } + int w = h; + if (gridMinimumWidth() > 0) { + if (w < gridMinimumWidth()) { + w = gridMinimumWidth(); + } + } + if (gridMaximumWidth() > 0) { + if (w > gridMaximumWidth()) { + w = gridMaximumWidth(); + } + } + setGridSize(QSize(w, h)); + // 在通过GalleryGroupStyle确定icon的尺寸 + const int shiftpix = 4; // 这个是移动像素,qt在鼠标移动到图标上时会移动一下,给用户明确的动态,导致如果布局很满会超出显示范围,因此要在此基础上缩放一点 + switch (galleryGroupStyle()) { + case IconWithText: { + int textHeight = fontMetrics().lineSpacing(); + int iconHeight = h - textHeight - 2 * spacing() - shiftpix; + if (iconHeight > 0) { + setIconSize(QSize(w - 2 * spacing() - shiftpix, iconHeight)); + } else { + setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); + } + break; + } + case IconWithWordWrapText: { + int textHeight = fontMetrics().lineSpacing() * 2; + int iconHeight = h - textHeight; + if (iconHeight > 0) { + setIconSize(QSize(w - 2 * spacing() - shiftpix, iconHeight - 2 * spacing() - shiftpix)); + } else { + setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); + } + break; + } + case IconOnly: { + setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); + break; + } + default: { + setIconSize(QSize(w - 2 * spacing() - shiftpix, h - 2 * spacing() - shiftpix)); + break; + } + } +#if 0 + qDebug() << "SARibbonGalleryGroup::recalcGridSize(" << galleryHeight << "): gridSize=" << gridSize() + << " iconSize=" << iconSize(); +#endif +} + +/// +/// \brief 设置默认的预设样式 +/// \param style +/// +void SARibbonGalleryGroup::setGalleryGroupStyle(SARibbonGalleryGroup::GalleryGroupStyle style) +{ + d_ptr->mPreStyle = style; + if (style == IconWithWordWrapText) { + setWordWrap(true); + } + recalcGridSize(); +} + +SARibbonGalleryGroup::GalleryGroupStyle SARibbonGalleryGroup::galleryGroupStyle() const +{ + return d_ptr->mPreStyle; +} + +void SARibbonGalleryGroup::addItem(const QString& text, const QIcon& icon) +{ + if (nullptr == groupModel()) { + return; + } + addItem(new SARibbonGalleryItem(text, icon)); +} + +/** + * @brief 添加一个条目 + * + * @param item 条目的内存所有权归属SARibbonGalleryGroup管理 + */ +void SARibbonGalleryGroup::addItem(SARibbonGalleryItem* item) +{ + if (nullptr == groupModel()) { + return; + } + groupModel()->append(item); +} + +void SARibbonGalleryGroup::addActionItem(QAction* act) +{ + if (nullptr == groupModel()) { + return; + } + d_ptr->mActionGroup->addAction(act); + groupModel()->append(new SARibbonGalleryItem(act)); +} + +void SARibbonGalleryGroup::addActionItemList(const QList< QAction* >& acts) +{ + SARibbonGalleryGroupModel* model = groupModel(); + + if (nullptr == model) { + return; + } + for (QAction* a : acts) { + d_ptr->mActionGroup->addAction(a); + } + for (int i = 0; i < acts.size(); ++i) { + model->append(new SARibbonGalleryItem(acts[ i ])); + } +} + +/// +/// \brief 构建一个model,这个model的父类是SARibbonGalleryGroup,如果要共享model,需要手动处理model的父类 +/// +void SARibbonGalleryGroup::setupGroupModel() +{ + setModel(new SARibbonGalleryGroupModel(this)); +} + +SARibbonGalleryGroupModel* SARibbonGalleryGroup::groupModel() +{ + return (qobject_cast< SARibbonGalleryGroupModel* >(model())); +} + +void SARibbonGalleryGroup::setGroupTitle(const QString& title) +{ + d_ptr->mGroupTitle = title; + emit groupTitleChanged(d_ptr->mGroupTitle); +} + +QString SARibbonGalleryGroup::groupTitle() const +{ + return (d_ptr->mGroupTitle); +} + +void SARibbonGalleryGroup::selectByIndex(int i) +{ + SARibbonGalleryGroupModel* model = groupModel(); + + if (nullptr == model) { + return; + } + QModelIndex ind = model->index(i, 0, QModelIndex()); + QItemSelectionModel* selmodel = selectionModel(); + + if (selmodel) { + selmodel->select(ind, QItemSelectionModel::SelectCurrent); + } +} + +/** + * @brief 设置显示的行数 + * @param r + */ +void SARibbonGalleryGroup::setDisplayRow(DisplayRow r) +{ + d_ptr->mDisplayRow = r; + recalcGridSize(); +} + +/** + * @brief Gallery显示的行数 + * @return + */ +SARibbonGalleryGroup::DisplayRow SARibbonGalleryGroup::displayRow() const +{ + return d_ptr->mDisplayRow; +} + +/** + * @brief 设置grid最小的宽度,默认为0(不限制) + * @param w + */ +void SARibbonGalleryGroup::setGridMinimumWidth(int w) +{ + d_ptr->mGridMinimumWidth = w; +} + +/** + * @brief grid最小的宽度,默认为0(不限制) + * @return + */ +int SARibbonGalleryGroup::gridMinimumWidth() const +{ + return d_ptr->mGridMinimumWidth; +} + +/** + * @brief 设置grid最大的宽度,默认为0(不限制) + * @param w + */ +void SARibbonGalleryGroup::setGridMaximumWidth(int w) +{ + d_ptr->mGridMaximumWidth = w; +} + +/** + * @brief grid最大的的宽度,默认为0(不限制) + * @param w + */ +int SARibbonGalleryGroup::gridMaximumWidth() const +{ + return d_ptr->mGridMaximumWidth; +} + +/** + * @brief 获取SARibbonGalleryGroup管理的actiongroup + * @return + */ +QActionGroup* SARibbonGalleryGroup::actionGroup() const +{ + return d_ptr->mActionGroup; +} + +void SARibbonGalleryGroup::onItemClicked(const QModelIndex& index) +{ + if (index.isValid()) { + SARibbonGalleryItem* item = (SARibbonGalleryItem*)index.internalPointer(); + if (item) { + QAction* act = item->action(); + if (act) { + act->activate(QAction::Trigger); + } + } + } +} + +void SARibbonGalleryGroup::onItemEntered(const QModelIndex& index) +{ + if (index.isValid()) { + SARibbonGalleryItem* item = (SARibbonGalleryItem*)index.internalPointer(); + if (item) { + QAction* act = item->action(); + if (act) { + act->activate(QAction::Hover); + } + } + } +} + +/*** End of inlined file: SARibbonGalleryGroup.cpp ***/ + +/*** Start of inlined file: SARibbonGallery.cpp ***/ +#include +#include +#define ICON_ARROW_UP QIcon(":/image/resource/ArrowUp.png") +#define ICON_ARROW_DOWN QIcon(":/image/resource/ArrowDown.png") +#define ICON_ARROW_MORE QIcon(":/image/resource/ArrowMore.png") +#include +#include +#include +#include +#include + +#include + +/** + * @brief The SARibbonGalleryPrivate class + */ +class SARibbonGallery::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonGallery) +public: + static int sGalleryButtonMaximumWidth; + SARibbonGalleryButton* buttonUp { nullptr }; + SARibbonGalleryButton* buttonDown { nullptr }; + SARibbonGalleryButton* buttonMore { nullptr }; +#if 0 + SARibbonMenu *popupWidget; +#else + SARibbonGalleryViewport* popupWidget { nullptr }; +#endif + SARibbonGalleryGroup* viewportGroup { nullptr }; + QBoxLayout* btnLayout { nullptr }; + QBoxLayout* layout { nullptr }; + PrivateData(SARibbonGallery* p) : q_ptr(p) + { + } + + void init() + { + buttonUp = new SARibbonGalleryButton(q_ptr); + buttonDown = new SARibbonGalleryButton(q_ptr); + buttonMore = new SARibbonGalleryButton(q_ptr); + buttonUp->setToolButtonStyle(Qt::ToolButtonIconOnly); + buttonDown->setToolButtonStyle(Qt::ToolButtonIconOnly); + buttonMore->setToolButtonStyle(Qt::ToolButtonIconOnly); + buttonUp->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + buttonDown->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + buttonMore->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + buttonUp->setObjectName(QStringLiteral("SARibbonGalleryButtonUp")); + buttonDown->setObjectName(QStringLiteral("SARibbonGalleryButtonDown")); + buttonMore->setObjectName(QStringLiteral("SARibbonGalleryButtonMore")); + buttonUp->setMaximumWidth(sGalleryButtonMaximumWidth); + buttonDown->setMaximumWidth(sGalleryButtonMaximumWidth); + buttonMore->setMaximumWidth(sGalleryButtonMaximumWidth); + buttonUp->setIcon(ICON_ARROW_UP); + buttonDown->setIcon(ICON_ARROW_DOWN); + buttonMore->setIcon(ICON_ARROW_MORE); + q_ptr->connect(buttonUp, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageUp); + q_ptr->connect(buttonDown, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::pageDown); + q_ptr->connect(buttonMore, &QAbstractButton::clicked, q_ptr, &SARibbonGallery::showMoreDetail); + // 信号转发 + q_ptr->connect(q_ptr, &SARibbonGallery::triggered, q_ptr, &SARibbonGallery::onTriggered); + popupWidget = nullptr; + viewportGroup = nullptr; + btnLayout = new QBoxLayout(QBoxLayout::TopToBottom); + btnLayout->setSpacing(0); + btnLayout->setContentsMargins(0, 0, 0, 0); + btnLayout->addWidget(buttonUp); + btnLayout->addWidget(buttonDown); + btnLayout->addWidget(buttonMore); + layout = new QBoxLayout(QBoxLayout::RightToLeft); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + layout->addLayout(btnLayout); + layout->addStretch(); + q_ptr->setLayout(layout); + } + + bool isValid() const + { + return (q_ptr != nullptr); + } + + void createPopupWidget() + { + if (nullptr == popupWidget) { +#if 0 + popupWidget = new SARibbonMenu(Parent); +#else + popupWidget = new SARibbonGalleryViewport(q_ptr); +#endif + } + } + + void setViewPort(SARibbonGalleryGroup* v) + { + if (nullptr == viewportGroup) { + viewportGroup = RibbonSubElementFactory->createRibbonGalleryGroup(q_ptr); + layout->addWidget(viewportGroup, 1); + } + viewportGroup->setRecalcGridSizeBlock(true); + viewportGroup->setGalleryGroupStyle(v->galleryGroupStyle()); + viewportGroup->setDisplayRow(v->displayRow()); + viewportGroup->setSpacing(v->spacing()); + viewportGroup->setGridMaximumWidth(v->gridMaximumWidth()); + viewportGroup->setGridMinimumWidth(v->gridMinimumWidth()); + viewportGroup->setRecalcGridSizeBlock(false); + viewportGroup->recalcGridSize(viewportGroup->height()); + viewportGroup->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + viewportGroup->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + viewportGroup->setModel(v->model()); + viewportGroup->show(); + } +}; + +// 静态变量初始化 + +/** + * @brief SARibbonGalleryPrivate::sGalleryButtonMaximumWidth + */ +int SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = 15; + +//=================================================== +// SARibbonGalleryButton +//=================================================== + +SARibbonGalleryButton::SARibbonGalleryButton(QWidget* parent) : QToolButton(parent) +{ +} + +//=================================================== +// SARibbonGalleryViewport +//=================================================== + +SARibbonGalleryViewport::SARibbonGalleryViewport(QWidget* parent) : QWidget(parent) +{ + setWindowFlags(Qt::Popup); + QPalette pl = palette(); + pl.setBrush(QPalette::Window, pl.brush(QPalette::Base)); + setPalette(pl); + m_layout = new QVBoxLayout(this); + m_layout->setSpacing(0); + m_layout->setContentsMargins(0, 0, 0, 0); +} + +/** + * @brief 添加窗口不带标题 + * @param w + */ +void SARibbonGalleryViewport::addWidget(QWidget* w) +{ + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_layout->addWidget(w); +} + +/** + * @brief 添加窗口,带标题 + * @param w + * @param title + */ +void SARibbonGalleryViewport::addWidget(QWidget* w, const QString& title) +{ + QLabel* label = new QLabel(this); + label->setText(title); + label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_layout->addWidget(label); + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + m_layout->addWidget(w); + _widgetToTitleLable[ w ] = label; +} + +/** + * @brief 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 + * @param g + * @return 如果没有管理group,将返回nullptr + */ +QLabel* SARibbonGalleryViewport::titleLabel(QWidget* w) +{ + return _widgetToTitleLable.value(w, nullptr); +} + +/** + * @brief 移除窗口 + * @param w + */ +void SARibbonGalleryViewport::removeWidget(QWidget* w) +{ + QLabel* label = titleLabel(w); + if (label) { + m_layout->removeWidget(label); + } + m_layout->removeWidget(w); +} + +/** + * @brief widget的标题改变 + * @param g + * @param title + */ +void SARibbonGalleryViewport::onTitleChanged(QWidget* w, const QString& title) +{ + QLabel* l = titleLabel(w); + if (l) { + l->setText(title); + } +} + +//=================================================== +// SARibbonGallery +//=================================================== + +SARibbonGallery::SARibbonGallery(QWidget* parent) : QFrame(parent), d_ptr(new SARibbonGallery::PrivateData(this)) +{ + d_ptr->init(); + setFrameShape(QFrame::Box); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setMinimumWidth(200); +} + +SARibbonGallery::~SARibbonGallery() +{ +} + +QSize SARibbonGallery::sizeHint() const +{ + return (QSize(100, 62)); +} + +/** + * @brief 获取一个空白SARibbonGalleryGroup + * @return + */ +SARibbonGalleryGroup* SARibbonGallery::addGalleryGroup() +{ + SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); + addGalleryGroup(group); + return (group); +} + +/** + * @brief 添加一组SARibbonGalleryGroup + * @param group + */ +void SARibbonGallery::addGalleryGroup(SARibbonGalleryGroup* group) +{ + group->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + SARibbonGalleryViewport* viewport = ensureGetPopupViewPort(); + viewport->addWidget(group, group->groupTitle()); + connect(group, &QAbstractItemView::clicked, this, &SARibbonGallery::onItemClicked); + connect(group, &SARibbonGalleryGroup::groupTitleChanged, this, [ group, viewport ](const QString& t) { + viewport->onTitleChanged(group, t); + }); + connect(group, &SARibbonGalleryGroup::triggered, this, &SARibbonGallery::triggered); + connect(group, &SARibbonGalleryGroup::hovered, this, &SARibbonGallery::hovered); + setCurrentViewGroup(group); +} + +/** + * @brief 添加一组actions + * @param title actions组的名字 + * @param actions + * @return 返回SARibbonGalleryGroup,用户可以通过修改SARibbonGalleryGroup属性控制其显示方法 + */ +SARibbonGalleryGroup* SARibbonGallery::addCategoryActions(const QString& title, QList< QAction* > actions) +{ + SARibbonGalleryGroup* group = RibbonSubElementFactory->createRibbonGalleryGroup(this); + if (!title.isEmpty()) { + group->setGroupTitle(title); + } + group->addActionItemList(actions); + addGalleryGroup(group); + return (group); +} + +void SARibbonGallery::setCurrentViewGroup(SARibbonGalleryGroup* group) +{ + d_ptr->setViewPort(group); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +SARibbonGalleryGroup* SARibbonGallery::currentViewGroup() const +{ + return (d_ptr->viewportGroup); +} + +/** + * @brief 获取弹出窗口 + * @return + */ +SARibbonGalleryViewport* SARibbonGallery::getPopupViewPort() const +{ + return d_ptr->popupWidget; +} + +/** + * @brief 设置最右边三个控制按钮的最大宽度(默认15) + * @param w + */ +void SARibbonGallery::setGalleryButtonMaximumWidth(int w) +{ + SARibbonGallery::PrivateData::sGalleryButtonMaximumWidth = w; +} + +/** + * @brief 上翻页 + */ +void SARibbonGallery::pageDown() +{ + if (d_ptr->viewportGroup) { + QScrollBar* vscrollBar = d_ptr->viewportGroup->verticalScrollBar(); + int v = vscrollBar->value(); + v += vscrollBar->singleStep(); + vscrollBar->setValue(v); + } +} + +/** + * @brief 下翻页 + */ +void SARibbonGallery::pageUp() +{ + if (d_ptr->viewportGroup) { + QScrollBar* vscrollBar = d_ptr->viewportGroup->verticalScrollBar(); + int v = vscrollBar->value(); + v -= vscrollBar->singleStep(); + vscrollBar->setValue(v); + } +} + +/** + * @brief 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 + */ +void SARibbonGallery::showMoreDetail() +{ + if (nullptr == d_ptr->popupWidget) { + return; + } + QSize popupMenuSize = d_ptr->popupWidget->sizeHint(); + QPoint start = mapToGlobal(QPoint(0, 0)); + + int width = d_ptr->viewportGroup->width(); // viewport + + width += qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent); // scrollbar + d_ptr->popupWidget->setGeometry(start.x(), start.y(), width, popupMenuSize.height()); + d_ptr->popupWidget->show(); +} + +void SARibbonGallery::onItemClicked(const QModelIndex& index) +{ + QObject* obj = sender(); + SARibbonGalleryGroup* group = qobject_cast< SARibbonGalleryGroup* >(obj); + if (group) { + setCurrentViewGroup(group); + SARibbonGalleryGroup* curGroup = currentViewGroup(); + curGroup->scrollTo(index); + curGroup->setCurrentIndex(index); + } +} + +void SARibbonGallery::onTriggered(QAction* action) +{ + Q_UNUSED(action); + // 点击后关闭弹出窗口 + if (d_ptr->popupWidget) { + if (d_ptr->popupWidget->isVisible()) { + d_ptr->popupWidget->hide(); + } + } +} + +SARibbonGalleryViewport* SARibbonGallery::ensureGetPopupViewPort() +{ + if (nullptr == d_ptr->popupWidget) { + d_ptr->createPopupWidget(); + } + return (d_ptr->popupWidget); +} + +void SARibbonGallery::resizeEvent(QResizeEvent* event) +{ + QFrame::resizeEvent(event); + // 对SARibbonGalleryViewport所有SARibbonGalleryGroup重置尺寸 + int h = layout()->contentsRect().height(); + if (d_ptr->viewportGroup) { + h = d_ptr->viewportGroup->height(); + d_ptr->viewportGroup->recalcGridSize(); + } + if (d_ptr->popupWidget) { + QLayout* lay = d_ptr->popupWidget->layout(); + if (!lay) { + return; + } + int c = lay->count(); + for (int i = 0; i < c; ++i) { + QLayoutItem* item = lay->itemAt(i); + if (!item) { + continue; + } + QWidget* w = item->widget(); + if (!w) { + continue; + } + SARibbonGalleryGroup* g = qobject_cast< SARibbonGalleryGroup* >(w); + if (!g) { + continue; + } + g->recalcGridSize(h); + } + } +} + +void SARibbonGallery::paintEvent(QPaintEvent* event) +{ + QFrame::paintEvent(event); +} + +/*** End of inlined file: SARibbonGallery.cpp ***/ + +/*** Start of inlined file: SARibbonBar.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HELP_DRAW_RECT(p, rect) \ + do { \ + p.save(); \ + QPen _pen(Qt::DashDotDotLine); \ + _pen.setColor(QColor(219, 26, 59)); \ + p.setPen(_pen); \ + p.setBrush(QBrush()); \ + p.drawRect(rect); \ + p.restore(); \ + } while (0) + +class _SAContextCategoryManagerData +{ +public: + SARibbonContextCategory* contextCategory; + QList< int > tabPageIndex; + bool operator==(const SARibbonContextCategory* contextPage) + { + return (this->contextCategory == contextPage); + } +}; + +/** + * @todo 此处要修改,此方式容易异常 + */ +class _SARibbonTabData +{ +public: + SARibbonCategory* category; + int index; + _SARibbonTabData() : category(nullptr), index(-1) + { + } +}; +Q_DECLARE_METATYPE(_SARibbonTabData) + +class SARibbonBar::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonBar) +public: + QPointer< QAbstractButton > mApplicationButton; + QPointer< SARibbonTabBar > mRibbonTabBar; + QPointer< SARibbonStackedWidget > mStackedContainerWidget; + QPointer< SARibbonButtonGroupWidget > mRightButtonGroup; ///< 在tab bar右边的按钮群 + QPointer< SARibbonQuickAccessBar > mQuickAccessBar; ///< 快速响应栏 + QAction* mMinimumCategoryButtonAction { nullptr }; ///< 隐藏面板按钮action + QList< _SAContextCategoryManagerData > mCurrentShowingContextCategory; + QList< SARibbonContextCategory* > mContextCategoryList; ///< 存放所有的上下文标签 + QList< _SARibbonTabData > mHidedCategory; + int mIconRightBorderPosition { 1 }; ///< 标题栏x值得最小值,在有图标和快捷启动按钮,此值都需要变化 + SARibbonBar::RibbonStyles mRibbonStyle { SARibbonBar::RibbonStyleLooseThreeRow }; ///< ribbon的风格 + SARibbonBar::RibbonMode mCurrentRibbonMode { SARibbonBar::NormalRibbonMode }; ///< 记录当前模式 + QSize mWindowButtonSize; ///< 由SARibbonMainWindow告诉的windowbutton的尺寸 + QList< QColor > mContextCategoryColorList; ///< contextCategory的色系 + int mContextCategoryColorListIndex { -1 }; ///< 记录contextCategory色系索引 + QColor mTitleTextColor; ///< 标题文字颜色,默认无效,无效的情况下和SARibbonBar的qss:color属性一致 + QColor mTabBarBaseLineColor { QColor(186, 201, 219) }; ///< tabbar 底部会绘制一条线条,定义线条颜色 + Qt::Alignment mTitleAligment { Qt::AlignCenter }; ///< 标题对齐方式 + bool mIsTitleVisible { true }; ///< 标题是否显示 + SARibbonAlignment mRibbonAlignment { SARibbonAlignment::AlignLeft }; ///< 对齐方式 + SARibbonPannel::PannelLayoutMode mDefaulePannelLayoutMode { SARibbonPannel::ThreeRowMode }; ///< 默认的PannelLayoutMode + bool mEnableShowPannelTitle { true }; ///< 是否运行pannel的标题栏显示 + bool mIsTabOnTitle { false }; ///< 是否tab在标题栏上 + int mTitleBarHeight { 30 }; ///< 标题栏高度 + int mTabBarHeight { 28 }; ///< tabbar高度 + int mPannelTitleHeight { 15 }; ///< pannel的标题栏默认高度 + int mCategoryHeight { 60 }; ///< Category的高度 + std::unique_ptr< int > mUserDefTitleBarHeight; ///< 用户定义的标题栏高度,正常不使用用户设定的高度,而是使用自动计算的高度 + std::unique_ptr< int > mUserDefTabBarHeight; ///< 用户定义的tabbar高度,正常不使用用户设定的高度,而是使用自动计算的高度 + std::unique_ptr< int > mUserDefCategoryHeight; ///< 用户定义的Category的高度,正常不使用用户设定的高度,而是使用自动计算的高度 +public: + PrivateData(SARibbonBar* par) : q_ptr(par) + { + mContextCategoryColorList = SARibbonBar::defaultContextCategoryColorList(); + } + void init(); + // 计算tabbar高度 + int calcTabBarHeight(); + // 根据字体信息计算标题栏高度 + int calcTitleBarHeight(); + // 根据字体信息计算category的高度 + int calcCategoryHeight(); + // 计算tabbar高度 + static int calcMainBarHeight(int tabHegith, int titleHeight, int categoryHeight, bool tabOnTitle, SARibbonBar::RibbonMode rMode); + // 获取当前最小模式下的高度 + int getCurrentMinimumModeMainBarHeight() const; + // 获取当前正常模式下的高度 + int getCurrentNormalModeMainBarHeight() const; + // 重置尺寸 + void resetSize(); + // 获取标题栏高度 + int titleBarHeight() const; + // tabbar 高度 + int tabBarHeigth() const; + // category高度 + int categoryHeight() const; + // 更新推荐的尺寸值 + void updateHintSize(); + + void setApplicationButton(QAbstractButton* btn); + + bool isContainContextCategoryInList(SARibbonContextCategory* contextCategory); + + void setMinimumMode(); + + void setNormalMode(); + + QColor getContextCategoryColor(); + + void updateTabData(); + + /** + * @brief 通过输入高度计算iconSize + * @param h + * @return + */ + static QSize calcIconSizeByHeight(int h); +}; + +void SARibbonBar::PrivateData::init() +{ + mApplicationButton = RibbonSubElementFactory->createRibbonApplicationButton(q_ptr); + q_ptr->connect(mApplicationButton, &QAbstractButton::clicked, q_ptr, &SARibbonBar::applicationButtonClicked); + mRibbonTabBar = RibbonSubElementFactory->createRibbonTabBar(q_ptr); + mRibbonTabBar->setObjectName(QStringLiteral("objSARibbonTabBar")); + mRibbonTabBar->setDrawBase(false); + q_ptr->connect(mRibbonTabBar, &QTabBar::currentChanged, q_ptr, &SARibbonBar::onCurrentRibbonTabChanged); + q_ptr->connect(mRibbonTabBar, &QTabBar::tabBarClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabClicked); + q_ptr->connect(mRibbonTabBar, &QTabBar::tabBarDoubleClicked, q_ptr, &SARibbonBar::onCurrentRibbonTabDoubleClicked); + q_ptr->connect(mRibbonTabBar, &QTabBar::tabMoved, q_ptr, &SARibbonBar::onTabMoved); + // + mStackedContainerWidget = RibbonSubElementFactory->createRibbonStackedWidget(q_ptr); + mStackedContainerWidget->setObjectName(QStringLiteral("objSARibbonStackedContainerWidget")); + mStackedContainerWidget->connect(mStackedContainerWidget, &SARibbonStackedWidget::hidWindow, q_ptr, &SARibbonBar::onStackWidgetHided); + // 捕获事件,在popmode时必须用到 + mStackedContainerWidget->installEventFilter(q_ptr); + // + mQuickAccessBar = RibbonSubElementFactory->createQuickAccessBar(q_ptr); + mQuickAccessBar->setObjectName(QStringLiteral("objSARibbonQuickAccessBar")); + mQuickAccessBar->setIcon(q_ptr->windowIcon()); + // + mRightButtonGroup = RibbonSubElementFactory->craeteButtonGroupWidget(q_ptr); + // + setNormalMode(); +} + +/** + * @brief 估算tabbar的高度 + * @param fm + * @return + */ +int SARibbonBar::PrivateData::calcTabBarHeight() +{ + int defaultHeight = q_ptr->style()->pixelMetric(QStyle::PM_TabBarBaseHeight); + int fontHeight = q_ptr->fontMetrics().lineSpacing(); // 不要用height,像宋体这种字体,height=12,lineSpacing=14,有些就无法显示 + int defaultHeight2 = fontHeight * 1.6; + if (defaultHeight2 < fontHeight + 10) { + defaultHeight2 = fontHeight + 10; // 主要为了满足office2021主题,tab下有个4px的横杠 + } + int r = qMax(defaultHeight, defaultHeight2); + if (r < 20) { + r = 20; + } + return r; +} + +/** + * @brief 估算标题栏的高度 + * @param fm + * @return + */ +int SARibbonBar::PrivateData::calcTitleBarHeight() +{ + int defaultHeight = q_ptr->style()->pixelMetric(QStyle::PM_TitleBarHeight); + int defaultHeight2 = q_ptr->fontMetrics().height() * 1.8; + int r = qMax(defaultHeight, defaultHeight2); + if (r < 25) { + r = 25; + } + return r; +} + +/** + * @brief 估算category的高度 + * @note 经过对照,1.6行高和office的高度比较接近 + * @param fm + * @param s + * @return + */ +int SARibbonBar::PrivateData::calcCategoryHeight() +{ + int textH = q_ptr->fontMetrics().lineSpacing(); // 这里用linespace,因为在换行的情况下,行距是不可忽略的,ribbon的大按钮默认是2行 + if (SARibbonPannel::ThreeRowMode == mDefaulePannelLayoutMode) { + // 5.5=(3*1.6+1) (三行),1是给panneltitle预留的 + return textH * 4.8 + mPannelTitleHeight; + } else { + // 3=2*1.6 + return textH * 3.2 + mPannelTitleHeight; + } + return (textH * 4.8 + mPannelTitleHeight); +} + +/** + * @brief 计算总高 + * + * @note 此总高是普通模式下的总高 + * + * @param tabHegith + * @param titleHeight + * @param categoryHeight + * @return + */ +int SARibbonBar::PrivateData::calcMainBarHeight(int tabHegith, int titleHeight, int categoryHeight, bool tabOnTitle, SARibbonBar::RibbonMode rMode) +{ + if (rMode == MinimumRibbonMode) { + // 最小模式,没有categoryHeight + if (tabOnTitle) { + return titleHeight; + } else { + return titleHeight + tabHegith; + } + } else { + if (tabOnTitle) { + return titleHeight + categoryHeight; + } else { + return tabHegith + titleHeight + categoryHeight; + } + } + return tabHegith + titleHeight + categoryHeight; +} + +int SARibbonBar::PrivateData::getCurrentMinimumModeMainBarHeight() const +{ + return calcMainBarHeight(tabBarHeigth(), titleBarHeight(), categoryHeight(), mIsTabOnTitle, SARibbonBar::MinimumRibbonMode); +} + +int SARibbonBar::PrivateData::getCurrentNormalModeMainBarHeight() const +{ + return calcMainBarHeight(tabBarHeigth(), titleBarHeight(), categoryHeight(), mIsTabOnTitle, SARibbonBar::NormalRibbonMode); +} + +/** + * @brief 重新计算尺寸 + */ +void SARibbonBar::PrivateData::resetSize() +{ + updateHintSize(); + const int th = tabBarHeigth(); + int mainBarHeight = calcMainBarHeight(th, titleBarHeight(), categoryHeight(), mIsTabOnTitle, mCurrentRibbonMode); + if (mRibbonTabBar) { + mRibbonTabBar->setFixedHeight(th); + } + // 处于最小模式下时,bar的高度为tabbar的bottom,这个调整必须在resize event之后 + q_ptr->setFixedHeight(mainBarHeight); +} + +/** + * @brief 标题栏高度 + * @return + */ +int SARibbonBar::PrivateData::titleBarHeight() const +{ + if (mUserDefTitleBarHeight) { + return *mUserDefTitleBarHeight; + } + return mTitleBarHeight; +} + +/** + * @brief tab高度 + * @return + */ +int SARibbonBar::PrivateData::tabBarHeigth() const +{ + if (mUserDefTabBarHeight) { + return *mUserDefTabBarHeight; + } + return mTabBarHeight; +} + +/** + * @brief category高度 + * @return + */ +int SARibbonBar::PrivateData::categoryHeight() const +{ + if (mUserDefCategoryHeight) { + return *mUserDefCategoryHeight; + } else { + return mCategoryHeight; + } +} + +/** + * @brief 更新推荐的尺寸值 + */ +void SARibbonBar::PrivateData::updateHintSize() +{ + auto fm = q_ptr->fontMetrics(); + mTitleBarHeight = calcTitleBarHeight(); + // mTabBarHeight有大于0的值说明用户设置了,就使用用户设置的值 + mTabBarHeight = calcTabBarHeight(); + mCategoryHeight = calcCategoryHeight(); +} + +void SARibbonBar::PrivateData::setApplicationButton(QAbstractButton* btn) +{ + if (mApplicationButton) { + mApplicationButton->hide(); + mApplicationButton->deleteLater(); + } + if (btn) { + if (btn->parent() != q_ptr) { + btn->setParent(q_ptr); + } + btn->move(0, q_ptr->titleBarHeight()); + btn->show(); + q_ptr->connect(btn, &QAbstractButton::clicked, q_ptr, &SARibbonBar::applicationButtonClicked); + } + mApplicationButton = btn; +} + +bool SARibbonBar::PrivateData::isContainContextCategoryInList(SARibbonContextCategory* contextCategory) +{ + for (int i = 0; i < mCurrentShowingContextCategory.size(); ++i) { + if (mCurrentShowingContextCategory[ i ] == contextCategory) { + return (true); + } + } + return (false); +} + +void SARibbonBar::PrivateData::setMinimumMode() +{ + mCurrentRibbonMode = SARibbonBar::MinimumRibbonMode; + this->mStackedContainerWidget->setPopupMode(); + this->mStackedContainerWidget->setFocusPolicy(Qt::NoFocus); + this->mStackedContainerWidget->clearFocus(); + this->mRibbonTabBar->setFocus(); + this->mStackedContainerWidget->hide(); + resetSize(); +} + +void SARibbonBar::PrivateData::setNormalMode() +{ + mCurrentRibbonMode = SARibbonBar::NormalRibbonMode; + this->mStackedContainerWidget->setNormalMode(); + this->mStackedContainerWidget->setFocus(); + this->mStackedContainerWidget->show(); + resetSize(); +} + +QColor SARibbonBar::PrivateData::getContextCategoryColor() +{ + if (mContextCategoryColorList.isEmpty()) { + mContextCategoryColorListIndex = -1; + return (QColor()); + } + ++mContextCategoryColorListIndex; + if ((mContextCategoryColorListIndex >= mContextCategoryColorList.size()) || (mContextCategoryColorListIndex < 0)) { + mContextCategoryColorListIndex = 0; + } + return (mContextCategoryColorList.at(mContextCategoryColorListIndex)); +} + +void SARibbonBar::PrivateData::updateTabData() +{ + int tabcount = mRibbonTabBar->count(); + + for (int i = 0; i < tabcount; ++i) { + QVariant var = mRibbonTabBar->tabData(i); + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + p.index = i; + mRibbonTabBar->setTabData(i, QVariant::fromValue(p)); + } + } + // 刷新完tabdata信息也要接着刷新ContextCategory信息 + for (_SAContextCategoryManagerData& cd : mCurrentShowingContextCategory) { + cd.tabPageIndex.clear(); + for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) { + SARibbonCategory* category = cd.contextCategory->categoryPage(i); + for (int t = 0; t < tabcount; ++t) { + QVariant v = mRibbonTabBar->tabData(t); + if (v.isValid()) { + _SARibbonTabData d = v.value< _SARibbonTabData >(); + if (d.category == category) { + cd.tabPageIndex.append(t); + } + } else { + cd.tabPageIndex.append(-1); + } + } + } + } +} + +QSize SARibbonBar::PrivateData::calcIconSizeByHeight(int h) +{ + if (h - 8 >= 20) { + return QSize(h - 8, h - 8); + } + return QSize(h - 4, h - 4); +} + +//=================================================== +// SARibbonBar +//=================================================== + +/** + * @brief SARibbonBar构造函数 + * @param parent + */ +SARibbonBar::SARibbonBar(QWidget* parent) : QMenuBar(parent), d_ptr(new SARibbonBar::PrivateData(this)) +{ + d_ptr->init(); + ensurePolished(); + setNativeMenuBar(false); + // #ifdef Q_OS_MACOS + // setNativeMenuBar(false); + // #endif + // #ifdef Q_OS_LINUX + // setNativeMenuBar(false); + // #endif + if (parent) { + connect(parent, &QWidget::windowTitleChanged, this, &SARibbonBar::onWindowTitleChanged); + connect(parent, &QWidget::windowIconChanged, this, &SARibbonBar::onWindowIconChanged); + } + setRibbonStyle(RibbonStyleLooseThreeRow); +} + +SARibbonBar::~SARibbonBar() +{ +} + +/** + * @brief 判断样式是否为2行 + * @param s + * @return 2行返回true,返回false代表当前是3行 + */ +bool SARibbonBar::isTwoRowStyle(SARibbonBar::RibbonStyles s) +{ + return s.testFlag(SARibbonBar::RibbonStyleTwoRow); +} + +/** + * @brief 判断样式是否为3行 + * @param s + * @return 3行返回true,返回false代表当前是2行 + */ +bool SARibbonBar::isThreeRowStyle(RibbonStyles s) +{ + return s.testFlag(SARibbonBar::RibbonStyleThreeRow); +} + +/** + * @brief 判断是否是宽松样式 + * @param s + * @return 宽松样式(带标题栏)返回true,否则就是紧凑样式 + */ +bool SARibbonBar::isLooseStyle(SARibbonBar::RibbonStyles s) +{ + return s.testFlag(SARibbonBar::RibbonStyleLoose); +} + +/** + * @brief 判断是否是紧凑样式 + * @param s + * @return 紧凑样式(不带标题栏)返回true,否则就是宽松样式 + */ +bool SARibbonBar::isCompactStyle(RibbonStyles s) +{ + return s.testFlag(SARibbonBar::RibbonStyleCompact); +} + +/** + * @brief 获取版本信息 + * @return {SA_RIBBON_BAR_VERSION_MAJ}.{SA_RIBBON_BAR_VERSION_MIN}.{SA_RIBBON_BAR_VERSION_PAT} + */ +QString SARibbonBar::versionString() +{ + return QString("%1.%2.%3").arg(SA_RIBBON_BAR_VERSION_MAJ).arg(SA_RIBBON_BAR_VERSION_MIN).arg(SA_RIBBON_BAR_VERSION_PAT); +} + +/** + @brief 获取默认的上下文标签颜色列表 + @return + */ +QList< QColor > SARibbonBar::defaultContextCategoryColorList() +{ + QList< QColor > res; + res << QColor(201, 89, 156) // 玫红 + << QColor(242, 203, 29) // 黄 + << QColor(255, 157, 0) // 橙 + << QColor(14, 81, 167) // 蓝 + << QColor(228, 0, 69) // 红 + << QColor(67, 148, 0) // 绿 + ; + return res; +} + +/** + * @brief 提供高分屏的支持静态函数 + * + * @note 此函数需要在main函数,QApplication生成之前调用 + * @code + * int main(int argc, char* argv[]){ + * SARibbonBar::initHighDpi(); + * QApplication a(argc, argv); + * ... + * } + * @endcode + */ +void SARibbonBar::initHighDpi() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif +} + +/** + * @brief 返回applicationButton + * @return 默认的applicationButton是@ref SARibbonApplicationButton 生成,通过@ref setApplicationButton 可设置为其他button + */ +QAbstractButton* SARibbonBar::applicationButton() +{ + return (d_ptr->mApplicationButton); +} + +/** + * @brief 设置applicationButton,如果想隐藏,可以传入nullptr + * + * 默认会有一个SARibbonApplicationButton,如果想取消,可传入nullptr,或者自定义的button也可以传入 + * + * @note applicationButton的所有权归SARibbonBar所有,不要在外部对applicationButton进行delete操作 + * @param btn applicationButton指针,可以传入@ref SARibbonApplicationButton, + * SA已经对SARibbonApplicationButton进行了样式设置 + */ +void SARibbonBar::setApplicationButton(QAbstractButton* btn) +{ + d_ptr->setApplicationButton(btn); + if (btn) { + if (btn->objectName().isEmpty()) { + btn->setObjectName(QStringLiteral("SARibbonApplicationButton")); + } + btn->setVisible(true); + // btn->setGeometry(applicationButtonGeometry()); + } + // 无论设置为什么都触发resize + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +/** + * @brief 返回tabbar + * @return SARibbonTabBar指针 + * @sa SARibbonTabBar + */ +SARibbonTabBar* SARibbonBar::ribbonTabBar() +{ + return (d_ptr->mRibbonTabBar); +} + +/** + * @brief 添加一个标签 + * 如果需要删除,直接delete即可,SARibbonBar会对其进行处理 + * @param title 标签名字,默认情况下SARibbonCategory的object name也被设置为title + * @return 返回一个窗口容器,在Category里可以添加其他控件 + * @sa SARibbonCategory + */ +SARibbonCategory* SARibbonBar::addCategoryPage(const QString& title) +{ + SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this); + + category->setObjectName(title); + category->setCategoryName(title); + addCategoryPage(category); + return (category); +} + +/** + * @brief 添加一个标签 + * @param category + */ +void SARibbonBar::addCategoryPage(SARibbonCategory* category) +{ + if (nullptr == category) { + return; + } + int index = d_ptr->mRibbonTabBar->count(); + insertCategoryPage(category, index); +} + +/** + * @brief qtdesigner专用 + * @param category + */ +void SARibbonBar::addCategoryPage(QWidget* category) +{ + SARibbonCategory* c = qobject_cast< SARibbonCategory* >(category); + + if (c) { + addCategoryPage(c); + } +} + +/** + * @brief 添加一个category,category的位置在index,如果当前category数量少于index,将插入到最后 + * @param title category的标题 + * @param index category的位置 + * @return + */ +SARibbonCategory* SARibbonBar::insertCategoryPage(const QString& title, int index) +{ + SARibbonCategory* category = RibbonSubElementFactory->createRibbonCategory(this); + + category->setObjectName(title); + category->setCategoryName(title); + insertCategoryPage(category, index); + return (category); +} + +void SARibbonBar::insertCategoryPage(SARibbonCategory* category, int index) +{ + if (nullptr == category) { + return; + } + category->setPannelLayoutMode(d_ptr->mDefaulePannelLayoutMode); + int i = d_ptr->mRibbonTabBar->insertTab(index, category->categoryName()); + + _SARibbonTabData tabdata; + + tabdata.category = category; + tabdata.index = i; + d_ptr->mRibbonTabBar->setTabData(i, QVariant::fromValue(tabdata)); + d_ptr->mStackedContainerWidget->insertWidget(index, category); + connect(category, &QWidget::windowTitleChanged, this, &SARibbonBar::onCategoryWindowTitleChanged); + // 更新index信息 + d_ptr->updateTabData(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +/** + * @brief 通过名字查找Category + * @param title Category的名字,既标签的标题 + * @return 如果没有找到,将返回nullptr,如果有重名,将返回第一个查询到的名字,因此,尽量避免重名标签 + * @note 由于翻译等原因,可能title会变化,因此如果想通过固定内容查找category,应该使用 @ref categoryByObjectName + * @see categoryByObjectName + */ +SARibbonCategory* SARibbonBar::categoryByName(const QString& title) const +{ + int c = d_ptr->mStackedContainerWidget->count(); + + for (int i = 0; i < c; ++i) { + SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i)); + if (w) { + if (w->categoryName() == title) { + return (w); + } + } + } + return (nullptr); +} + +/** + * @brief 通过ObjectName查找Category + * @param objname + * @return 如果没有找到,将返回nullptr,如果有同样的ObjectName,将返回第一个查询到的名字,因此,尽量避免ObjectName重名 + * @see categoryByName + */ +SARibbonCategory* SARibbonBar::categoryByObjectName(const QString& objname) const +{ + int c = d_ptr->mStackedContainerWidget->count(); + + for (int i = 0; i < c; ++i) { + SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i)); + if (w) { + if (w->objectName() == objname) { + return (w); + } + } + } + return (nullptr); +} + +/** + * @brief 通过索引找到category,如果超过索引范围,会返回nullptr + * @param index 索引 + * @return 如果超过索引范围,会返回nullptr + * @note 如果此时有上下文标签,上下文的标签也会返回 + * @note 通过索引查找的category必须是visible状态的category,如果通过@ref hideCategory 隐藏的标签,通过索引是找不到的 + * @note 通过@ref categoryByObjectName 可以找到所有加入过的标签, + * 如果想得到ribbonbar管理的所有标签,可以通过函数@ref categoryPages 得到 + * @see categoryIndex categoryByObjectName categoryByName + */ +SARibbonCategory* SARibbonBar::categoryByIndex(int index) const +{ + QVariant var = d_ptr->mRibbonTabBar->tabData(index); + + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + return (p.category); + } + return (nullptr); +} + +/** + * @brief 隐藏category,并不会删除或者取走,只是隐藏 + * @param category + */ +void SARibbonBar::hideCategory(SARibbonCategory* category) +{ + int tabcount = d_ptr->mRibbonTabBar->count(); + + for (int i = 0; i < tabcount; ++i) { + QVariant var = d_ptr->mRibbonTabBar->tabData(i); + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + if (p.category == category) { + d_ptr->mHidedCategory.append(p); + d_ptr->mRibbonTabBar->removeTab(i); // 仅仅把tab移除 + // 注意Category隐藏后,contex的位置就会发生变化,需要更新 + d_ptr->updateTabData(); + return; + } + } + } +} + +/** + * @brief 显示被隐藏的category + * @param category + */ +void SARibbonBar::showCategory(SARibbonCategory* category) +{ + for (auto i = d_ptr->mHidedCategory.begin(); i != d_ptr->mHidedCategory.end(); ++i) { + if (i->category == category) { + // 说明要显示 + int index = d_ptr->mRibbonTabBar->insertTab(i->index, i->category->categoryName()); + i->index = index; + d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(*i)); + d_ptr->mHidedCategory.erase(i); // 移除 + // 更新index信息 + d_ptr->updateTabData(); + raiseCategory(category); + return; + } + } + raiseCategory(category); +} + +/** + * @brief 判断这个category是否在显示状态,也就是tabbar有这个category + * @param category + * @return + */ +bool SARibbonBar::isCategoryVisible(const SARibbonCategory* c) const +{ + int tabindex = categoryIndex(c); + + return (tabindex >= 0); +} + +/** + * @brief 获取category的索引 + * @param c + * @return 如果找不到,返回-1 + */ +int SARibbonBar::categoryIndex(const SARibbonCategory* c) const +{ + // category的顺序不能以stackedwidget为准,因为存在contextcategory,contextcategory正常是不显示的 + int tabcount = d_ptr->mRibbonTabBar->count(); + + for (int i = 0; i < tabcount; ++i) { + QVariant var = d_ptr->mRibbonTabBar->tabData(i); + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + if (p.category == c) { + return (i); + } + } + } + return (-1); +} + +/** + * @brief 移动一个Category从from index到to index + * @param from + * @param to + */ +void SARibbonBar::moveCategory(int from, int to) +{ + d_ptr->mRibbonTabBar->moveTab(from, to); + // 这时要刷新所有tabdata的index信息 + d_ptr->updateTabData(); + // 这里会触发tabMoved信号,在tabMoved信号中调整stacked里窗口的位置 +} + +/** + * @brief 获取当前显示的所有的SARibbonCategory,包含未显示的SARibbonContextCategory的SARibbonCategory也一并返回 + * + * @return + */ +QList< SARibbonCategory* > SARibbonBar::categoryPages(bool getAll) const +{ + int c = d_ptr->mStackedContainerWidget->count(); + QList< SARibbonCategory* > res; + + for (int i = 0; i < c; ++i) { + SARibbonCategory* w = qobject_cast< SARibbonCategory* >(d_ptr->mStackedContainerWidget->widget(i)); + if (w) { + if (!getAll && w->isContextCategory()) { + // 不是getall且是上下文时跳过 + continue; + } + res.append(w); + } + } + return (res); +} + +/** + * @brief 移除SARibbonCategory + * + * SARibbonBar不会delete SARibbonCategory*,但这个SARibbonCategory会脱离SARibbonBar的管理 + * 表现在tabbar会移除,面板会移除,使用此函数后可以对SARibbonCategory进行delete + * @param category + */ +void SARibbonBar::removeCategory(SARibbonCategory* category) +{ + int index = tabIndex(category); + bool isupdate = false; + if (index >= 0) { + d_ptr->mRibbonTabBar->removeTab(index); + isupdate = true; + } + d_ptr->mStackedContainerWidget->removeWidget(category); + // 同时验证这个category是否是contexcategory里的 + + for (SARibbonContextCategory* c : qAsConst(d_ptr->mContextCategoryList)) { + c->takeCategory(category); + } + // 这时要刷新所有tabdata的index信息 + if (isupdate) { + d_ptr->updateTabData(); + } + // 移除完后需要重绘 + repaint(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +/** + * @brief 添加上下文标签 + * + * 上下文标签是特殊时候触发的标签,需要用户手动触发 + * + * 调用@ref SARibbonContextCategory::addCategoryPage 可在上下文标签中添加SARibbonCategory, + * 在上下文标签添加的SARibbonCategory,只有在上下文标签显示的时候才会显示 + * @param title 上下文标签的标题,在Office模式下会显示,在wps模式下不显示。默认情况下SARibbonContextCategory的object name也被设置为title + * @param color 上下文标签的颜色,如果指定为空QColor(),将会使用SARibbonBar的默认色系 + * @param id 上下文标签的id,以便进行查找 + * @return 返回上下文标签指针 + * @note SARibbonBar拥有SARibbonContextCategory的管理权,用户避免在外部直接delete,如果要删除,调用@ref destroyContextCategory 函数 + */ +SARibbonContextCategory* SARibbonBar::addContextCategory(const QString& title, const QColor& color, const QVariant& id) +{ + SARibbonContextCategory* context = RibbonSubElementFactory->createRibbonContextCategory(this); + + context->setObjectName(title); + context->setContextTitle(title); + context->setId(id); + context->setContextColor(color.isValid() ? color : d_ptr->getContextCategoryColor()); + addContextCategory(context); + return (context); +} + +/** + * @brief 添加上下文标签 + * @param context + */ +void SARibbonBar::addContextCategory(SARibbonContextCategory* context) +{ + if (nullptr == context) { + return; + } + connect(context, &SARibbonContextCategory::categoryPageAdded, this, &SARibbonBar::onContextsCategoryPageAdded); + connect(context, &SARibbonContextCategory::categoryTitleChanged, this, &SARibbonBar::onContextsCategoryCategoryNameChanged); + // remove并没有绑定,主要是remove后在stacked里也不会显示,remove且delete的话,stacked里也会删除 + d_ptr->mContextCategoryList.append(context); +} + +/** + * @brief 显示上下文标签 + * @param context 上下文标签指针 + */ +void SARibbonBar::showContextCategory(SARibbonContextCategory* context) +{ + if (isContextCategoryVisible(context)) { + return; + } + _SAContextCategoryManagerData contextCategoryData; + + contextCategoryData.contextCategory = context; + for (int i = 0; i < context->categoryCount(); ++i) { + SARibbonCategory* category = context->categoryPage(i); + // 此句如果模式重复设置不会进行多余操作 + category->setPannelLayoutMode(d_ptr->mDefaulePannelLayoutMode); + // 切换模式后会改变高度,上下文标签显示时要保证显示出来 + int index = d_ptr->mRibbonTabBar->addTab(category->categoryName()); + contextCategoryData.tabPageIndex.append(index); + + _SARibbonTabData tabdata; + tabdata.category = category; + tabdata.index = index; + d_ptr->mRibbonTabBar->setTabData(index, QVariant::fromValue(tabdata)); + } + d_ptr->mCurrentShowingContextCategory.append(contextCategoryData); + // 由于上下文都是在最后追加,不需要调用updateTabData(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +/** + * @brief 隐藏上下文标签 + * @param context 上下文标签指针 + */ +void SARibbonBar::hideContextCategory(SARibbonContextCategory* context) +{ + bool needResize = false; + + for (int i = 0; i < d_ptr->mCurrentShowingContextCategory.size(); ++i) { + if (d_ptr->mCurrentShowingContextCategory[ i ].contextCategory == context) { + const QList< int >& indexs = d_ptr->mCurrentShowingContextCategory[ i ].tabPageIndex; + for (int j = indexs.size() - 1; j >= 0; --j) { + d_ptr->mRibbonTabBar->removeTab(indexs[ j ]); + } + // 注意,再删除ContextCategory后,tab的序号就会改变,这时,这个tab后面的都要调整它的序号 + needResize = true; + d_ptr->mCurrentShowingContextCategory.removeAt(i); + // 移除了ContextCategory后需要break + break; + } + } + if (needResize) { + d_ptr->updateTabData(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); + } +} + +/** + * @brief 判断上下文是否在显示状态 + * @param context + * @return 在显示状态返回true + * @sa setContextCategoryVisible + */ +bool SARibbonBar::isContextCategoryVisible(SARibbonContextCategory* context) +{ + return (d_ptr->isContainContextCategoryInList(context)); +} + +/** + * @brief 设置上下文标签的显示状态 + * + * 上下文标签的当前显示状态可通过 @ref isContextCategoryVisible 进行判断 + * @param context 上下文标签 + * @param visible 显示状态,true为显示 + */ +void SARibbonBar::setContextCategoryVisible(SARibbonContextCategory* context, bool visible) +{ + if (nullptr == context) { + return; + } + if (visible) { + showContextCategory(context); + } else { + hideContextCategory(context); + } +} + +/** + * @brief 获取所有的上下文标签 + * @return 返回上下文标签列表 + */ +QList< SARibbonContextCategory* > SARibbonBar::contextCategoryList() const +{ + return (d_ptr->mContextCategoryList); +} + +/** + * @brief 销毁上下文标签,上下文标签的SARibbonCategory也会随之销毁 + * @param context 需要销毁的上下文标签指针 + */ +void SARibbonBar::destroyContextCategory(SARibbonContextCategory* context) +{ + if (nullptr == context) { + return; + } + //! 1、如果上下文标签显示中,先隐藏 + if (isContextCategoryVisible(context)) { + hideContextCategory(context); + } + //! 2、删除上下文标签的相关内容 + d_ptr->mContextCategoryList.removeAll(context); + //! + QList< SARibbonCategory* > res = context->categoryList(); + + for (SARibbonCategory* c : qAsConst(res)) { + c->hide(); + c->deleteLater(); + } + context->deleteLater(); + QApplication::postEvent(this, new QResizeEvent(size(), size())); +} + +/** + * @brief 设置为最小/正常模式 + * + * 隐藏模式下,只会显示tabbar,不会显示内容,默认状态是显示模式 + * + * 默认下双击tabbar会切换隐藏显示模式,如果想禁用此功能,可以重载 @ref onCurrentRibbonTabDoubleClicked + * 函数,不对函数进行任何处理即可 + * + * @param isMinimum 参数为true时,切换为Minimum模式 + * @see 此函数会改变@ref RibbonState 状态,通过@ref currentRibbonState 函数可以查看当前状态 + */ +void SARibbonBar::setMinimumMode(bool isMinimum) +{ +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + qDebug() << "SARibbonBar::setHideMode " << isMinimum; +#endif + if (isMinimum) { + d_ptr->setMinimumMode(); + } else { + d_ptr->setNormalMode(); + } + QResizeEvent resizeEvent(size(), size()); + QApplication::sendEvent(this, &resizeEvent); + // 发射信号 + emit ribbonModeChanged(isMinimum ? MinimumRibbonMode : NormalRibbonMode); +} + +/// +/// \brief 当前ribbon是否是隐藏模式 +/// \return +/// +bool SARibbonBar::isMinimumMode() const +{ + return (d_ptr->mStackedContainerWidget->isPopupMode()); +} + +/// +/// \brief 设置显示隐藏ribbon按钮 +/// +void SARibbonBar::showMinimumModeButton(bool isShow) +{ + if (isShow) { + activeRightButtonGroup(); + + d_ptr->mMinimumCategoryButtonAction = new QAction(this); + d_ptr->mMinimumCategoryButtonAction->setIcon( + style()->standardIcon(isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton, nullptr)); + connect(d_ptr->mMinimumCategoryButtonAction, &QAction::triggered, this, [ this ]() { + this->setMinimumMode(!isMinimumMode()); + this->d_ptr->mMinimumCategoryButtonAction->setIcon( + style()->standardIcon(isMinimumMode() ? QStyle::SP_TitleBarUnshadeButton : QStyle::SP_TitleBarShadeButton, nullptr)); + }); + d_ptr->mRightButtonGroup->addAction(d_ptr->mMinimumCategoryButtonAction); + + } else { + if (nullptr != d_ptr->mMinimumCategoryButtonAction) { + d_ptr->mMinimumCategoryButtonAction->deleteLater(); + d_ptr->mMinimumCategoryButtonAction = nullptr; + } + } + QResizeEvent resizeEvent(size(), size()); + + QApplication::sendEvent(this, &resizeEvent); +} + +/** + * @brief 是否显示隐藏ribbon按钮 + * @return + */ +bool SARibbonBar::haveShowMinimumModeButton() const +{ + return (nullptr != d_ptr->mMinimumCategoryButtonAction); +} + +/** + @brief 隐藏ribbon对应的action + @return + */ +QAction* SARibbonBar::minimumModeAction() const +{ + return d_ptr->mMinimumCategoryButtonAction; +} + +/** + * @brief 当前ribbon的状态(正常|最小化) + * @return + */ +SARibbonBar::RibbonMode SARibbonBar::currentRibbonState() const +{ + return (d_ptr->mCurrentRibbonMode); +} + +/** + @brief tabBar的高度 + @return + */ +int SARibbonBar::tabBarHeight() const +{ + return d_ptr->mRibbonTabBar->height(); +} + +/** + * @brief 设置tabbar的高度 + * + * 用户调用setTabBarHeight后,将使用用户设定的高度,而不使用自动计算的高度,这时tabbar高度不会跟随字体等信息重新计算 + * + * @note 注意,在RibbonStyleCompact**模式下,tabbar高度要保证小于等于titlebar高度,否则会显示异常 + * @note 此函数不会自动刷新,如果需要刷新调用此函数后需要调用@ref updateRibbonGeometry + * @param h + */ +void SARibbonBar::setTabBarHeight(int h, bool resizeByNow) +{ + if (nullptr == d_ptr->mUserDefTabBarHeight) { + d_ptr->mUserDefTabBarHeight = std::make_unique< int >(h); + } else { + *(d_ptr->mUserDefTabBarHeight) = h; + } + if (resizeByNow) { + updateRibbonGeometry(); + } +} + +/** + @brief 返回标题栏高度 + @sa setTitleBarHeight + @return + */ +int SARibbonBar::titleBarHeight() const +{ + return d_ptr->titleBarHeight(); +} + +/** + @brief 设置标题栏的高度 + @sa titleBarHeight + @note 此操作会发射@ref titleBarHeightChanged 信号 + @param h + */ +void SARibbonBar::setTitleBarHeight(int h, bool resizeByNow) +{ + int oldHeight = d_ptr->mTitleBarHeight; + if (nullptr == d_ptr->mUserDefTitleBarHeight) { + d_ptr->mUserDefTitleBarHeight = std::make_unique< int >(h); + } else { + *(d_ptr->mUserDefTitleBarHeight) = h; + // + oldHeight = *(d_ptr->mUserDefTitleBarHeight); + } + + if (resizeByNow) { + updateRibbonGeometry(); + } + emit titleBarHeightChanged(oldHeight, h); +} + +/** + * @brief category的高度 + * @return + */ +int SARibbonBar::categoryHeight() const +{ + return d_ptr->mStackedContainerWidget->height(); +} + +/** + * @brief 设置category的高度 + * @param h + * @param resizeByNow + */ +void SARibbonBar::setCategoryHeight(int h, bool resizeByNow) +{ + if (nullptr == d_ptr->mUserDefCategoryHeight) { + d_ptr->mUserDefCategoryHeight = std::make_unique< int >(h); + } else { + *(d_ptr->mUserDefCategoryHeight) = h; + } + if (resizeByNow) { + updateRibbonGeometry(); + } +} + +void SARibbonBar::onWindowTitleChanged(const QString& title) +{ + Q_UNUSED(title); + update(); +} + +void SARibbonBar::onWindowIconChanged(const QIcon& i) +{ + if (quickAccessBar()) { + quickAccessBar()->setIcon(i); + } +} + +/** + * @brief category的名字发生改变触发 + * @param title + */ +void SARibbonBar::onCategoryWindowTitleChanged(const QString& title) +{ + // 全部更新一遍 + Q_UNUSED(title); + updateCategoryTitleToTabName(); +} + +/// +/// \brief ribbon的显示界面隐藏 +/// +void SARibbonBar::onStackWidgetHided() +{ +} + +/** + * @brief 标签切换触发槽函数 + * @param index + */ +void SARibbonBar::onCurrentRibbonTabChanged(int index) +{ + QVariant var = d_ptr->mRibbonTabBar->tabData(index); + SARibbonCategory* category = nullptr; + + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + category = p.category; + } + if (category) { + if (d_ptr->mStackedContainerWidget->currentWidget() != category) { + d_ptr->mStackedContainerWidget->setCurrentWidget(category); + } + if (isMinimumMode()) { + d_ptr->mRibbonTabBar->clearFocus(); + if (!d_ptr->mStackedContainerWidget->isVisible()) { + if (d_ptr->mStackedContainerWidget->isPopupMode()) { + // 在stackedContainerWidget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开 + QHoverEvent ehl(QEvent::HoverLeave, + d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()), + d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos())); + QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl); + resizeStackedContainerWidget(); + d_ptr->mStackedContainerWidget->setFocus(); + d_ptr->mStackedContainerWidget->exec(); + // 在最小模式下,每次显示完stackedContainerWidget后把tab的 + // 的index设置为-1,这样每次点击都会触发onCurrentRibbonTabChanged + } + } else { + } + } + } + emit currentRibbonTabChanged(index); +} + +/** + * @brief ribbon tab bar单击 + * + * 此实现必须在eventfilter中传递stackedwidget的QEvent::MouseButtonDblClick事件到tabbar中,否则会导致双击变两次单击 + * + * 单击事件仅用于响应点击同一个tab时 + * @param index + */ +void SARibbonBar::onCurrentRibbonTabClicked(int index) +{ + if (index != d_ptr->mRibbonTabBar->currentIndex()) { + // 点击的标签不一致通过changed槽去处理 + return; + } + if (this->isMinimumMode()) { + if (!this->d_ptr->mStackedContainerWidget->isVisible()) { + if (this->d_ptr->mStackedContainerWidget->isPopupMode()) { + // 在stackedContainerWidget弹出前,先给tabbar一个QHoverEvent,让tabbar知道鼠标已经移开 + QHoverEvent ehl(QEvent::HoverLeave, + d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos()), + d_ptr->mRibbonTabBar->mapToGlobal(QCursor::pos())); + QApplication::sendEvent(d_ptr->mRibbonTabBar, &ehl); + // 弹出前都调整一下位置,避免移动后位置异常 + resizeStackedContainerWidget(); + this->d_ptr->mStackedContainerWidget->setFocus(); + this->d_ptr->mStackedContainerWidget->exec(); + } + } + } +} + +/** + * @brief ribbon tab bar双击 + * + * 默认情况下双击会切换最小和正常模式 + * @param index + */ +void SARibbonBar::onCurrentRibbonTabDoubleClicked(int index) +{ + Q_UNUSED(index); + setMinimumMode(!isMinimumMode()); +} + +void SARibbonBar::onContextsCategoryPageAdded(SARibbonCategory* category) +{ + Q_ASSERT_X(category != nullptr, "onContextsCategoryPageAdded", "add nullptr page"); + d_ptr->mStackedContainerWidget->addWidget(category); // 这里stackedWidget用append,其他地方都应该使用insert +} + +/** + * @brief 上下文标签管理的标签的名字发生变换 + * @param category + * @param title + */ +void SARibbonBar::onContextsCategoryCategoryNameChanged(SARibbonCategory* category, const QString& title) +{ + Q_UNUSED(category); + Q_UNUSED(title); + updateCategoryTitleToTabName(); +} + +/** + * @brief 标签移动的信号 + * @param from + * @param to + */ +void SARibbonBar::onTabMoved(int from, int to) +{ + const QSignalBlocker blocker(d_ptr->mStackedContainerWidget); + // 调整stacked widget的顺序,调整顺序是为了调用categoryPages函数返回的QList顺序和tabbar一致 + d_ptr->mStackedContainerWidget->moveWidget(from, to); +} + +/** + * @brief 根据SARibbonCategory*指针查找tabbar的index + * + * @param c SARibbonCategory对应的QObject指针 + * @return 如果没有找到,返回-1 + * @note 此函数不会调用SARibbonCategory*的任何方法,因此可以在SARibbonCategory的destroyed槽中调用 + */ +int SARibbonBar::tabIndex(SARibbonCategory* obj) +{ + const int size = d_ptr->mRibbonTabBar->count(); + + for (int i = 0; i < size; ++i) { + QVariant v = d_ptr->mRibbonTabBar->tabData(i); + if (v.isValid()) { + _SARibbonTabData innervalue = v.value< _SARibbonTabData >(); + if (innervalue.category == obj) { + return (i); + } + } + } + // 如果找不到就从stackedwidget中找 + + return (-1); +} + +void SARibbonBar::resizeAll() +{ + if (isLooseStyle()) { + resizeInLooseStyle(); + } else { + resizeInCompactStyle(); + } + update(); +} + +/** + * @brief 把ribbonbar的内容,同步进各个category中 + * @param autoUpdate + */ +void SARibbonBar::synchronousCategoryData(bool autoUpdate) +{ + iterate([ this ](SARibbonCategory* c) -> bool { + c->setEnableShowPannelTitle(this->isEnableShowPannelTitle()); + c->setPannelTitleHeight(this->pannelTitleHeight()); + c->setCategoryAlignment(this->ribbonAlignment()); + c->setPannelLayoutMode(this->pannelLayoutMode()); + return true; + }); + + //! 直接给一个resizeevent,让所有刷新 + if (autoUpdate) { + QResizeEvent* e = new QResizeEvent(size(), QSize()); + QApplication::postEvent(this, e); + } +} + +/** + @brief 激活tabbar右边的按钮群 + @return 返回右边的按钮群指针 + */ +SARibbonButtonGroupWidget* SARibbonBar::activeRightButtonGroup() +{ + if (nullptr == d_ptr->mRightButtonGroup) { + d_ptr->mRightButtonGroup = RibbonSubElementFactory->craeteButtonGroupWidget(this); + } + d_ptr->mRightButtonGroup->show(); + return d_ptr->mRightButtonGroup; +} + +/** + @brief 返回右边的按钮群指针 + @return 如果没有创建,返回nullptr + */ +SARibbonButtonGroupWidget* SARibbonBar::rightButtonGroup() +{ + return d_ptr->mRightButtonGroup; +} + +/** + @brief 激活QuickAccessBar + @return + */ +SARibbonQuickAccessBar* SARibbonBar::activeQuickAccessBar() +{ + if (nullptr == d_ptr->mQuickAccessBar) { + d_ptr->mQuickAccessBar = RibbonSubElementFactory->createQuickAccessBar(this); + d_ptr->mQuickAccessBar->setObjectName(QStringLiteral("objSARibbonQuickAccessBar")); + d_ptr->mQuickAccessBar->setIcon(windowIcon()); + } + d_ptr->mQuickAccessBar->show(); + return d_ptr->mQuickAccessBar; +} + +SARibbonQuickAccessBar* SARibbonBar::quickAccessBar() +{ + return (d_ptr->mQuickAccessBar); +} + +/** + * @brief 设置ribbonbar的风格,此函数会重新设置所有元素,包括button的布局方式, + * 尤其是从三行变到两行的过程,重设的内容较多 + * @note 此函数会自动触发ResizeEvent,不需要手动调用 + * @note 默认情况下2行模式不换行,如果想在两行模式换行,在调用SARibbonBar::setRibbonStyle后,再SARibbonToolButton::setEnableWordWrap(true) + * ,同时再调用updateRibbonElementGeometry()刷新布局 + * @param v 样式,见@ref SARibbonBar::RibbonStyle + */ +void SARibbonBar::setRibbonStyle(SARibbonBar::RibbonStyles v) +{ + // 先幅值给变量 + d_ptr->mRibbonStyle = v; +#if SA_DEBUG_PRINT_SIZE_HINT + qDebug() << "setRibbonStyle(" << v << ")" // + << "\n isThreeRowStyle=" << isThreeRowStyle() // + << "\n isTwoRowStyle=" << isTwoRowStyle() // + << "\n isLooseStyle=" << isLooseStyle() // + << "\n isCompactStyle=" << isCompactStyle() // + ; +#endif + // 执行判断 + setEnableWordWrap(isThreeRowStyle(v)); + setTabOnTitle(isCompactStyle()); + d_ptr->mQuickAccessBar->setEnableShowIcon(isLooseStyle()); + setEnableShowPannelTitle(isThreeRowStyle(v)); + setPannelLayoutMode(isThreeRowStyle(v) ? SARibbonPannel::ThreeRowMode : SARibbonPannel::TwoRowMode); + + // 此函数会调用setFixedHeight + synchronousCategoryData(false); // 这里不急着刷新,下面会继续刷新 + d_ptr->resetSize(); + + emit ribbonStyleChanged(d_ptr->mRibbonStyle); +} + +/** + @brief 返回当前ribbon的风格 + @return 返回当前ribbon的风格 + */ +SARibbonBar::RibbonStyles SARibbonBar::currentRibbonStyle() const +{ + return (d_ptr->mRibbonStyle); +} + +/** + @brief 切换到对应标签 + @param index 标签索引 + */ +void SARibbonBar::setCurrentIndex(int index) +{ + d_ptr->mRibbonTabBar->setCurrentIndex(index); + // onCurrentRibbonTabChanged(index); +} + +/** + * @brief 返回当前的tab索引 + * @return 当前的索引 + */ +int SARibbonBar::currentIndex() +{ + return (d_ptr->mRibbonTabBar->currentIndex()); +} + +/** + * @brief 确保标签显示出来,tab并切换到对应页 + * @param category 标签指针 + */ +void SARibbonBar::raiseCategory(SARibbonCategory* category) +{ + int index = d_ptr->mStackedContainerWidget->indexOf(category); + + if (index >= 0) { + setCurrentIndex(index); + } +} + +/** + * @brief 判断当前的样式是否为2行 + * @return + */ +bool SARibbonBar::isTwoRowStyle() const +{ + return (d_ptr->mDefaulePannelLayoutMode == SARibbonPannel::TwoRowMode); +} + +/** + * @brief 判断当前的样式是否为3行 + * @return + */ +bool SARibbonBar::isThreeRowStyle() const +{ + return (d_ptr->mDefaulePannelLayoutMode == SARibbonPannel::ThreeRowMode); +} + +/** + * @brief 判断当前的样式是否为宽松样式 + * @return + */ +bool SARibbonBar::isLooseStyle() const +{ + return (SARibbonBar::isLooseStyle(currentRibbonStyle())); +} + +/** + * @brief 判断当前的样式是否为紧凑样式 + * @return + */ +bool SARibbonBar::isCompactStyle() const +{ + return (SARibbonBar::isCompactStyle(currentRibbonStyle())); +} + +/** + * @brief 设置标题的文字颜色 + * + * 标题时mainwindow的windowTitle,如果要设置标题,直接调用SARibbonMainWindow::setWindowTitle 进行设置 + * + * 如果不设置标题颜色,默认是SARibbonBar的qss的color属性 + * @param clr + * @note 此函数不会刷新,刷新请自行调用repaint + */ +void SARibbonBar::setWindowTitleTextColor(const QColor& clr) +{ + d_ptr->mTitleTextColor = clr; +} + +/** + * @brief 获取标题的文字颜色 + * @return 如果返回的是无效颜色,!QColor::isValid(),说明没有手动设置颜色,颜色将跟随SARibbonBar的qss的文字颜色 + */ +QColor SARibbonBar::windowTitleTextColor() const +{ + return d_ptr->mTitleTextColor; +} + +/** + * @brief tabbar 底部会绘制一条线条,此接口定义线条颜色 + * @param clr + */ +void SARibbonBar::setTabBarBaseLineColor(const QColor& clr) +{ + d_ptr->mTabBarBaseLineColor = clr; +} + +/** + * @brief 获取tabbar底部基线颜色 + * @return + */ +QColor SARibbonBar::tabBarBaseLineColor() const +{ + return d_ptr->mTabBarBaseLineColor; +} + +/** + * @brief 更新ribbon的布局数据,此函数适用于一些关键性尺寸变化,换起ribbon下面元素的布局 + * + * @note 此函数调用较慢,避免在高速要求场合调用 + */ +void SARibbonBar::updateRibbonGeometry() +{ + d_ptr->resetSize(); + iterate([](SARibbonCategory* c) -> bool { + c->updateItemGeometry(); + return true; + }); + //! 直接给一个resizeevent,让所有刷新 + // QResizeEvent* e = new QResizeEvent(size(), QSize()); + // QApplication::postEvent(this, e); +} + +/** + * @brief SARibbonPannel的布局模式 + * @return + */ +SARibbonPannel::PannelLayoutMode SARibbonBar::pannelLayoutMode() const +{ + return d_ptr->mDefaulePannelLayoutMode; +} + +/** + * @brief SARibbonPannel的布局模式设置 + * @param m + */ +void SARibbonBar::setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m) +{ + d_ptr->mDefaulePannelLayoutMode = m; + iterate([ m ](SARibbonCategory* c) -> bool { + c->setPannelLayoutMode(m); + return true; + }); +} + +/** + * @brief 设置tab在title上面,这样可以省略title区域 + * @param on + */ +void SARibbonBar::setTabOnTitle(bool on) +{ + if (d_ptr->mIsTabOnTitle != on) { + d_ptr->mIsTabOnTitle = on; + d_ptr->resetSize(); + } +} + +/** + * @brief tab是否在title上面 + * @return + */ +bool SARibbonBar::isTabOnTitle() const +{ + return d_ptr->mIsTabOnTitle; +} + +/** + * @brief 设置标题的对齐方式 + * @param al + */ +void SARibbonBar::setWindowTitleAligment(Qt::Alignment al) +{ + d_ptr->mTitleAligment = al; +} + +/** + * @brief 获取标题的对齐方式 + * @return + */ +Qt::Alignment SARibbonBar::windowTitleAligment() const +{ + return d_ptr->mTitleAligment; +} + +/** + * @brief 设置ribbonbar的按钮文字允许换行 + * @param on + */ +void SARibbonBar::setEnableWordWrap(bool on) +{ + SARibbonToolButton::setEnableWordWrap(on); + updateRibbonGeometry(); +} + +/** + * @brief 判断是否允许换行 + * @return + */ +bool SARibbonBar::isEnableWordWrap() const +{ + return SARibbonToolButton::isEnableWordWrap(); +} + +/** + * @brief pannel标题栏的高度 + * @return + */ +int SARibbonBar::pannelTitleHeight() const +{ + return d_ptr->mPannelTitleHeight; +} + +/** + * @brief 设置pannel的高度 + * @param h + */ +void SARibbonBar::setPannelTitleHeight(int h) +{ + d_ptr->mPannelTitleHeight = h; + iterate([ h ](SARibbonCategory* c) -> bool { + c->setPannelTitleHeight(h); + return true; + }); +} + +/** + * @brief 是否pannel显示标题栏 + * @return + */ +bool SARibbonBar::isEnableShowPannelTitle() const +{ + return d_ptr->mEnableShowPannelTitle; +} + +/** + * @brief 设置显示pannel标题 + * @param on + */ +void SARibbonBar::setEnableShowPannelTitle(bool on) +{ + d_ptr->mEnableShowPannelTitle = on; + iterate([ on ](SARibbonCategory* c) -> bool { + c->setEnableShowPannelTitle(on); + return true; + }); +} + +/** + * @brief ribbonbar内部的StackedWidget + * 所有的category都放置在StackedWidget中 + * @return + */ +SARibbonStackedWidget* SARibbonBar::ribbonStackedWidget() +{ + return d_ptr->mStackedContainerWidget; +} + +/** + * @brief 设置是否显示标题 + * @param on + */ +void SARibbonBar::setTitleVisible(bool on) +{ + d_ptr->mIsTitleVisible = on; +} + +/** + * @brief 判断标题是否显示 + * @return + */ +bool SARibbonBar::isTitleVisible() const +{ + return d_ptr->mIsTitleVisible; +} + +/** + @brief 设置上下文标签的颜色列表 + + 上下文标签显示的时候,会从颜色列表中取颜色进行标签的渲染 + @param cls + */ +void SARibbonBar::setContextCategoryColorList(const QList< QColor >& cls) +{ + d_ptr->mContextCategoryColorList = cls; + if (d_ptr->mContextCategoryColorList.isEmpty()) { + d_ptr->mContextCategoryColorList = defaultContextCategoryColorList(); + } + d_ptr->mContextCategoryColorListIndex = 0; + // 这时需要对已经显示的contextCategoryData的颜色进行重新设置 + for (SARibbonContextCategory* c : d_ptr->mContextCategoryList) { + c->setContextColor(d_ptr->getContextCategoryColor()); + } +} + +/** + @brief 获取上下文标签的颜色列表 + @return + */ +QList< QColor > SARibbonBar::contextCategoryColorList() const +{ + return d_ptr->mContextCategoryColorList; +} + +/** + @brief 设置ribbon的对齐方式 + @param al + */ +void SARibbonBar::setRibbonAlignment(SARibbonAlignment al) +{ + d_ptr->mRibbonAlignment = al; + synchronousCategoryData(); +} + +/** + @brief ribbon的对齐方式 + @return + */ +SARibbonAlignment SARibbonBar::ribbonAlignment() const +{ + return d_ptr->mRibbonAlignment; +} + +/** + * @brief 此函数会遍历SARibbonBar下的所有Category,执行函数指针bool(SARibbonCategory*) + * @param fp 函数指针返回false则停止迭代 + * @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代 + */ +bool SARibbonBar::iterate(FpCategoryIterate fp) +{ + const QList< SARibbonCategory* > cs = categoryPages(true); + for (SARibbonCategory* c : cs) { + if (!fp(c)) { + return false; + } + } + return true; +} + +/** + * @brief 此函数会遍历SARibbonBar下的所有Category,并迭代所有的pannel,执行函数指针bool(SARibbonPannel*) + * @param fp 函数指针返回false则停止迭代 + * @return 返回false代表没有迭代完所有的category,中途接收到回调函数的false返回而中断迭代 + */ +bool SARibbonBar::iterate(FpPannelIterate fp) +{ + const QList< SARibbonCategory* > cs = categoryPages(true); + for (SARibbonCategory* c : cs) { + if (!c->iterate(fp)) { + return false; + } + } + return true; +} + +/** + * @brief 设置边角widget可见性,对于mdi窗口,会出现TopLeftCorner和TopRightCorner两个corner widget + * @param on + * @param c + */ +void SARibbonBar::setCornerWidgetVisible(bool on, Qt::Corner c) +{ + if (QWidget* w = cornerWidget(c)) { + w->setVisible(on); + } +} + +/** + * @brief SARibbonBar::eventFilter + * @param obj + * @param e + * @return + */ +bool SARibbonBar::eventFilter(QObject* obj, QEvent* e) +{ + if (obj) { + // 调整多文档时在窗口模式下的按钮更新 + if ((obj == cornerWidget(Qt::TopLeftCorner)) || (obj == cornerWidget(Qt::TopRightCorner))) { + if ((QEvent::UpdateLater == e->type()) || (QEvent::MouseButtonRelease == e->type()) + || (QEvent::WindowActivate == e->type())) { + QApplication::postEvent(this, new QResizeEvent(size(), size())); + } + } else if (obj == d_ptr->mStackedContainerWidget) { + // 在stack 是popup模式时,点击的是stackedContainerWidget区域外的时候,如果是在ribbonTabBar上点击 + // 那么忽略掉这次点击,把点击下发到ribbonTabBar,这样可以避免stackedContainerWidget在点击ribbonTabBar后 + // 隐藏又显示,出现闪烁 + if ((QEvent::MouseButtonPress == e->type()) || (QEvent::MouseButtonDblClick == e->type())) { + if (d_ptr->mStackedContainerWidget->isPopupMode()) { + QMouseEvent* mouseEvent = static_cast< QMouseEvent* >(e); + if (!d_ptr->mStackedContainerWidget->rect().contains(mouseEvent->pos())) { + QWidget* clickedWidget = QApplication::widgetAt(mouseEvent->globalPos()); + if (clickedWidget == d_ptr->mRibbonTabBar) { + const QPoint targetPoint = clickedWidget->mapFromGlobal(mouseEvent->globalPos()); + QMouseEvent* evPress = new QMouseEvent(mouseEvent->type(), + targetPoint, + mouseEvent->globalPos(), + mouseEvent->button(), + mouseEvent->buttons(), + mouseEvent->modifiers()); + QApplication::postEvent(clickedWidget, evPress); + return (true); + } + } + } + } + } + } + return (QMenuBar::eventFilter(obj, e)); +} + +/** + * @brief 根据情况重置tabbar的宽度,主要针对wps模式 + */ +int SARibbonBar::calcMinTabBarWidth() const +{ + // 20200831 + // tabBarWidth的宽度原来为endX - x;,现需要根据实际进行调整 + // 为了把tabbar没有tab的部分不占用,这里的宽度需要根据tab的size来进行设置,让tabbar的长度刚刚好,这样能让出 + // mainwindow的空间,接受鼠标事件,从而实现拖动等操作,否则tabbar占用整个顶栏,鼠标无法点击到mainwindow + // 计算tab所占用的宽度 + const QMargins& mg = d_ptr->mRibbonTabBar->tabMargin(); + return d_ptr->mRibbonTabBar->sizeHint().width() + (mg.left() + mg.right()); +} + +/** + * @brief 正常模式下的高度 + * + * 有可能SARibbonBar::height和mainBarHeight不相等,这种情况发生在RibbonState::MinimumRibbonMode状态下 + * @return 高度 + */ +int SARibbonBar::normalModeMainBarHeight() const +{ + return d_ptr->getCurrentNormalModeMainBarHeight(); +} + +/** + @brief 最小模式下的高度 + @return + */ +int SARibbonBar::minimumModeMainBarHeight() const +{ + return d_ptr->getCurrentMinimumModeMainBarHeight(); +} + +/** + * @brief 更新所有的category title对应的tab名 + * + * 此函数会对所有的category的名字和tab进行匹配,如果匹配不上会重新设置tab名 + */ +void SARibbonBar::updateCategoryTitleToTabName() +{ + SARibbonTabBar* tab = d_ptr->mRibbonTabBar; + for (int i = 0; i < tab->count(); ++i) { + // 鉴于tab不会很多,不考虑效率问题 + QVariant var = tab->tabData(i); + if (var.isValid()) { + _SARibbonTabData p = var.value< _SARibbonTabData >(); + if (p.category && p.category->categoryName() != tab->tabText(i)) { + tab->setTabText(i, p.category->categoryName()); + } + } + } + repaint(); +} + +/** + * @brief 告知WindowButtonGroup的尺寸 + * @param s + */ +void SARibbonBar::setWindowButtonGroupSize(const QSize& s) +{ + d_ptr->mWindowButtonSize = s; +} + +void SARibbonBar::paintEvent(QPaintEvent* e) +{ + Q_UNUSED(e); + if (isLooseStyle()) { + paintInLooseStyle(); + } else { + paintInCompactStyle(); + } +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + QPainter p(this); + HELP_DRAW_RECT(p, m_d->quickAccessBar->geometry()); + HELP_DRAW_RECT(p, m_d->ribbonTabBar->geometry()); + HELP_DRAW_RECT(p, m_d->stackedContainerWidget->geometry()); +#endif +} + +void SARibbonBar::paintInLooseStyle() +{ + QPainter p(this); + + //!绘制tabbar下的基线,这个函数仅仅对office2013主题有用,大部分主题都不绘制基线 + paintTabbarBaseLine(p); + + //! 显示上下文标签 + p.save(); + QList< _SAContextCategoryManagerData > contextCategoryDataList = d_ptr->mCurrentShowingContextCategory; + // bool isCurrentSelectContextCategoryPage = false; + + QPoint contextCategoryRegion(width(), -1); + QMargins border = contentsMargins(); + + for (int i = 0; i < contextCategoryDataList.size(); ++i) { + QRect contextTitleRect; + QList< int > indexs = contextCategoryDataList[ i ].tabPageIndex; + QColor clr = contextCategoryDataList[ i ].contextCategory->contextColor(); + if (!indexs.isEmpty()) { + contextTitleRect = d_ptr->mRibbonTabBar->tabRect(indexs.first()); + QRect endRect = d_ptr->mRibbonTabBar->tabRect(indexs.last()); + contextTitleRect.setRight(endRect.right()); + contextTitleRect.translate(d_ptr->mRibbonTabBar->x(), d_ptr->mRibbonTabBar->y()); + contextTitleRect.setHeight(d_ptr->mRibbonTabBar->height() - 1); // 减1像素,避免tabbar基线覆盖 + contextTitleRect -= d_ptr->mRibbonTabBar->tabMargin(); + // 把区域顶部扩展到窗口顶部 + contextTitleRect.setTop(border.top()); + // 绘制 + paintContextCategoryTab(p, contextCategoryDataList[ i ].contextCategory->contextTitle(), contextTitleRect, clr); + // 更新上下文标签的范围,用于控制标题栏的显示 + if (contextTitleRect.left() < contextCategoryRegion.x()) { + contextCategoryRegion.setX(contextTitleRect.left()); + } + if (contextTitleRect.right() > contextCategoryRegion.y()) { + contextCategoryRegion.setY(contextTitleRect.right()); + } + } + // isCurrentSelectContextCategoryPage = indexs.contains(d_ptr->mRibbonTabBar->currentIndex()); + // if (isCurrentSelectContextCategoryPage) { + // QPen pen; + // pen.setColor(clr); + // pen.setWidth(1); + // p.setPen(pen); + // p.setBrush(Qt::NoBrush); + // p.drawRect(d_ptr->mStackedContainerWidget->geometry()); + // isCurrentSelectContextCategoryPage = false; + // } + } + p.restore(); + //! 显示标题等 + QWidget* parWindow = parentWidget(); + + if (parWindow) { + QRect titleRegion; + if (contextCategoryRegion.y() < 0) { + titleRegion.setRect(d_ptr->mQuickAccessBar->geometry().right() + 1, + border.top(), + width() - d_ptr->mIconRightBorderPosition - border.right() + - d_ptr->mWindowButtonSize.width() - d_ptr->mQuickAccessBar->geometry().right() - 1, + titleBarHeight()); + } else { + int leftwidth = contextCategoryRegion.x() - d_ptr->mQuickAccessBar->geometry().right() - d_ptr->mIconRightBorderPosition; + int rightwidth = width() - contextCategoryRegion.y() - d_ptr->mWindowButtonSize.width(); + // if (width() - contextCategoryRegion.y() > contextCategoryRegion.x()-x) { + if (rightwidth > leftwidth) { + // 说明右边的区域大一点,标题显示在右,显示在右边需要减去windowbutton宽度 + titleRegion.setRect(contextCategoryRegion.y(), border.top(), rightwidth, titleBarHeight()); + } else { + // 说明左边的大一点 + titleRegion.setRect(d_ptr->mIconRightBorderPosition + d_ptr->mQuickAccessBar->geometry().right(), + border.top(), + leftwidth, + titleBarHeight()); + } + } +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + p.save(); + p.setBrush(QColor(255, 0, 0, 120)); + p.drawRect(titleRegion); + p.restore(); +#endif + paintWindowTitle(p, parWindow->windowTitle(), titleRegion); + } +} + +void SARibbonBar::paintInCompactStyle() +{ + QPainter p(this); + //! + paintTabbarBaseLine(p); + //! 显示上下文标签 + p.save(); + QList< _SAContextCategoryManagerData > contextCategoryDataList = d_ptr->mCurrentShowingContextCategory; + QMargins border = contentsMargins(); + for (int i = 0; i < contextCategoryDataList.size(); ++i) { + QRect contextTitleRect; + QList< int > indexs = contextCategoryDataList[ i ].tabPageIndex; + QColor clr = contextCategoryDataList[ i ].contextCategory->contextColor(); + if (!indexs.isEmpty()) { + contextTitleRect = d_ptr->mRibbonTabBar->tabRect(indexs.first()); + QRect endRect = d_ptr->mRibbonTabBar->tabRect(indexs.last()); + contextTitleRect.setRight(endRect.right()); + contextTitleRect.translate(d_ptr->mRibbonTabBar->x(), d_ptr->mRibbonTabBar->y()); + contextTitleRect.setHeight(d_ptr->mRibbonTabBar->height() - 1); + contextTitleRect -= d_ptr->mRibbonTabBar->tabMargin(); + // 把区域顶部扩展到窗口顶部 + contextTitleRect.setTop(border.top()); + // 绘制 + paintContextCategoryTab(p, QString(), contextTitleRect, clr); + } + } + p.restore(); + //! 显示标题等 + + QWidget* parWindow = parentWidget(); + + if (parWindow) { + int start = d_ptr->mRibbonTabBar->x() + d_ptr->mRibbonTabBar->width(); + int width = d_ptr->mQuickAccessBar->x() - start; + if (width > 20) { + QRect titleRegion(start, border.top(), width, titleBarHeight()); +#ifdef SA_RIBBON_DEBUG_HELP_DRAW + p.save(); + p.setBrush(QColor(255, 0, 0, 120)); + p.drawRect(titleRegion); + p.restore(); +#endif + paintWindowTitle(p, parWindow->windowTitle(), titleRegion); + } + } +} + +void SARibbonBar::resizeStackedContainerWidget() +{ + QMargins border = contentsMargins(); + const QRect& ribbonTabBarGeometry = d_ptr->mRibbonTabBar->geometry(); + + int x = border.left(); + int y = ribbonTabBarGeometry.bottom() + 1; + int w = width() - border.left() - border.right(); + int h = d_ptr->mCategoryHeight; + if (d_ptr->mStackedContainerWidget->isPopupMode()) { + // 弹出模式时,位置为全局位置 + QPoint absPosition = mapToGlobal(QPoint(x, y)); + x = absPosition.x(); + y = absPosition.y(); + } + d_ptr->mStackedContainerWidget->setFixedSize(QSize(w, h)); + d_ptr->mStackedContainerWidget->setGeometry(x, y, w, h); +} + +/** + * @brief 刷新所有ContextCategoryManagerData,这个在单独一个Category删除时调用 + */ +void SARibbonBar::updateContextCategoryManagerData() +{ + const int c = d_ptr->mRibbonTabBar->count(); + + for (_SAContextCategoryManagerData& cd : d_ptr->mCurrentShowingContextCategory) { + cd.tabPageIndex.clear(); + for (int i = 0; i < cd.contextCategory->categoryCount(); ++i) { + SARibbonCategory* category = cd.contextCategory->categoryPage(i); + for (int t = 0; t < c; ++t) { + QVariant v = d_ptr->mRibbonTabBar->tabData(t); + if (v.isValid()) { + _SARibbonTabData d = v.value< _SARibbonTabData >(); + if (d.category == category) { + cd.tabPageIndex.append(t); + } + } else { + cd.tabPageIndex.append(-1); + } + } + } + } +} + +/** + * @brief 绘制上下文标签的背景 + * @param painter 绘图QPainter + * @param title 上下文标签的title + * @param contextRect 上下文标签的绘制区域 + * @param color 上下文标签赋予的颜色 + */ +void SARibbonBar::paintContextCategoryTab(QPainter& painter, const QString& title, QRect contextRect, const QColor& color) +{ + // 绘制上下文标签 + // 首先有5像素的实体粗线位于顶部 + QMargins border = contentsMargins(); + painter.save(); + painter.setPen(Qt::NoPen); + painter.setBrush(color); + painter.drawRect(QRect(contextRect.x(), border.top(), contextRect.width(), 5)); + + // 剩下把颜色变亮90% + QColor gColor = color.lighter(190); + + // 减去之前的5像素 + contextRect -= QMargins(0, 5, 0, 0); + painter.fillRect(contextRect, gColor); + + // 只有在office模式下才需要绘制标题 + if (isLooseStyle()) { + if (!title.isEmpty()) { + contextRect.setBottom(d_ptr->mRibbonTabBar->geometry().top()); + painter.setPen(color); + painter.drawText(contextRect, Qt::AlignCenter, title); + } + } + painter.restore(); +} + +void SARibbonBar::resizeEvent(QResizeEvent* e) +{ + Q_UNUSED(e); + if (isLooseStyle()) { + resizeInLooseStyle(); + } else { + resizeInCompactStyle(); + } + update(); +} + +/** + * @brief 重写moveevent是为了在移动时调整isPopupMode状态下的stackedContainerWidget位置 + * @param event + */ +void SARibbonBar::moveEvent(QMoveEvent* e) +{ + if (d_ptr->mStackedContainerWidget) { + if (d_ptr->mStackedContainerWidget->isPopupMode()) { + // 弹出模式时,窗口发生了移动,同步调整StackedContainerWidget的位置 + resizeStackedContainerWidget(); + } + } + QMenuBar::moveEvent(e); +} + +/** + * @brief 跟踪字体改变 + * @param event + */ +void SARibbonBar::changeEvent(QEvent* e) +{ + if (nullptr == e) { + return; + } + switch (e->type()) { + case QEvent::FontChange: { + QFont f = font(); + QList< QWidget* > listWidgets = findChildren< QWidget* >(); + for (QWidget* w : listWidgets) { + w->setFont(f); + } + updateRibbonGeometry(); + } break; + case QEvent::StyleChange: { + updateRibbonGeometry(); + } break; + default: + break; + } + QMenuBar::changeEvent(e); +} + +bool SARibbonBar::event(QEvent* e) +{ + switch (e->type()) { + case QEvent::Show: + // 第一次显示刷新 + updateRibbonGeometry(); + break; + default: + break; + } + return QMenuBar::event(e); +} + +void SARibbonBar::resizeInLooseStyle() +{ + synchronousCategoryData(false); + + QMargins border = contentsMargins(); + int x = border.left(); + int y = border.top(); + + // cornerWidget - TopLeftCorner + const int validTitleBarHeight = d_ptr->titleBarHeight(); + const int tabH = d_ptr->tabBarHeigth(); + + // 布局corner widget + x += d_ptr->mIconRightBorderPosition + 5; + if (QWidget* connerL = cornerWidget(Qt::TopLeftCorner)) { + if (connerL->isVisibleTo(this)) { + QSize connerSize = connerL->sizeHint(); + if (connerSize.height() < validTitleBarHeight) { + int detal = (validTitleBarHeight - connerSize.height()) / 2; + connerL->setGeometry(x, y + detal, connerSize.width(), connerSize.height()); + } else { + connerL->setGeometry(x, y, connerSize.width(), validTitleBarHeight); + } + x = connerL->geometry().right() + 5; + } + } + // quick access bar定位 + if (d_ptr->mQuickAccessBar) { + if (d_ptr->mQuickAccessBar->isVisibleTo(this)) { + if (d_ptr->mQuickAccessBar->height() != validTitleBarHeight) { + d_ptr->mQuickAccessBar->setFixedHeight(validTitleBarHeight); + } + QSize quickAccessBarSize = d_ptr->mQuickAccessBar->sizeHint(); + // 上下留1px的边线 + d_ptr->mQuickAccessBar->setGeometry(x, y + 1, quickAccessBarSize.width(), validTitleBarHeight); + } + } + // 第二行,开始布局applicationButton,tabbar,tabBarRightSizeButtonGroupWidget,TopRightCorner + x = border.left(); + y += validTitleBarHeight; + // applicationButton 定位 + if (d_ptr->mApplicationButton) { + if (d_ptr->mApplicationButton->isVisibleTo(this)) { + d_ptr->mApplicationButton->setGeometry(x, y, d_ptr->mApplicationButton->sizeHint().width(), tabH); + x = d_ptr->mApplicationButton->geometry().right(); + } + } + // top right是一定要配置的,对于多文档窗口,子窗口的缩放等按钮就是通过这个窗口实现, + // 由于这个窗口一定要在最右,因此先对这个窗口进行布局 + // cornerWidget - TopRightCorner + // 获取最右边的位置 + int endX = width() - border.right(); + + if (QWidget* connerW = cornerWidget(Qt::TopRightCorner)) { + if (connerW->isVisibleTo(this)) { + QSize connerSize = connerW->sizeHint(); + endX -= connerSize.width(); + if (connerSize.height() < tabH) { + int detal = (tabH - connerSize.height()) / 2; + connerW->setGeometry(endX, y + detal, connerSize.width(), connerSize.height()); + } else { + connerW->setGeometry(endX, y, connerSize.width(), tabH); + } + } + } + // applicationButton和TopRightCorner完成定位,才可以定位tab bar + // tab bar 定位 + + // tabBar 右边的附加按钮组,这里一般会附加一些类似登录等按钮组 + // 20231106 把visible的判断去掉 && d_ptr->mRightButtonGroup->isVisible() + if (d_ptr->mRightButtonGroup) { + QSize wSize = d_ptr->mRightButtonGroup->sizeHint(); + endX -= wSize.width(); + // 上下留1px的边线 + d_ptr->mRightButtonGroup->setGeometry(endX, y + 1, wSize.width(), tabH - 2); + } + // 最后确定tabbar宽度 + int tabBarAllowedWidth = endX - x; + if (ribbonAlignment() == SARibbonAlignment::AlignLeft) { + d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); + } else { + // 居中对齐的情况下,Tab要居中显示 + // 得到tab的推荐尺寸 + int mintabBarWidth = calcMinTabBarWidth(); + if (mintabBarWidth >= tabBarAllowedWidth) { + // 这时tabbar没有居中对齐的必要性,空间位置不够了 + d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); + } else { + // 说明tabbar的宽度有居中的可能性 + int xoffset = (tabBarAllowedWidth - mintabBarWidth) / 2; + d_ptr->mRibbonTabBar->setGeometry(x + xoffset, y, mintabBarWidth, tabH); + } + } + resizeStackedContainerWidget(); +} + +void SARibbonBar::resizeInCompactStyle() +{ + synchronousCategoryData(false); + QMargins border = contentsMargins(); + int x = border.left(); + int y = border.top(); + + const int validTitleBarHeight = titleBarHeight(); + + // 先布局右边内容 + // cornerWidget - TopRightCorner + int endX = width() - border.right() - d_ptr->mWindowButtonSize.width(); + + if (QWidget* connerW = cornerWidget(Qt::TopRightCorner)) { + if (connerW->isVisibleTo(this)) { + QSize connerSize = connerW->sizeHint(); + endX -= connerSize.width(); + if (connerSize.height() < validTitleBarHeight) { + int detal = (validTitleBarHeight - connerSize.height()) / 2; + connerW->setGeometry(endX, y + detal, connerSize.width(), connerSize.height()); + } else { + connerW->setGeometry(endX, y, connerSize.width(), validTitleBarHeight); + } + } + } + + // tabBar 右边的附加按钮组 + // 20231106 把visible的判断去掉 && d_ptr->mRightButtonGroup->isVisible() + if (d_ptr->mRightButtonGroup) { + QSize wSize = d_ptr->mRightButtonGroup->sizeHint(); + endX -= wSize.width(); + // 上下留1px的边线 + d_ptr->mRightButtonGroup->setGeometry(endX, y + 1, wSize.width(), validTitleBarHeight - 2); + } + // quick access bar定位 + if (d_ptr->mQuickAccessBar) { + if (d_ptr->mQuickAccessBar->isVisibleTo(this)) { + QSize quickAccessBarSize = d_ptr->mQuickAccessBar->sizeHint(); + endX -= quickAccessBarSize.width(); + // 上下留1px的边线 + d_ptr->mQuickAccessBar->setGeometry(endX, y + 1, quickAccessBarSize.width(), validTitleBarHeight - 2); + } + } + // cornerWidget - TopLeftCorner + if (QWidget* connerL = cornerWidget(Qt::TopLeftCorner)) { + if (connerL->isVisibleTo(this)) { + QSize connerSize = connerL->sizeHint(); + endX -= connerSize.width(); + if (connerSize.height() < validTitleBarHeight) { + int detal = (validTitleBarHeight - connerSize.height()) / 2; + connerL->setGeometry(endX, y + detal, connerSize.width(), connerSize.height()); + } else { + connerL->setGeometry(endX, y, connerSize.width(), validTitleBarHeight); + } + } + } + + // tab 的y值需要重新计算 + int tabH = tabBarHeight(); + + if (tabH > validTitleBarHeight) { + // 这种直接把tabH设置为validTitleBarHeight + tabH = validTitleBarHeight; + } + y = y + validTitleBarHeight - tabH; // 如果tabH较小,则下以,让tab底部和title的底部对齐 + + // applicationButton 定位,与TabBar同高 + if (d_ptr->mApplicationButton) { + if (d_ptr->mApplicationButton->isVisibleTo(this)) { + d_ptr->mApplicationButton->setGeometry(x, y, d_ptr->mApplicationButton->sizeHint().width(), tabH); + x = d_ptr->mApplicationButton->geometry().right() + 2; + } + } + // tab bar 定位 wps模式下applicationButton的右边就是tab bar + int tabBarAllowedWidth = endX - x; + // 20200831 + // tabBarWidth的宽度原来为endX - x;,现需要根据实际进行调整 + // 为了把tabbar没有tab的部分不占用,这里的宽度需要根据tab的size来进行设置,让tabbar的长度刚刚好,这样能让出 + // mainwindow的空间,接受鼠标事件,从而实现拖动等操作,否则tabbar占用整个顶栏,鼠标无法点击到mainwindow + // 计算tab所占用的宽度 + int mintabBarWidth = calcMinTabBarWidth(); + + if (ribbonAlignment() == SARibbonAlignment::AlignLeft) { + if (mintabBarWidth < tabBarAllowedWidth) { + tabBarAllowedWidth = mintabBarWidth; + } + d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); + } else { + // 居中对齐 + if (mintabBarWidth >= tabBarAllowedWidth) { + // 这时tabbar没有居中对齐的必要性,空间位置不够了 + d_ptr->mRibbonTabBar->setGeometry(x, y, tabBarAllowedWidth, tabH); + } else { + // 说明tabbar的宽度有居中的可能性 + int xoffset = (tabBarAllowedWidth - mintabBarWidth) / 2; + d_ptr->mRibbonTabBar->setGeometry(x + xoffset, y, mintabBarWidth, tabH); + } + } + // 调整整个stackedContainer + resizeStackedContainerWidget(); +} + +/** + * @brief 绘制tabbar下的基准线,这个方法仅仅在office2013模式下需要 + * @param painter + */ +void SARibbonBar::paintTabbarBaseLine(QPainter& painter) +{ + if (!d_ptr->mTabBarBaseLineColor.isValid()) { + return; + } + painter.save(); + // 在tabbar下绘制一条线 + const int lineY = d_ptr->mRibbonTabBar->geometry().bottom(); + QPen pen(d_ptr->mTabBarBaseLineColor); + QMargins border = contentsMargins(); + + pen.setWidth(1); + pen.setStyle(Qt::SolidLine); + painter.setPen(pen); + painter.drawLine(QPoint(border.left(), lineY), QPoint(width() - border.right() - 1, lineY)); + painter.restore(); +} + +/// +/// \brief 绘制标题栏 +/// \param painter +/// \param title 标题 +/// \param contextCategoryRegion 当前显示的上下文标签的范围,上下文标签是可以遮挡标题栏的,因此需要知道上下文标签的范围 +/// x表示左边界,y表示右边界 +/// +void SARibbonBar::paintWindowTitle(QPainter& painter, const QString& title, const QRect& titleRegion) +{ + // 如果标题不显示直接跳出 + if (!isTitleVisible()) { + return; + } + painter.save(); + if (d_ptr->mTitleTextColor.isValid()) { + painter.setPen(d_ptr->mTitleTextColor); + } + painter.drawText(titleRegion, d_ptr->mTitleAligment, title); + painter.restore(); +} + +#if SA_DEBUG_PRINT_SARIBBONBAR +QDebug operator<<(QDebug debug, const SARibbonBar& ribbon) +{ + QDebugStateSaver saver(debug); + QFontMetrics fm = ribbon.fontMetrics(); + debug.nospace() << "SARibbonBar(" << ribbon.versionString() << ")" // + << "\nribbon font metrics info:" // + << "\n - lineSpacing:" << fm.lineSpacing() // + << "\n - height:" << fm.height() // + << "\n - em:" << fm.boundingRect("M").width() // + << "\n - ex:" << fm.boundingRect("X").height() // + << "\nribbon info:" // + << "\n -mTitleBarHeight=" << ribbon.d_ptr->mTitleBarHeight // + << "\n -mTabBarHeight=" << ribbon.d_ptr->mTabBarHeight // + << "\n -mPannelTitleHeight=" << ribbon.d_ptr->mPannelTitleHeight // + << "\n -mCategoryHeight=" << ribbon.d_ptr->mCategoryHeight // + << "\n -mIsTabOnTitle=" << ribbon.d_ptr->mIsTabOnTitle // + << "\n -mEnableShowPannelTitle=" << ribbon.d_ptr->mEnableShowPannelTitle // + << "\n -mWindowButtonSize=" << ribbon.d_ptr->mWindowButtonSize // + << "\n -mIconRightBorderPosition=" << ribbon.d_ptr->mIconRightBorderPosition // + ; + + return debug; +} +#endif + +/*** End of inlined file: SARibbonBar.cpp ***/ + +/*** Start of inlined file: SARibbonElementFactory.cpp ***/ +#include +#include + +SARibbonElementFactory::SARibbonElementFactory() +{ +} + +SARibbonElementFactory::~SARibbonElementFactory() +{ +} + +SARibbonTabBar* SARibbonElementFactory::createRibbonTabBar(QWidget* parent) +{ + return (new SARibbonTabBar(parent)); +} + +SARibbonApplicationButton* SARibbonElementFactory::createRibbonApplicationButton(QWidget* parent) +{ + return (new SARibbonApplicationButton(parent)); +} + +SARibbonCategory* SARibbonElementFactory::createRibbonCategory(QWidget* parent) +{ + return (new SARibbonCategory(parent)); +} + +SARibbonContextCategory* SARibbonElementFactory::createRibbonContextCategory(QWidget* parent) +{ + return (new SARibbonContextCategory(parent)); +} + +SARibbonPannel* SARibbonElementFactory::createRibbonPannel(QWidget* parent) +{ + return (new SARibbonPannel(parent)); +} + +SARibbonSeparatorWidget* SARibbonElementFactory::createRibbonSeparatorWidget(QWidget* parent) +{ + return (new SARibbonSeparatorWidget(parent)); +} + +SARibbonGallery* SARibbonElementFactory::createRibbonGallery(QWidget* parent) +{ + return (new SARibbonGallery(parent)); +} + +SARibbonGalleryGroup* SARibbonElementFactory::createRibbonGalleryGroup(QWidget* parent) +{ + return (new SARibbonGalleryGroup(parent)); +} + +SARibbonToolButton* SARibbonElementFactory::createRibbonToolButton(QWidget* parent) +{ + return (new SARibbonToolButton(parent)); +} + +SARibbonControlButton* SARibbonElementFactory::createRibbonControlButton(QWidget* parent) +{ + return (new SARibbonControlButton(parent)); +} + +SARibbonControlToolButton* SARibbonElementFactory::createRibbonControlToolButton(QWidget* parent) +{ + return (new SARibbonControlToolButton(parent)); +} + +SARibbonStackedWidget* SARibbonElementFactory::createRibbonStackedWidget(SARibbonBar* parent) +{ + return (new SARibbonStackedWidget(parent)); +} + +SARibbonButtonGroupWidget* SARibbonElementFactory::craeteButtonGroupWidget(QWidget* parent) +{ + return (new SARibbonButtonGroupWidget(parent)); +} + +SARibbonQuickAccessBar* SARibbonElementFactory::createQuickAccessBar(QWidget* parent) +{ + return (new SARibbonQuickAccessBar(parent)); +} + +SARibbonSystemButtonBar* SARibbonElementFactory::createWindowButtonGroup(QWidget* parent) +{ + return (new SARibbonSystemButtonBar(parent)); +} + +/** + * @brief 创建SARibbonPannelOptionButton + * @param pannel 附属的pannel + * @return + * @sa SARibbonPannelOptionButton + */ +SARibbonPannelOptionButton* SARibbonElementFactory::createRibbonPannelOptionButton(SARibbonPannel* pannel) +{ + return (new SARibbonPannelOptionButton(pannel)); +} + +/*** End of inlined file: SARibbonElementFactory.cpp ***/ + +/*** Start of inlined file: SARibbonElementManager.cpp ***/ +SARibbonElementManager::SARibbonElementManager() +{ + mFactory.reset(new SARibbonElementFactory()); +} + +SARibbonElementManager::~SARibbonElementManager() +{ +} + +SARibbonElementManager* SARibbonElementManager::instance() +{ + static SARibbonElementManager s_instance; + + return (&(s_instance)); +} + +SARibbonElementFactory* SARibbonElementManager::factory() +{ + return (mFactory.data()); +} + +void SARibbonElementManager::setupFactory(SARibbonElementFactory* delegate) +{ + mFactory.reset(delegate); +} + +/*** End of inlined file: SARibbonElementManager.cpp ***/ + +/*** Start of inlined file: SARibbonCustomizeData.cpp ***/ +#include +#include +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// SARibbonCustomizeData +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +SARibbonCustomizeData::SARibbonCustomizeData() + : indexValue(-1), actionRowProportionValue(SARibbonPannelItem::Large), m_type(UnknowActionType), m_actionsManagerPointer(nullptr) +{ +} + +SARibbonCustomizeData::SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr) + : indexValue(-1), actionRowProportionValue(SARibbonPannelItem::Large), m_type(type), m_actionsManagerPointer(mgr) +{ +} + +/** + * @brief 获取CustomizeData的action type + * @return + */ +SARibbonCustomizeData::ActionType SARibbonCustomizeData::actionType() const +{ + return (m_type); +} + +/** + * @brief 设置CustomizeData的action type + * @param a + */ +void SARibbonCustomizeData::setActionType(SARibbonCustomizeData::ActionType a) +{ + m_type = a; +} + +/** + * @brief 判断是否是一个正常的CustomizeData + * + * 实际逻辑actionType() != UnknowActionType + * @return 有用的CustomizeData返回true + */ +bool SARibbonCustomizeData::isValid() const +{ + return (actionType() != UnknowActionType); +} + +/** + * @brief 应用SARibbonCustomizeData到SARibbonBar + * @param m + * @return 如果应用失败,返回false,如果actionType==UnknowActionType直接返回false + */ +bool SARibbonCustomizeData::apply(SARibbonBar* bar) const +{ + if (nullptr == bar) { + return (false); + } + switch (actionType()) { + case UnknowActionType: + return (false); + + case AddCategoryActionType: { + // 添加标签 + SARibbonCategory* c = bar->insertCategoryPage(keyValue, indexValue); + if (nullptr == c) { + return (false); + } + c->setObjectName(categoryObjNameValue); + SARibbonCustomizeData::setCanCustomize(c); + return (true); + } + + case AddPannelActionType: { + // 添加pannel + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* p = c->insertPannel(keyValue, indexValue); + p->setObjectName(pannelObjNameValue); + SARibbonCustomizeData::setCanCustomize(p); + return (true); + } + + case AddActionActionType: { + if (nullptr == m_actionsManagerPointer) { + return (false); + } + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + QAction* act = m_actionsManagerPointer->action(keyValue); + if (nullptr == act) { + return (false); + } + SARibbonCustomizeData::setCanCustomize(act); + pannel->addAction(act, actionRowProportionValue); + return (true); + } + + case RemoveCategoryActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + bar->removeCategory(c); + return (true); + } + + case RemovePannelActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + c->removePannel(pannel); + return (true); + } + + case RemoveActionActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + QAction* act = m_actionsManagerPointer->action(keyValue); + if (nullptr == act) { + return (false); + } + pannel->removeAction(act); + return (true); + } + + case ChangeCategoryOrderActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + int currentindex = bar->categoryIndex(c); + if (-1 == currentindex) { + return (false); + } + int toindex = currentindex + indexValue; + bar->moveCategory(currentindex, toindex); + return (true); + } + + case ChangePannelOrderActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + int pannelIndex = c->pannelIndex(pannel); + if (-1 == pannelIndex) { + return (false); + } + c->movePannel(pannelIndex, pannelIndex + indexValue); + return (true); + } + + case ChangeActionOrderActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + QAction* act = m_actionsManagerPointer->action(keyValue); + if (nullptr == act) { + return (false); + } + int actindex = pannel->actionIndex(act); + if (actindex <= -1) { + return (false); + } + pannel->moveAction(actindex, actindex + indexValue); + return (true); + } + + case RenameCategoryActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + c->setCategoryName(keyValue); + return (true); + } + + case RenamePannelActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + SARibbonPannel* pannel = c->pannelByObjectName(pannelObjNameValue); + if (nullptr == pannel) { + return (false); + } + pannel->setPannelName(keyValue); + return (true); + } + + case VisibleCategoryActionType: { + SARibbonCategory* c = bar->categoryByObjectName(categoryObjNameValue); + if (nullptr == c) { + return (false); + } + if (1 == indexValue) { + bar->showCategory(c); + } else { + bar->hideCategory(c); + } + return (true); + } + + default: + break; + } + return (false); +} + +/** + * @brief 获取actionmanager指针 + * @return + */ +SARibbonActionsManager* SARibbonCustomizeData::actionManager() +{ + return (m_actionsManagerPointer); +} + +/** + * @brief 设置ActionsManager + * @param mgr + */ +void SARibbonCustomizeData::setActionsManager(SARibbonActionsManager* mgr) +{ + m_actionsManagerPointer = mgr; +} + +/** + * @brief 创建一个AddCategoryActionType的SARibbonCustomizeData + * @param title category 的标题 + * @param index category要插入的位置 + * @param objName category的object name + * @return 返回AddCategoryActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName) +{ + SARibbonCustomizeData d(AddCategoryActionType); + + d.indexValue = index; + d.keyValue = title; + d.categoryObjNameValue = objName; + return (d); +} + +/** + * @brief 创建一个AddPannelActionType的SARibbonCustomizeData + * @param title pannel的标题 + * @param index pannel的index + * @param categoryobjName pannel的category的objectname + * @param objName pannel的objname + * @return 返回AddPannelActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeAddPannelCustomizeData(const QString& title, + int index, + const QString& categoryobjName, + const QString& objName) +{ + SARibbonCustomizeData d(AddPannelActionType); + + d.indexValue = index; + d.keyValue = title; + d.pannelObjNameValue = objName; + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 添加action + * @param key action的索引 + * @param mgr action管理器 + * @param rp 定义action的占位情况 + * @param categoryObjName action添加到的category的objname + * @param pannelObjName action添加到的category下的pannel的objname + * @param index action添加到的pannel的索引 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeAddActionCustomizeData(const QString& key, + SARibbonActionsManager* mgr, + SARibbonPannelItem::RowProportion rp, + const QString& categoryObjName, + const QString& pannelObjName) +{ + SARibbonCustomizeData d(AddActionActionType, mgr); + + d.keyValue = key; + d.categoryObjNameValue = categoryObjName; + d.pannelObjNameValue = pannelObjName; + d.actionRowProportionValue = rp; + + return (d); +} + +/** + * @brief 创建一个RenameCategoryActionType的SARibbonCustomizeData + * @param newname 新名字 + * @param index category的索引 + * @return 返回RenameCategoryActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRenameCategoryCustomizeData(const QString& newname, const QString& categoryobjName) +{ + SARibbonCustomizeData d(RenameCategoryActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize rename category," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.keyValue = newname; + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 创建一个RenamePannelActionType的SARibbonCustomizeData + * @param newname pannel的名字 + * @param indexValue pannel的索引 + * @param categoryobjName pannel对应的category的object name + * @return 返回RenamePannelActionType的SARibbonCustomizeData + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRenamePannelCustomizeData(const QString& newname, + const QString& categoryobjName, + const QString& pannelObjName) +{ + SARibbonCustomizeData d(RenamePannelActionType); + + if (pannelObjName.isEmpty() || categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize rename pannel," + "but get an empty category/pannel object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.keyValue = newname; + d.pannelObjNameValue = pannelObjName; + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 对应ChangeCategoryOrderActionType + * @param categoryobjName 需要移动的categoryobjName + * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, int moveindex) +{ + SARibbonCustomizeData d(ChangeCategoryOrderActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize change category order," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.indexValue = moveindex; + return (d); +} + +/** + * @brief 对应ChangePannelOrderActionType + * @param categoryobjName 需要移动的pannel对应的categoryobjName + * @param pannelObjName 需要移动的pannelObjName + * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeChangePannelOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + int moveindex) +{ + SARibbonCustomizeData d(ChangePannelOrderActionType); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize change pannel order," + "but get an empty category/pannel object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + d.indexValue = moveindex; + return (d); +} + +/** + * @brief 对应ChangeActionOrderActionType + * @param categoryobjName 需要移动的pannel对应的categoryobjName + * @param pannelObjName 需要移动的pannelObjName + * @param key SARibbonActionsManager管理的key名 + * @param mgr SARibbonActionsManager指针 + * @param moveindex 移动位置,-1代表向上(向左)移动一个位置,1带表向下(向右)移动一个位置 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeChangeActionOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr, + int moveindex) +{ + SARibbonCustomizeData d(ChangeActionOrderActionType, mgr); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize change action order," + "but get an empty category/pannel/action object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + d.keyValue = key; + d.indexValue = moveindex; + return (d); +} + +/** + * @brief 对应RemoveCategoryActionType + * @param categoryobjName 需要移除的objname + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRemoveCategoryCustomizeData(const QString& categoryobjName) +{ + SARibbonCustomizeData d(RemoveCategoryActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize remove category," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + return (d); +} + +/** + * @brief 对应RemovePannelActionType + * @param categoryobjName pannel对应的category的obj name + * @param pannelObjName pannel对应的 obj name + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRemovePannelCustomizeData(const QString& categoryobjName, const QString& pannelObjName) +{ + SARibbonCustomizeData d(RemovePannelActionType); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize remove pannel," + "but get an empty category/pannel object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + return (d); +} + +/** + * @brief 对应RemoveActionActionType + * @param categoryobjName pannel对应的category的obj name + * @param pannelObjName pannel对应的 obj name + * @param key SARibbonActionsManager管理的key名 + * @param mgr SARibbonActionsManager指针 + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeRemoveActionCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr) +{ + SARibbonCustomizeData d(RemoveActionActionType, mgr); + + if (categoryobjName.isEmpty() || pannelObjName.isEmpty() || key.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize remove action," + "but get an empty category/pannel/action object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.pannelObjNameValue = pannelObjName; + d.keyValue = key; + return (d); +} + +/** + * @brief SARibbonCustomizeData::makeVisibleCategoryCustomizeData + * @param categoryobjName + * @param isShow + * @return + */ +SARibbonCustomizeData SARibbonCustomizeData::makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow) +{ + SARibbonCustomizeData d(VisibleCategoryActionType); + + if (categoryobjName.isEmpty()) { + qDebug() << QObject::tr("SARibbon Warning !!! customize visible category," + "but get an empty category object name," + "if you want to customize SARibbon," + "please make sure every element has been set object name."); + } + d.categoryObjNameValue = categoryobjName; + d.indexValue = isShow ? 1 : 0; + return (d); +} + +/** + * @brief 判断外置属性,是否允许自定义 + * @param obj + * @return + */ +bool SARibbonCustomizeData::isCanCustomize(QObject* obj) +{ + QVariant v = obj->property(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE); + + if (v.isValid()) { + return (v.toBool()); + } + return (false); +} + +/** + * @brief 设置外置属性允许自定义 + * @param obj + * @param canbe + */ +void SARibbonCustomizeData::setCanCustomize(QObject* obj, bool canbe) +{ + obj->setProperty(SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE, canbe); +} + +QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex); + +QList< SARibbonCustomizeData > remove_indexs(const QList< SARibbonCustomizeData >& csd, const QList< int >& willremoveIndex) +{ + QList< SARibbonCustomizeData > res; + + for (int i = 0; i < csd.size(); ++i) { + if (!willremoveIndex.contains(i)) { + res << csd[ i ]; + } + } + return (res); +} + +/** + * @brief 对QList进行简化操作 + * + * 此函数会执行如下操作: + * 1、针对同一个category/pannel连续出现的添加和删除操作进行移除(前一步添加,后一步删除) + * + * 2、针对VisibleCategoryActionType,对于连续出现的操作只保留最后一步 + * + * 3、针对RenameCategoryActionType和RenamePannelActionType操作,只保留最后一个 + * + * 4、针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并为一个动作, + * 如果合并后原地不动,则删除 + * + * @param csd + * @return 返回简化的QList + */ +QList< SARibbonCustomizeData > SARibbonCustomizeData::simplify(const QList< SARibbonCustomizeData >& csd) +{ + int size = csd.size(); + + if (size <= 1) { + return (csd); + } + QList< SARibbonCustomizeData > res; + QList< int > willremoveIndex; // 记录要删除的index + + //! 首先针对连续出现的添加和删除操作进行优化 + for (int i = 1; i < size; ++i) { + if ((csd[ i - 1 ].actionType() == AddCategoryActionType) && (csd[ i ].actionType() == RemoveCategoryActionType)) { + if (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue) { + willremoveIndex << i - 1 << i; + } + } else if ((csd[ i - 1 ].actionType() == AddPannelActionType) && (csd[ i ].actionType() == RemovePannelActionType)) { + if ((csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) + && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { + willremoveIndex << i - 1 << i; + } + } else if ((csd[ i - 1 ].actionType() == AddActionActionType) && (csd[ i ].actionType() == RemoveActionActionType)) { + if ((csd[ i - 1 ].keyValue == csd[ i ].keyValue) && (csd[ i - 1 ].pannelObjNameValue == csd[ i ].pannelObjNameValue) + && (csd[ i - 1 ].categoryObjNameValue == csd[ i ].categoryObjNameValue)) { + willremoveIndex << i - 1 << i; + } + } + } + res = remove_indexs(csd, willremoveIndex); + willremoveIndex.clear(); + + //! 筛选VisibleCategoryActionType,对于连续出现的操作只保留最后一步 + size = res.size(); + for (int i = 1; i < size; ++i) { + if ((res[ i - 1 ].actionType() == VisibleCategoryActionType) && (res[ i ].actionType() == VisibleCategoryActionType)) { + if (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue) { + // 要保证操作的是同一个内容 + willremoveIndex << i - 1; // 删除前一个只保留最后一个 + } + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + + //! 针对RenameCategoryActionType和RenamePannelActionType操作,只需保留最后一个 + size = res.size(); + for (int i = 0; i < size; ++i) { + if (res[ i ].actionType() == RenameCategoryActionType) { + // 向后查询,如果查询到有同一个Category改名,把这个索引加入删除队列 + for (int j = i + 1; j < size; ++j) { + if ((res[ j ].actionType() == RenameCategoryActionType) + && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { + willremoveIndex << i; + } + } + } else if (res[ i ].actionType() == RenamePannelActionType) { + // 向后查询,如果查询到有同一个pannel改名,把这个索引加入删除队列 + for (int j = i + 1; j < size; ++j) { + if ((res[ j ].actionType() == RenamePannelActionType) && (res[ i ].pannelObjNameValue == res[ j ].pannelObjNameValue) + && (res[ i ].categoryObjNameValue == res[ j ].categoryObjNameValue)) { + willremoveIndex << i; + } + } + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + + //! 针对连续的ChangeCategoryOrderActionType,ChangePannelOrderActionType,ChangeActionOrderActionType进行合并 + size = res.size(); + for (int i = 1; i < size; ++i) { + if ((res[ i - 1 ].actionType() == ChangeCategoryOrderActionType) && (res[ i ].actionType() == ChangeCategoryOrderActionType) + && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { + // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 + res[ i ].indexValue += res[ i - 1 ].indexValue; + willremoveIndex << i - 1; + } else if ((res[ i - 1 ].actionType() == ChangePannelOrderActionType) && (res[ i ].actionType() == ChangePannelOrderActionType) + && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) + && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { + // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 + res[ i ].indexValue += res[ i - 1 ].indexValue; + willremoveIndex << i - 1; + } else if ((res[ i - 1 ].actionType() == ChangeActionOrderActionType) + && (res[ i ].actionType() == ChangeActionOrderActionType) && (res[ i - 1 ].keyValue == res[ i ].keyValue) + && (res[ i - 1 ].pannelObjNameValue == res[ i ].pannelObjNameValue) + && (res[ i - 1 ].categoryObjNameValue == res[ i ].categoryObjNameValue)) { + // 说明连续两个顺序调整,把前一个indexvalue和后一个indexvalue相加,前一个删除 + res[ i ].indexValue += res[ i - 1 ].indexValue; + willremoveIndex << i - 1; + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + + //! 上一步操作可能会产生indexvalue为0的情况,此操作把indexvalue为0的删除 + size = res.size(); + for (int i = 0; i < size; ++i) { + if ((res[ i ].actionType() == ChangeCategoryOrderActionType) || (res[ i ].actionType() == ChangePannelOrderActionType) + || (res[ i ].actionType() == ChangeActionOrderActionType)) { + if (0 == res[ i ].indexValue) { + willremoveIndex << i; + } + } + } + res = remove_indexs(res, willremoveIndex); + willremoveIndex.clear(); + return (res); +} + +/*** End of inlined file: SARibbonCustomizeData.cpp ***/ + +/*** Start of inlined file: SARibbonCustomizeWidget.cpp ***/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +/// SARibbonCustomizeWidget +//////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds) +{ + if (cds.size() <= 0) { + return (false); + } + + xml->writeStartElement("sa-ribbon-customize"); + for (const SARibbonCustomizeData& d : cds) { + xml->writeStartElement("customize-data"); + xml->writeAttribute("type", QString::number(d.actionType())); + xml->writeAttribute("index", QString::number(d.indexValue)); + xml->writeAttribute("key", d.keyValue); + xml->writeAttribute("category", d.categoryObjNameValue); + xml->writeAttribute("pannel", d.pannelObjNameValue); + xml->writeAttribute("row-prop", QString::number(d.actionRowProportionValue)); + + xml->writeEndElement(); + } + xml->writeEndElement(); + if (xml->hasError()) { + qWarning() << "write has error"; + } + return (true); +} + +QList< SARibbonCustomizeData > sa_customize_datas_from_xml(QXmlStreamReader* xml, SARibbonActionsManager* mgr) +{ + // 先找到"sa-ribbon-customize" + while (!xml->atEnd()) { + + if (xml->isStartElement() && (xml->name().toString() == "sa-ribbon-customize")) { + break; + } + xml->readNext(); + } + QList< SARibbonCustomizeData > res; + + // 开始遍历"customize-data" + while (!xml->atEnd()) { + if (xml->isStartElement() && (xml->name().toString() == "customize-data")) { + // 首先读取属性type + SARibbonCustomizeData d; + QXmlStreamAttributes attrs = xml->attributes(); + if (!attrs.hasAttribute("type")) { + // 说明异常,跳过这个 + xml->readNextStartElement(); + continue; + } + bool isOk = false; + int v = xml->attributes().value("type").toInt(&isOk); + if (!isOk) { + // 说明异常,跳过这个 + xml->readNextStartElement(); + continue; + } + d.setActionType(static_cast< SARibbonCustomizeData::ActionType >(v)); + // 开始读取子对象 + if (attrs.hasAttribute("index")) { + v = xml->attributes().value("index").toInt(&isOk); + if (isOk) { + d.indexValue = v; + } + } + if (attrs.hasAttribute("key")) { + d.keyValue = attrs.value("key").toString(); + } + if (attrs.hasAttribute("category")) { + d.categoryObjNameValue = attrs.value("category").toString(); + } + if (attrs.hasAttribute("pannel")) { + d.pannelObjNameValue = attrs.value("pannel").toString(); + } + if (attrs.hasAttribute("row-prop")) { + v = xml->attributes().value("row-prop").toInt(&isOk); + if (isOk) { + d.actionRowProportionValue = static_cast< SARibbonPannelItem::RowProportion >(v); + } + } + d.setActionsManager(mgr); + res.append(d); + } + xml->readNext(); + } + if (xml->hasError()) { + qWarning() << xml->errorString(); + } + return (res); +} + +int sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) +{ + int c = 0; + + for (const SARibbonCustomizeData& d : cds) { + if (d.apply(bar)) { + ++c; + } + } + return (c); +} + +int sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* bar) +{ + int c = 0; + // todo 支持反向操作 + return (c); +} + +bool sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr) +{ + QFile f(filePath); + + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + return (false); + } + f.seek(0); + QXmlStreamReader xml(&f); + + return (SARibbonCustomizeWidget::fromXml(&xml, bar, mgr)); +} + +/** + * @brief 构建SARibbonCustomizeWidget的Ui + */ +class SARibbonCustomizeWidgetUi +{ +public: + QHBoxLayout* horizontalLayoutMain; + QVBoxLayout* verticalLayoutSelect; + QLabel* labelSelectAction; + QHBoxLayout* horizontalLayoutSearch; + QComboBox* comboBoxActionIndex; + QLineEdit* lineEditSearchAction; + QListView* listViewSelect; + QVBoxLayout* verticalLayoutMidButtons; + QSpacerItem* verticalSpacerUp; + QPushButton* pushButtonAdd; + QPushButton* pushButtonDelete; + QPushButton* pushButtonReset; + QSpacerItem* verticalSpacerDown; + QLabel* labelProportion; + QComboBox* comboBoxActionProportion; + QVBoxLayout* verticalLayoutResult; + QLabel* labelCustomize; + QHBoxLayout* horizontalLayoutCategorySelect; + QRadioButton* radioButtonMainCategory; + QRadioButton* radioButtonAllCategory; + QButtonGroup* radioButtonGroup; + QTreeView* treeViewResult; + QHBoxLayout* horizontalLayoutActionOptBtns; + QPushButton* pushButtonNewCategory; + QPushButton* pushButtonNewPannel; + QPushButton* pushButtonRename; + QVBoxLayout* verticalLayoutRightButtons; + QSpacerItem* verticalSpacerUp2; + QToolButton* toolButtonUp; + QToolButton* toolButtonDown; + QSpacerItem* verticalSpacerDown2; + + void setupUi(QWidget* customizeWidget) + { + if (customizeWidget->objectName().isEmpty()) { + customizeWidget->setObjectName(QStringLiteral("SARibbonCustomizeWidget")); + } + customizeWidget->resize(800, 600); + horizontalLayoutMain = new QHBoxLayout(customizeWidget); + horizontalLayoutMain->setObjectName(QStringLiteral("horizontalLayoutMain")); + verticalLayoutSelect = new QVBoxLayout(); + verticalLayoutSelect->setObjectName(QStringLiteral("verticalLayoutSelect")); + labelSelectAction = new QLabel(customizeWidget); + labelSelectAction->setObjectName(QStringLiteral("labelSelectAction")); + + verticalLayoutSelect->addWidget(labelSelectAction); + + horizontalLayoutSearch = new QHBoxLayout(); + horizontalLayoutSearch->setObjectName(QStringLiteral("horizontalLayoutSearch")); + comboBoxActionIndex = new QComboBox(customizeWidget); + comboBoxActionIndex->setObjectName(QStringLiteral("comboBoxActionIndex")); + comboBoxActionIndex->setEditable(false); + + horizontalLayoutSearch->addWidget(comboBoxActionIndex); + + lineEditSearchAction = new QLineEdit(customizeWidget); + lineEditSearchAction->setObjectName(QStringLiteral("lineEditSearchAction")); + + horizontalLayoutSearch->addWidget(lineEditSearchAction); + + verticalLayoutSelect->addLayout(horizontalLayoutSearch); + + listViewSelect = new QListView(customizeWidget); + listViewSelect->setObjectName(QStringLiteral("listViewSelect")); + + verticalLayoutSelect->addWidget(listViewSelect); + + horizontalLayoutMain->addLayout(verticalLayoutSelect); + + verticalLayoutMidButtons = new QVBoxLayout(); + verticalLayoutMidButtons->setObjectName(QStringLiteral("verticalLayoutMidButtons")); + verticalSpacerUp = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutMidButtons->addItem(verticalSpacerUp); + + pushButtonAdd = new QPushButton(customizeWidget); + pushButtonAdd->setObjectName(QStringLiteral("pushButtonAdd")); + pushButtonAdd->setEnabled(false); + + verticalLayoutMidButtons->addWidget(pushButtonAdd); + + pushButtonDelete = new QPushButton(customizeWidget); + pushButtonDelete->setObjectName(QStringLiteral("pushButtonDelete")); + pushButtonDelete->setEnabled(false); + + verticalLayoutMidButtons->addWidget(pushButtonDelete); + + verticalSpacerDown = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutMidButtons->addItem(verticalSpacerDown); + + labelProportion = new QLabel(customizeWidget); + labelProportion->setObjectName(QStringLiteral("labelProportion")); + verticalLayoutMidButtons->addWidget(labelProportion); + + comboBoxActionProportion = new QComboBox(customizeWidget); + comboBoxActionProportion->setObjectName(QStringLiteral("comboBoxActionProportion")); + comboBoxActionProportion->setEditable(false); + verticalLayoutMidButtons->addWidget(comboBoxActionProportion); + horizontalLayoutMain->addLayout(verticalLayoutMidButtons); + + verticalLayoutResult = new QVBoxLayout(); + verticalLayoutResult->setObjectName(QStringLiteral("verticalLayoutResult")); + labelCustomize = new QLabel(customizeWidget); + labelCustomize->setObjectName(QStringLiteral("labelCustomize")); + + verticalLayoutResult->addWidget(labelCustomize); + + horizontalLayoutCategorySelect = new QHBoxLayout(); + horizontalLayoutCategorySelect->setObjectName(QStringLiteral("horizontalLayoutCategorySelect")); + radioButtonMainCategory = new QRadioButton(customizeWidget); + radioButtonMainCategory->setObjectName(QStringLiteral("radioButtonMainCategory")); + radioButtonMainCategory->setChecked(false); + + horizontalLayoutCategorySelect->addWidget(radioButtonMainCategory); + + radioButtonAllCategory = new QRadioButton(customizeWidget); + radioButtonAllCategory->setObjectName(QStringLiteral("radioButtonAllCategory")); + radioButtonAllCategory->setChecked(true); + + horizontalLayoutCategorySelect->addWidget(radioButtonAllCategory); + + radioButtonGroup = new QButtonGroup(customizeWidget); + radioButtonGroup->addButton(radioButtonMainCategory); + radioButtonGroup->addButton(radioButtonAllCategory); + + verticalLayoutResult->addLayout(horizontalLayoutCategorySelect); + + treeViewResult = new QTreeView(customizeWidget); + treeViewResult->setObjectName(QStringLiteral("treeViewResult")); + treeViewResult->setHeaderHidden(true); + treeViewResult->setSelectionMode(QAbstractItemView::SingleSelection); + treeViewResult->setAnimated(true); // 支持动画 + treeViewResult->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不允许直接在item上重命名 + + verticalLayoutResult->addWidget(treeViewResult); + + horizontalLayoutActionOptBtns = new QHBoxLayout(); + horizontalLayoutActionOptBtns->setObjectName(QStringLiteral("horizontalLayoutActionOptBtns")); + pushButtonNewCategory = new QPushButton(customizeWidget); + pushButtonNewCategory->setObjectName(QStringLiteral("pushButtonNewCategory")); + + horizontalLayoutActionOptBtns->addWidget(pushButtonNewCategory); + + pushButtonNewPannel = new QPushButton(customizeWidget); + pushButtonNewPannel->setObjectName(QStringLiteral("pushButtonNewPannel")); + + horizontalLayoutActionOptBtns->addWidget(pushButtonNewPannel); + + pushButtonRename = new QPushButton(customizeWidget); + pushButtonRename->setObjectName(QStringLiteral("pushButtonRename")); + + horizontalLayoutActionOptBtns->addWidget(pushButtonRename); + + pushButtonReset = new QPushButton(customizeWidget); + pushButtonReset->setObjectName(QStringLiteral("pushButtonReset")); + horizontalLayoutActionOptBtns->addWidget(pushButtonReset); + + verticalLayoutResult->addLayout(horizontalLayoutActionOptBtns); + + horizontalLayoutMain->addLayout(verticalLayoutResult); + + verticalLayoutRightButtons = new QVBoxLayout(); + verticalLayoutRightButtons->setObjectName(QStringLiteral("verticalLayoutRightButtons")); + verticalSpacerUp2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutRightButtons->addItem(verticalSpacerUp2); + + toolButtonUp = new QToolButton(customizeWidget); + toolButtonUp->setObjectName(QStringLiteral("pushButtonUp")); + toolButtonUp->setArrowType(Qt::UpArrow); + toolButtonUp->setAutoRaise(true); + + verticalLayoutRightButtons->addWidget(toolButtonUp); + + toolButtonDown = new QToolButton(customizeWidget); + toolButtonDown->setObjectName(QStringLiteral("pushButtonDown")); + toolButtonDown->setArrowType(Qt::DownArrow); + toolButtonDown->setAutoRaise(true); + + verticalLayoutRightButtons->addWidget(toolButtonDown); + + verticalSpacerDown2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + verticalLayoutRightButtons->addItem(verticalSpacerDown2); + + horizontalLayoutMain->addLayout(verticalLayoutRightButtons); + + retranslateUi(customizeWidget); + } // setupUi + + void retranslateUi(QWidget* customizeWidget) + { + customizeWidget->setWindowTitle(QApplication::translate("SARibbonCustomizeWidget", "Customize Widget", Q_NULLPTR)); + labelSelectAction->setText(QApplication::translate("SARibbonCustomizeWidget", "Please Select", Q_NULLPTR)); // cn:请选择 + lineEditSearchAction->setInputMask(QString()); + lineEditSearchAction->setText(QString()); + lineEditSearchAction->setPlaceholderText(QApplication::translate("SARibbonCustomizeWidget", "Find Command", Q_NULLPTR)); // cn:查找命令 + pushButtonAdd->setText(QApplication::translate("SARibbonCustomizeWidget", "Add >>", Q_NULLPTR)); // cn:添加 >> + pushButtonDelete->setText(QApplication::translate("SARibbonCustomizeWidget", "<< Remove", Q_NULLPTR)); // cn:<< 移除 + labelCustomize->setText(QApplication::translate("SARibbonCustomizeWidget", "Customize the Ribbon", Q_NULLPTR)); // cn:自定义功能区 + radioButtonMainCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "Main Category", Q_NULLPTR)); // cn:主选项卡 + radioButtonAllCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "All Category", Q_NULLPTR)); // cn:所有选项卡 + pushButtonNewCategory->setText(QApplication::translate("SARibbonCustomizeWidget", "New Category", Q_NULLPTR)); // cn:新建选项卡 + pushButtonNewPannel->setText(QApplication::translate("SARibbonCustomizeWidget", "New Group", Q_NULLPTR)); // cn:新建组 + pushButtonRename->setText(QApplication::translate("SARibbonCustomizeWidget", "Rename", Q_NULLPTR)); // cn:重命名 + pushButtonReset->setText(QApplication::translate("SARibbonCustomizeWidget", "reset", Q_NULLPTR)); // cn:重置 + labelProportion->setText(QApplication::translate("SARibbonCustomizeWidget", "proportion:", Q_NULLPTR)); // cn:比例 + } // retranslateUi +}; + +/** + * @brief 管理SARibbonCustomizeWidget的业务逻辑 + */ +class SARibbonCustomizeWidget::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonCustomizeWidget) +public: + SARibbonCustomizeWidget::RibbonTreeShowType mShowType { SARibbonCustomizeWidget::ShowAllCategory }; ///< 显示类型 + SARibbonBar* mRibbonBar { nullptr }; ///< 保存SARibbonMainWindow的指针 + SARibbonActionsManager* mActionMgr { nullptr }; ///< action管理器 + SARibbonActionsManagerModel* mAcionModel { nullptr }; ///< action管理器对应的model + QStandardItemModel* mRibbonModel { nullptr }; ///< 用于很成ribbon的树 + int mCustomizeCategoryCount { 0 }; ///< 记录自定义Category的个数 + int mCustomizePannelCount { 0 }; ///< 记录自定义Pannel的个数 +public: + PrivateData(SARibbonCustomizeWidget* p); + void updateModel(); + + QList< SARibbonCustomizeData > mCustomizeDatasCache; ///< 缓存记录所有的自定义动作 + QList< SARibbonCustomizeData > mCustomizeDatasApplied; ///< 应用后的所有的自定义动作 + QList< SARibbonCustomizeData > mOldCustomizeDatas; ///< 记录旧的自定义动作,本地文件缓存 + // 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch_suf + static QString makeRandomObjName(const QString& pre); + + int itemLevel(QStandardItem* item) const; + + // + bool isCustomizeItem(QStandardItem* item) const; + + // 把item转换为category + SARibbonCategory* itemToCategory(QStandardItem* item) const; + + // 把item转换为SARibbonPannel + SARibbonPannel* itemToPannel(QStandardItem* item) const; + + // 获取item对应的object name + QString itemObjectName(QStandardItem* item) const; + + // 判断是否可以自定义 + bool isItemCanCustomize(QStandardItem* item) const; + + // 从item转为action + QAction* itemToAction(QStandardItem* item) const; +}; + +SARibbonCustomizeWidget::PrivateData::PrivateData(SARibbonCustomizeWidget* p) + : q_ptr(p), mAcionModel(new SARibbonActionsManagerModel(p)), mRibbonModel(new QStandardItemModel(p)) +{ +} + +void SARibbonCustomizeWidget::PrivateData::updateModel() +{ + if (mRibbonBar == nullptr) { + return; + } + mRibbonModel->clear(); + SARibbonBar* ribbonbar = mRibbonBar; + QList< SARibbonCategory* > categorys = ribbonbar->categoryPages(); + + for (const SARibbonCategory* c : qAsConst(categorys)) { + if ((mShowType == SARibbonCustomizeWidget::ShowMainCategory) && c->isContextCategory()) { + // 如果是只显示主内容,如果是上下文标签就忽略 + continue; + } + QStandardItem* ci = new QStandardItem(); + if (c->isContextCategory()) { + ci->setText(QString("[%1]").arg(c->categoryName())); + } else { + ci->setText(c->categoryName()); + } + if (c->isCanCustomize() && !c->isContextCategory()) { + // 上下文标签不做显示隐藏处理 + ci->setCheckable(true); + ci->setCheckState(ribbonbar->isCategoryVisible(c) ? Qt::Checked : Qt::Unchecked); + ci->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 + } + ci->setData(0, SARibbonCustomizeWidget::LevelRole); + ci->setData(QVariant::fromValue< qintptr >(qintptr(c)), SARibbonCustomizeWidget::PointerRole); + QList< SARibbonPannel* > pannels = c->pannelList(); + for (const SARibbonPannel* p : qAsConst(pannels)) { + QStandardItem* pi = new QStandardItem(p->pannelName()); + pi->setData(1, SARibbonCustomizeWidget::LevelRole); + pi->setData(QVariant::fromValue< qintptr >(qintptr(p)), SARibbonCustomizeWidget::PointerRole); + if (p->isCanCustomize()) { + pi->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 + } + ci->appendRow(pi); + const QList< SARibbonPannelItem* >& items = p->ribbonPannelItem(); + for (SARibbonPannelItem* i : qAsConst(items)) { + if (i->action->isSeparator()) { + continue; + } + QStandardItem* ii = new QStandardItem(); + // if (i->customWidget) { + // //如果是自定义窗口 + // if (i->widget()->windowTitle().isEmpty() && i->widget()->windowIcon().isNull()) { + // delete ii; + // continue; //如果窗口啥也没有,就跳过 + // } + // ii->setText(i->widget()->windowTitle()); + // ii->setIcon(i->widget()->windowIcon()); + // if (SARibbonCustomizeData::isCanCustomize(i->widget())) { + // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 + // } + // } else { + // //不是自定义,说明是action + // ii->setText(i->action->text()); + // ii->setIcon(i->action->icon()); + // if (SARibbonCustomizeData::isCanCustomize(i->action)) { + // ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); //标记这个是可以自定义的 + // } + // } + ii->setText(i->action->text()); + ii->setIcon(i->action->icon()); + if (SARibbonCustomizeData::isCanCustomize(i->action)) { + ii->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 标记这个是可以自定义的 + } + ii->setData(2, SARibbonCustomizeWidget::LevelRole); + ii->setData(QVariant::fromValue< qintptr >(qintptr(i)), SARibbonCustomizeWidget::PointerRole); + pi->appendRow(ii); + } + } + mRibbonModel->appendRow(ci); + } +} + +/** + * @brief 创建一个随机id,形如:pre_QDateTime::currentMSecsSinceEpoch + * @param pre 前缀 + * @return + */ +QString SARibbonCustomizeWidget::PrivateData::makeRandomObjName(const QString& pre) +{ + return (QString("%1_%2").arg(pre).arg(QDateTime::currentMSecsSinceEpoch())); +} + +/** + * @brief 获取item的level + * @param item + * @return + */ +int SARibbonCustomizeWidget::PrivateData::itemLevel(QStandardItem* item) const +{ + return (item->data(SARibbonCustomizeWidget::LevelRole).toInt()); +} + +/** + * @brief 判断itemn为自定义的item,自定义的item都带有CustomizeRole角色 + * @param item + * @return + */ +bool SARibbonCustomizeWidget::PrivateData::isCustomizeItem(QStandardItem* item) const +{ + if (nullptr == item) { + return (false); + } + return (item->data(SARibbonCustomizeWidget::CustomizeRole).isValid()); +} + +/** + * @brief 把item转换为category + * @param item + * @return无法转换返回nullptr + */ +SARibbonCategory* SARibbonCustomizeWidget::PrivateData::itemToCategory(QStandardItem* item) const +{ + int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); + + if (level != 0) { + return (nullptr); + } + qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); + + return (reinterpret_cast< SARibbonCategory* >(p)); +} + +/** + * @brief 把item转换为pannel + * @param item + * @return 无法转换返回nullptr + */ +SARibbonPannel* SARibbonCustomizeWidget::PrivateData::itemToPannel(QStandardItem* item) const +{ + int level = item->data(SARibbonCustomizeWidget::LevelRole).toInt(); + + if (level != 1) { + return (nullptr); + } + qintptr p = item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >(); + + return (reinterpret_cast< SARibbonPannel* >(p)); +} + +/** + * @brief 获取item对应的objectname + * @param item + * @return 如果无法获取,返回一个空的QString + */ +QString SARibbonCustomizeWidget::PrivateData::itemObjectName(QStandardItem* item) const +{ + QString objName; + + if (isCustomizeItem(item)) { + // 说明是自定义的 + objName = item->data(SARibbonCustomizeWidget::CustomizeObjNameRole).toString(); + } else { + // 说明这个是非自定义的 + int level = itemLevel(item); + if (0 == level) { + SARibbonCategory* category = itemToCategory(item); + if (category) { + objName = category->objectName(); + } + } else if (1 == level) { + SARibbonPannel* pannel = itemToPannel(item); + if (pannel) { + objName = pannel->objectName(); + } + } + } + return (objName); +} + +/** + * @brief 判断item是否可自定义 + * @param item + * @return + */ +bool SARibbonCustomizeWidget::PrivateData::isItemCanCustomize(QStandardItem* item) const +{ + if (nullptr == item) { + return (false); + } + QVariant v = item->data(SARibbonCustomizeWidget::CanCustomizeRole); + + if (v.isValid()) { + return (v.toBool()); + } + return (false); +} + +/** + * @brief 从item 转为action + * @param item + * @return + */ +QAction* SARibbonCustomizeWidget::PrivateData::itemToAction(QStandardItem* item) const +{ + if (2 != itemLevel(item)) { + return (nullptr); + } + // 这里要非常注意,SARibbonCustomizeWidget::CustomizeRole为true时,说明这个是自定义的内容, + // 这时PointerRole里存放的是action指针,不是SARibbonPannelItem + QAction* act = nullptr; + + if (item->data(SARibbonCustomizeWidget::CustomizeRole).toBool()) { + act = reinterpret_cast< QAction* >(item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); + } else { + SARibbonPannelItem* pi = reinterpret_cast< SARibbonPannelItem* >( + item->data(SARibbonCustomizeWidget::PointerRole).value< qintptr >()); + act = (pi->action); + } + return (act); +} + +//=================================================== +// SARibbonCustomizeWidget +//=================================================== +/** + * @brief SARibbonCustomizeWidget::SARibbonCustomizeWidget + * @param ribbonWindow 传入需要管理的SARibbonMainWindow指针 + * @param parent 用于界面生成的parent,可以和SARibbonMainWindow一样 + * @param f 同QWidget::QWidget的第二个参数 + */ +SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) +{ + init(ribbonWindow->ribbonBar()); +} + +SARibbonCustomizeWidget::SARibbonCustomizeWidget(SARibbonBar* ribbonbar, QWidget* parent, Qt::WindowFlags f) + : QWidget(parent, f), d_ptr(new SARibbonCustomizeWidget::PrivateData(this)), ui(new SARibbonCustomizeWidgetUi) +{ + init(ribbonbar); +} + +void SARibbonCustomizeWidget::init(SARibbonBar* ribbonbar) +{ + d_ptr->mRibbonBar = ribbonbar; + ui->setupUi(this); + ui->listViewSelect->setModel(d_ptr->mAcionModel); + ui->treeViewResult->setModel(d_ptr->mRibbonModel); + initConnection(); + updateModel(); +} + +SARibbonCustomizeWidget::~SARibbonCustomizeWidget() +{ + delete ui; +} + +void SARibbonCustomizeWidget::initConnection() +{ + // 这个需要qt5.8以上支持 + // connect(ui->comboBoxActionIndex, QOverload::of(&QComboBox::currentIndexChanged) + // , this, &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); + connect(ui->comboBoxActionIndex, + static_cast< void (QComboBox::*)(int) >(&QComboBox::currentIndexChanged), + this, + &SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged); + // 这个需要qt5.8以上支持 + // connect(ui->radioButtonGroup, QOverload::of(&QButtonGroup::buttonClicked) + // , this, &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); + connect(ui->radioButtonGroup, + static_cast< void (QButtonGroup::*)(QAbstractButton*) >(&QButtonGroup::buttonClicked), + this, + &SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked); + connect(ui->pushButtonNewCategory, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewCategoryClicked); + connect(ui->pushButtonNewPannel, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonNewPannelClicked); + connect(ui->pushButtonRename, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonRenameClicked); + connect(ui->pushButtonAdd, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonAddClicked); + connect(ui->pushButtonDelete, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonDeleteClicked); + connect(ui->listViewSelect, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onListViewSelectClicked); + connect(ui->treeViewResult, &QAbstractItemView::clicked, this, &SARibbonCustomizeWidget::onTreeViewResultClicked); + connect(ui->toolButtonUp, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonUpClicked); + connect(ui->toolButtonDown, &QToolButton::clicked, this, &SARibbonCustomizeWidget::onToolButtonDownClicked); + connect(d_ptr->mRibbonModel, &QStandardItemModel::itemChanged, this, &SARibbonCustomizeWidget::onItemChanged); + connect(ui->lineEditSearchAction, &QLineEdit::textEdited, this, &SARibbonCustomizeWidget::onLineEditSearchActionTextEdited); + connect(ui->pushButtonReset, &QPushButton::clicked, this, &SARibbonCustomizeWidget::onPushButtonResetClicked); +} + +/** + * @brief 设置action管理器 + * @param mgr + */ +void SARibbonCustomizeWidget::setupActionsManager(SARibbonActionsManager* mgr) +{ + d_ptr->mActionMgr = mgr; + if (d_ptr->mActionMgr) { + d_ptr->mAcionModel->uninstallActionsManager(); + } + d_ptr->mAcionModel->setupActionsManager(mgr); + // 更新左边复选框 + QList< int > tags = mgr->actionTags(); + + ui->comboBoxActionIndex->clear(); + for (int tag : qAsConst(tags)) { + if (mgr->tagName(tag).isEmpty()) + continue; + ui->comboBoxActionIndex->addItem(mgr->tagName(tag), tag); + } +} + +/** + * @brief //判断用户是否有要存储的内容,对应save动作 + * @return + */ +bool SARibbonCustomizeWidget::isApplied() const +{ + return (d_ptr->mCustomizeDatasApplied.size() > 0); +} + +/** + * @brief 判断用户是否有改动内容,对应apply动作 + * @return + */ +bool SARibbonCustomizeWidget::isCached() const +{ + return (d_ptr->mCustomizeDatasCache.size() > 0); +} + +/** + * @brief 获取model + * @return + */ +const QStandardItemModel* SARibbonCustomizeWidget::model() const +{ + return (d_ptr->mRibbonModel); +} + +/** + * @brief 根据当前的radiobutton选项来更新model + */ +void SARibbonCustomizeWidget::updateModel() +{ + updateModel(ui->radioButtonAllCategory->isChecked() ? ShowAllCategory : ShowMainCategory); + if (d_ptr->mRibbonBar) { + SARibbonBar* bar = d_ptr->mRibbonBar; + if (bar) { + ui->comboBoxActionProportion->clear(); + if (bar->isTwoRowStyle()) { + ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); + ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); + } else { + ui->comboBoxActionProportion->addItem(tr("large"), SARibbonPannelItem::Large); + ui->comboBoxActionProportion->addItem(tr("medium"), SARibbonPannelItem::Medium); + ui->comboBoxActionProportion->addItem(tr("small"), SARibbonPannelItem::Small); + } + } + } +} + +/** + * @brief 更新model + */ +void SARibbonCustomizeWidget::updateModel(RibbonTreeShowType type) +{ + d_ptr->mShowType = type; + d_ptr->updateModel(); +} + +/** + * @brief 应用所有设定 + * @return 应用成功返回true + * @note 所有设定有一个应用成功都会返回true + */ +bool SARibbonCustomizeWidget::applys() +{ + simplify(); + if (sa_customize_datas_apply(d_ptr->mCustomizeDatasCache, d_ptr->mRibbonBar) > 0) { + // 将临时操作存入已应用操作,并清空临时操作 + makeActionsApplied(); + clearCache(); + return true; + } else { + return false; + } +} + +/** + * @brief 转换为xml + * + * 此函数仅会写element,不会写document相关内容,因此如果需要写document, + * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() + * + * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); + * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, + * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: + * @code + * SARibbonCustomizeDialog dlg(this);//this为SARibbonMainWindow的窗口 + * dlg.setupActionsManager(m_actMgr); + * if (SARibbonCustomizeDialog::Accepted == dlg.exec()) { + * dlg.applys(); + * QByteArray str; + * QXmlStreamWriter xml(&str);//QXmlStreamWriter不建议通过QString构造,遇到中文会异常 + * xml.setAutoFormatting(true); + * xml.setAutoFormattingIndent(2); + * xml.setCodec("utf-8");//在writeStartDocument之前指定编码 + * xml.writeStartDocument(); + * bool isok = dlg.toXml(&xml); + * xml.writeEndDocument(); + * if (isok) { + * QFile f("customize.xml"); + * if (f.open(QIODevice::ReadWrite|QIODevice::Text|QIODevice::Truncate)) { + * QTextStream s(&f); + * s.setCodec("utf-8");//指定编码输出 + * s << str; + * s.flush(); + * } + * m_edit->append("write xml:");//m_edit的定义为:QTextEdit *m_edit; + * m_edit->append(str); + * } + * } + * @endcode + * @return 如果出现异常,返回false,如果没有自定义数据也会返回false + * @see sa_customize_datas_to_xml + */ +bool SARibbonCustomizeWidget::toXml(QXmlStreamWriter* xml) const +{ + QList< SARibbonCustomizeData > res = d_ptr->mOldCustomizeDatas; + + if (isApplied()) + res << d_ptr->mCustomizeDatasApplied; + if (isCached()) + res << d_ptr->mCustomizeDatasCache; + + res = SARibbonCustomizeData::simplify(res); + return (sa_customize_datas_to_xml(xml, res)); +} + +/** + * @brief 把配置写入文件中 + * @param xmlpath + * @return + */ +bool SARibbonCustomizeWidget::toXml(const QString& xmlpath) const +{ + QFile f(xmlpath); + + if (!f.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { + return (false); + } + QXmlStreamWriter xml(&f); + + xml.setAutoFormatting(true); + xml.setAutoFormattingIndent(2); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // QXmlStreamWriter always encodes XML in UTF-8. + xml.setCodec("utf-8"); // 在writeStartDocument之前指定编码 +#endif + xml.writeStartDocument(); + bool isOK = toXml(&xml); + + xml.writeEndDocument(); + f.close(); + return (isOK); +} + +/** + * @brief 从xml中加载QList + * + * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + * @param xml + * @return + * @note 此函数要在@ref setupActionsManager 函数之后调用 + */ +void SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml) +{ + QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, d_ptr->mActionMgr); + + d_ptr->mOldCustomizeDatas = cds; +} + +/** + * @brief 从xml中加载QList + * + * 对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + * @param xmlpath + * @note 此函数要在@ref setupActionsManager 函数之后调用 + * @note 如果程序启动后加载了自定义配置,再调用此窗口时需要调用此函数,把原来的配置加载进来, + * 在生成新动作时会把旧动作保存,但在调用applys时不会调用此加载的动作 + */ +void SARibbonCustomizeWidget::fromXml(const QString& xmlpath) +{ + QFile f(xmlpath); + + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } + f.seek(0); + QXmlStreamReader xml(&f); + + fromXml(&xml); +} + +/** + * @brief 应用xml配置 + * + * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: + * @code + * //只能调用一次 + * static bool has_call = false; + * if (!has_call) { + * QFile f("customize.xml"); + * if (!f.open(QIODevice::ReadWrite|QIODevice::Text)) { + * return; + * } + * f.seek(0); + * QXmlStreamReader xml(&f); + * has_call = SARibbonCustomizeWidget::fromXml(&xml, this, m_actMgr); + * } + * @endcode + * @param xml + * @param bar SARibbonBar + * @return 所有设定有一个应用成功都会返回true + * @see sa_customize_datas_from_xml sa_customize_datas_apply sa_apply_customize_from_xml_file + */ +bool SARibbonCustomizeWidget::fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr) +{ + // 先找到sa-ribbon-customize标签 + QList< SARibbonCustomizeData > cds = sa_customize_datas_from_xml(xml, mgr); + + return (sa_customize_datas_apply(cds, bar) > 0); +} + +/** + * @brief 清除已应用的动作 + */ +void SARibbonCustomizeWidget::makeActionsApplied() +{ + d_ptr->mCustomizeDatasApplied << d_ptr->mCustomizeDatasCache; +} + +/** + * @brief 清除applied的动作,cancel操作后需要清空已应用的动作 + */ +void SARibbonCustomizeWidget::clearApplied() +{ + d_ptr->mCustomizeDatasApplied.clear(); +} + +/** + * @brief 清除缓存动作 + * + * 在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 + */ +void SARibbonCustomizeWidget::clearCache() +{ + d_ptr->mCustomizeDatasCache.clear(); +} + +/** + * @brief 清除所有动作,不包含本地读取的数据 + */ +void SARibbonCustomizeWidget::clear() +{ + clearApplied(); + clearCache(); +} + +/** + * @brief 精简 + */ +void SARibbonCustomizeWidget::simplify() +{ + d_ptr->mCustomizeDatasCache = SARibbonCustomizeData::simplify(d_ptr->mCustomizeDatasCache); +} + +/** + * @brief 获取当前界面选中的行属性 + * @return + */ +SARibbonPannelItem::RowProportion SARibbonCustomizeWidget::selectedRowProportion() const +{ + return (static_cast< SARibbonPannelItem::RowProportion >(ui->comboBoxActionProportion->currentData().toInt())); +} + +/** + * @brief 获取listview中选中的action + * @return 如果没有选中action,返回nullptr + * @note 如果要获取treeview选中的action,使用@ref itemToAction 函数 + */ +QAction* SARibbonCustomizeWidget::selectedAction() const +{ + QItemSelectionModel* m = ui->listViewSelect->selectionModel(); + + if ((nullptr == m) || !m->hasSelection()) { + return (nullptr); + } + QModelIndex i = m->currentIndex(); + + return (d_ptr->mAcionModel->indexToAction(i)); +} + +/** + * @brief 把item转换为action + * @param item + * @return 如果没有action可转换,返回nullptr + */ +QAction* SARibbonCustomizeWidget::itemToAction(QStandardItem* item) const +{ + return (d_ptr->itemToAction(item)); +} + +/** + * @brief 获取ribbon tree选中的item + * @return + */ +QStandardItem* SARibbonCustomizeWidget::selectedItem() const +{ + QItemSelectionModel* m = ui->treeViewResult->selectionModel(); + + if ((nullptr == m) || !m->hasSelection()) { + return (nullptr); + } + QModelIndex i = m->currentIndex(); + + return (d_ptr->mRibbonModel->itemFromIndex(i)); +} + +/** + * @brief 获取选中的ribbon tree 的level + * @return -1为选中异常,0代表选中了category 1代表选中了pannel 2代表选中了action + */ +int SARibbonCustomizeWidget::selectedRibbonLevel() const +{ + QStandardItem* item = selectedItem(); + + if (item) { + return (itemLevel(item)); + } + return (-1); +} + +/** + * @brief 获取StandardItem 的level + * @param item + * @return + */ +int SARibbonCustomizeWidget::itemLevel(QStandardItem* item) const +{ + return (d_ptr->itemLevel(item)); +} + +/** + * @brief 设置某个item被选中 + * @param item + */ +void SARibbonCustomizeWidget::setSelectItem(QStandardItem* item, bool ensureVisible) +{ + QItemSelectionModel* m = ui->treeViewResult->selectionModel(); + + if (nullptr == m) { + return; + } + if (m) { + m->clearSelection(); + m->setCurrentIndex(item->index(), QItemSelectionModel::SelectCurrent); + } + if (ensureVisible) { + ui->treeViewResult->scrollTo(item->index()); + } +} + +/** + * @brief 判断itemn能否改动,可以改动返回true + * @param item + * @return + */ +bool SARibbonCustomizeWidget::isItemCanCustomize(QStandardItem* item) const +{ + return (d_ptr->isItemCanCustomize(item)); +} + +bool SARibbonCustomizeWidget::isSelectedItemCanCustomize() const +{ + return (isItemCanCustomize(selectedItem())); +} + +/** + * @brief 判断itemn能否改动,可以改动返回true + * @param item + * @return + */ +bool SARibbonCustomizeWidget::isCustomizeItem(QStandardItem* item) const +{ + return (d_ptr->isCustomizeItem(item)); +} + +bool SARibbonCustomizeWidget::isSelectedItemIsCustomize() const +{ + return (isCustomizeItem(selectedItem())); +} + +void SARibbonCustomizeWidget::removeItem(QStandardItem* item) +{ + if (item->parent()) { + item->parent()->removeRow(item->row()); + } else { + d_ptr->mRibbonModel->removeRow(item->row()); + } +} + +void SARibbonCustomizeWidget::onComboBoxActionIndexCurrentIndexChanged(int index) +{ + int tag = ui->comboBoxActionIndex->itemData(index).toInt(); + + d_ptr->mAcionModel->setFilter(tag); +} + +void SARibbonCustomizeWidget::onRadioButtonGroupButtonClicked(QAbstractButton* b) +{ + updateModel(b == ui->radioButtonAllCategory ? ShowAllCategory : ShowMainCategory); +} + +void SARibbonCustomizeWidget::onPushButtonNewCategoryClicked() +{ + int row = d_ptr->mRibbonModel->rowCount(); + QItemSelectionModel* m = ui->treeViewResult->selectionModel(); + + if (m && m->hasSelection()) { + QModelIndex i = m->currentIndex(); + while (i.parent().isValid()) { + i = i.parent(); + } + // 获取选中的最顶层item + row = i.row() + 1; + } + QStandardItem* ni = new QStandardItem(tr("new category[customize]%1").arg(++(d_ptr->mCustomizeCategoryCount))); + + ni->setData(0, SARibbonCustomizeWidget::LevelRole); + d_ptr->mRibbonModel->insertRow(row, ni); + // 设置新增的为选中 + setSelectItem(ni); + // 把动作插入动作列表中 + SARibbonCustomizeData d = SARibbonCustomizeData::makeAddCategoryCustomizeData(ni->text(), + ni->row(), + SARibbonCustomizeWidget::PrivateData::makeRandomObjName( + "category")); + + d_ptr->mCustomizeDatasCache.append(d); + ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole + ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); + ni->setData(d.categoryObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); +} + +void SARibbonCustomizeWidget::onPushButtonNewPannelClicked() +{ + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + int level = selectedRibbonLevel(); + + QStandardItem* ni = new QStandardItem(tr("new pannel[customize]%1").arg(++(d_ptr->mCustomizePannelCount))); + + ni->setData(1, SARibbonCustomizeWidget::LevelRole); + + if (0 == level) { + // 说明是category,插入到最后 + item->appendRow(ni); + } else if (1 == level) { + // 说明选择的是pannel,插入到此pannel之后 + QStandardItem* categoryItem = item->parent(); + if (nullptr == categoryItem) { + return; + } + categoryItem->insertRow(item->row() + 1, ni); + } else { + // 不符合就删除退出 + delete ni; + ni = nullptr; + return; + } + // 查找category的object name + QStandardItem* categoryItem = ni->parent(); + QString categoryObjName = ""; + + categoryObjName = d_ptr->itemObjectName(categoryItem); + SARibbonCustomizeData d = SARibbonCustomizeData::makeAddPannelCustomizeData(ni->text(), + ni->row(), + categoryObjName, + SARibbonCustomizeWidget::PrivateData::makeRandomObjName( + "pannel")); + + d_ptr->mCustomizeDatasCache.append(d); + ni->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole + ni->setData(true, SARibbonCustomizeWidget::CustomizeRole); + ni->setData(d.pannelObjNameValue, SARibbonCustomizeWidget::CustomizeObjNameRole); + setSelectItem(ni); +} + +void SARibbonCustomizeWidget::onPushButtonRenameClicked() +{ + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + bool ok; + QString text = ""; + + text = QInputDialog::getText(this, tr("rename"), tr("name:"), QLineEdit::Normal, item->text(), &ok); + + if (!ok || text.isEmpty()) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 改Category名 + QString cateObjName = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeRenameCategoryCustomizeData(text, cateObjName); + d_ptr->mCustomizeDatasCache.append(d); + } else if (1 == level) { + QString cateObjName = d_ptr->itemObjectName(item->parent()); + QString pannelObjName = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeRenamePannelCustomizeData(text, cateObjName, pannelObjName); + d_ptr->mCustomizeDatasCache.append(d); + } else { + // action 不允许改名 + return; + } + item->setText(text); +} + +void SARibbonCustomizeWidget::onPushButtonAddClicked() +{ + QAction* act = selectedAction(); + QStandardItem* item = selectedItem(); + + if ((nullptr == act) || (nullptr == item)) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 选中category不进行操作 + return; + } else if (2 == level) { + // 选中action,添加到这个action之后,把item设置为pannel + item = item->parent(); + } + QString pannelObjName = d_ptr->itemObjectName(item); + QString categoryObjName = d_ptr->itemObjectName(item->parent()); + QString key = d_ptr->mActionMgr->key(act); + + SARibbonCustomizeData d = SARibbonCustomizeData::makeAddActionCustomizeData(key, + d_ptr->mActionMgr, + selectedRowProportion(), + categoryObjName, + pannelObjName); + + d_ptr->mCustomizeDatasCache.append(d); + + QStandardItem* actItem = new QStandardItem(act->icon(), act->text()); + + actItem->setData(2, SARibbonCustomizeWidget::LevelRole); + actItem->setData(true, SARibbonCustomizeWidget::CanCustomizeRole); // 有CustomizeRole,必有CanCustomizeRole + actItem->setData(true, SARibbonCustomizeWidget::CustomizeRole); + actItem->setData(act->objectName(), SARibbonCustomizeWidget::CustomizeObjNameRole); + actItem->setData(qintptr(act), SARibbonCustomizeWidget::PointerRole); // 把action指针传入 + item->appendRow(actItem); +} + +void SARibbonCustomizeWidget::onPushButtonDeleteClicked() +{ + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + if (!isItemCanCustomize(item)) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 删除category + SARibbonCustomizeData d = SARibbonCustomizeData::makeRemoveCategoryCustomizeData(d_ptr->itemObjectName(item)); + d_ptr->mCustomizeDatasCache.append(d); + } else if (1 == level) { + // 删除pannel + QString catObjName = d_ptr->itemObjectName(item->parent()); + QString pannelObjName = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeRemovePannelCustomizeData(catObjName, pannelObjName); + d_ptr->mCustomizeDatasCache.append(d); + } else if (2 == level) { + // 删除Action + QString catObjName = d_ptr->itemObjectName(item->parent()->parent()); + QString pannelObjName = d_ptr->itemObjectName(item->parent()); + QAction* act = itemToAction(item); + QString key = d_ptr->mActionMgr->key(act); + if (key.isEmpty() || catObjName.isEmpty() || pannelObjName.isEmpty()) { + return; + } + + SARibbonCustomizeData d = SARibbonCustomizeData::makeRemoveActionCustomizeData(catObjName, pannelObjName, key, d_ptr->mActionMgr); + d_ptr->mCustomizeDatasCache.append(d); + } + // 执行删除操作 + removeItem(item); + // 删除后重新识别 + ui->pushButtonAdd->setEnabled(selectedAction() && isSelectedItemIsCustomize() && selectedRibbonLevel() > 0); + ui->pushButtonDelete->setEnabled(isSelectedItemIsCustomize()); +} + +void SARibbonCustomizeWidget::onListViewSelectClicked(const QModelIndex& index) +{ + // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 + // 点击了listview,判断treeview的状态 + Q_UNUSED(index); + ui->pushButtonAdd->setEnabled(isSelectedItemCanCustomize() && selectedRibbonLevel() > 0); + ui->pushButtonDelete->setEnabled(isSelectedItemCanCustomize()); +} + +void SARibbonCustomizeWidget::onTreeViewResultClicked(const QModelIndex& index) +{ + Q_UNUSED(index); + // 每次点击,判断是否可以进行操作,决定pushButtonAdd和pushButtonDelete的显示状态 + QStandardItem* item = selectedItem(); + + if (nullptr == item) { + return; + } + int level = itemLevel(item); + + ui->pushButtonAdd->setEnabled(selectedAction() && (level > 0) && isItemCanCustomize(item)); + ui->pushButtonDelete->setEnabled(isItemCanCustomize(item)); // 有CustomizeRole,必有CanCustomizeRole + ui->pushButtonRename->setEnabled(level != 2 || isItemCanCustomize(item)); // QAction 不能改名 , 有CustomizeRole,必有CanCustomizeRole +} + +void SARibbonCustomizeWidget::onToolButtonUpClicked() +{ + QStandardItem* item = selectedItem(); + + if ((nullptr == item) || (0 == item->row())) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 移动category + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = d_ptr->mRibbonModel->takeItem(r); + d_ptr->mRibbonModel->removeRow(r); + d_ptr->mRibbonModel->insertRow(r - 1, item); + } else if (1 == level) { + QStandardItem* paritem = item->parent(); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData(d_ptr->itemObjectName(paritem), + d_ptr->itemObjectName(item), + -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = paritem->takeChild(r); + paritem->removeRow(r); + paritem->insertRow(r - 1, item); + } else if (2 == level) { + QStandardItem* pannelItem = item->parent(); + QStandardItem* categoryItem = pannelItem->parent(); + QAction* act = itemToAction(item); + if (!act) { + return; + } + QString key = d_ptr->mActionMgr->key(act); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData(d_ptr->itemObjectName(categoryItem), + d_ptr->itemObjectName(pannelItem), + key, + d_ptr->mActionMgr, + -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = pannelItem->takeChild(r); + pannelItem->removeRow(r); + pannelItem->insertRow(r - 1, item); + } + + // 保持焦点,方便连续操作 + setSelectItem(item); + onTreeViewResultClicked(item->index()); +} + +void SARibbonCustomizeWidget::onToolButtonDownClicked() +{ + QStandardItem* item = selectedItem(); + + if (item == nullptr) { + return; + } + int count = 0; + + if (item->parent()) { + count = item->parent()->rowCount(); + } else { + count = d_ptr->mRibbonModel->rowCount(); + } + if ((nullptr == item) || ((count - 1) == item->row())) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + // 移动category + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeCategoryOrderCustomizeData(d_ptr->itemObjectName(item), 1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = d_ptr->mRibbonModel->takeItem(item->row()); + d_ptr->mRibbonModel->removeRow(r); + d_ptr->mRibbonModel->insertRow(r + 1, item); + } else if (1 == level) { + QStandardItem* paritem = item->parent(); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangePannelOrderCustomizeData(d_ptr->itemObjectName(paritem), + d_ptr->itemObjectName(item), + 1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = paritem->takeChild(r); + paritem->removeRow(r); + paritem->insertRow(r + 1, item); + } else if (2 == level) { + QStandardItem* pannelItem = item->parent(); + QStandardItem* categoryItem = pannelItem->parent(); + QAction* act = itemToAction(item); + if (!act) { + return; + } + QString key = d_ptr->mActionMgr->key(act); + SARibbonCustomizeData d = SARibbonCustomizeData::makeChangeActionOrderCustomizeData(d_ptr->itemObjectName(categoryItem), + d_ptr->itemObjectName(pannelItem), + key, + d_ptr->mActionMgr, + -1); + d_ptr->mCustomizeDatasCache.append(d); + int r = item->row(); + item = pannelItem->takeChild(r); + pannelItem->removeRow(r); + pannelItem->insertRow(r + 1, item); + } + + // 保持焦点,方便连续操作 + setSelectItem(item); + onTreeViewResultClicked(item->index()); +} + +void SARibbonCustomizeWidget::onItemChanged(QStandardItem* item) +{ + if (item == nullptr) { + return; + } + int level = itemLevel(item); + + if (0 == level) { + if (item->isCheckable()) { + QString objname = d_ptr->itemObjectName(item); + SARibbonCustomizeData d = SARibbonCustomizeData::makeVisibleCategoryCustomizeData(objname, item->checkState() == Qt::Checked); + d_ptr->mCustomizeDatasCache.append(d); + } + } +} + +void SARibbonCustomizeWidget::onLineEditSearchActionTextEdited(const QString& text) +{ + d_ptr->mAcionModel->search(text); +} + +void SARibbonCustomizeWidget::onPushButtonResetClicked() +{ + int btn = QMessageBox::warning(this, + tr("Warning"), + tr("Are you sure reset all customize setting?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (btn == QMessageBox::Yes) { + clear(); + } +} + +/*** End of inlined file: SARibbonCustomizeWidget.cpp ***/ + +/*** Start of inlined file: SARibbonCustomizeDialog.cpp ***/ +#include +#include +#include +#include + +/** + * @brief The SARibbonCustomizeDialogUi class + */ +class SARibbonCustomizeDialogUi +{ +public: + SARibbonCustomizeWidget* customWidget; + QVBoxLayout* verticalLayoutMain; + QHBoxLayout* horizontalLayoutButtonGroup; + QPushButton* pushButtonCancel; + QPushButton* pushButtonOk; + QSpacerItem* spacerItemleft; + void setupUi(SARibbonMainWindow* ribbonWindow, QWidget* customizeDialog) + { + if (customizeDialog->objectName().isEmpty()) { + customizeDialog->setObjectName(QStringLiteral("SARibbonCustomizeDialog")); + } + customizeDialog->resize(800, 600); + verticalLayoutMain = new QVBoxLayout(customizeDialog); + verticalLayoutMain->setObjectName(QStringLiteral("verticalLayoutMain")); + + customWidget = new SARibbonCustomizeWidget(ribbonWindow, customizeDialog); + customWidget->setObjectName(QStringLiteral("customWidget")); + verticalLayoutMain->addWidget(customWidget); + + horizontalLayoutButtonGroup = new QHBoxLayout(); + horizontalLayoutButtonGroup->setObjectName(QStringLiteral("horizontalLayoutButtonGroup")); + + spacerItemleft = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + horizontalLayoutButtonGroup->addItem(spacerItemleft); + + pushButtonCancel = new QPushButton(customizeDialog); + pushButtonCancel->setObjectName(QStringLiteral("pushButtonCancel")); + horizontalLayoutButtonGroup->addWidget(pushButtonCancel); + + pushButtonOk = new QPushButton(customizeDialog); + pushButtonOk->setObjectName(QStringLiteral("pushButtonCancel")); + horizontalLayoutButtonGroup->addWidget(pushButtonOk); + verticalLayoutMain->addItem(horizontalLayoutButtonGroup); + retranslateUi(customizeDialog); + } + + void retranslateUi(QWidget* customizeDialog) + { + customizeDialog->setWindowTitle(QApplication::translate("SARibbonCustomizeDialog", "Customize Dialog", Q_NULLPTR)); + pushButtonCancel->setText(QApplication::translate("SARibbonCustomizeDialog", "Cancel", Q_NULLPTR)); + pushButtonOk->setText(QApplication::translate("SARibbonCustomizeDialog", "OK", Q_NULLPTR)); + } +}; + +//////////////////////////////////////////////////////////////////// + +SARibbonCustomizeDialog::SARibbonCustomizeDialog(SARibbonMainWindow* ribbonWindow, QWidget* p, Qt::WindowFlags f) + : QDialog(p, f), ui(new SARibbonCustomizeDialogUi) +{ + ui->setupUi(ribbonWindow, this); + initConnection(); +} + +/** + * @brief 设置action管理器 + * + * 等同@ref SARibbonCustomizeWidget::setupActionsManager + * @param mgr + */ +void SARibbonCustomizeDialog::setupActionsManager(SARibbonActionsManager* mgr) +{ + ui->customWidget->setupActionsManager(mgr); +} + +/** + * @brief //判断用户是否有要存储的内容,对应save动作 + * @return + */ +bool SARibbonCustomizeDialog::isApplied() const +{ + return ui->customWidget->isApplied(); +} + +/** + * @brief 判断用户是否有改动内容,对应apply动作 + * @return + */ +bool SARibbonCustomizeDialog::isCached() const +{ + return ui->customWidget->isCached(); +} + +void SARibbonCustomizeDialog::initConnection() +{ + connect(ui->pushButtonOk, &QPushButton::clicked, this, &QDialog::accept); + connect(ui->pushButtonCancel, &QPushButton::clicked, this, &QDialog::reject); +} + +/** + * @brief 等同SARibbonCustomizeWidget::applys + * + * @ref SARibbonCustomizeWidget::applys + * @return + */ +bool SARibbonCustomizeDialog::applys() +{ + return ui->customWidget->applys(); +} + +/** + * @brief 清除所有动作 + * + * @ref SARibbonCustomizeWidget::clear + */ +void SARibbonCustomizeDialog::clear() +{ + ui->customWidget->clear(); +} + +/** + * @brief 转换为xml + * + * @ref SARibbonCustomizeWidget::toXml + * @param xml + * @return + */ +bool SARibbonCustomizeDialog::toXml(QXmlStreamWriter* xml) const +{ + return (ui->customWidget->toXml(xml)); +} + +/** + * @brief 等同SARibbonCustomizeWidget::toXml + * @ref SARibbonCustomizeWidget::toXml + * @param xmlpath + * @return + */ +bool SARibbonCustomizeDialog::toXml(const QString& xmlpath) const +{ + return (ui->customWidget->toXml(xmlpath)); +} + +/** + * @brief 等同SARibbonCustomizeWidget::fromXml + * @param xml + */ +void SARibbonCustomizeDialog::fromXml(QXmlStreamReader* xml) +{ + ui->customWidget->fromXml(xml); +} + +/** + * @brief 等同SARibbonCustomizeWidget::fromXml + * @param xmlpath + */ +void SARibbonCustomizeDialog::fromXml(const QString& xmlpath) +{ + ui->customWidget->fromXml(xmlpath); +} + +/** + * @brief 返回SARibbonCustomizeWidget窗口指针 + * + * 通过SARibbonCustomizeWidget窗口可以操作更多的内容 + * + * @return SARibbonCustomizeWidget指针,参考@ref SARibbonCustomizeWidget + */ +SARibbonCustomizeWidget* SARibbonCustomizeDialog::customizeWidget() const +{ + return (ui->customWidget); +} + +/*** End of inlined file: SARibbonCustomizeDialog.cpp ***/ + +/*** Start of inlined file: SARibbonMainWindow.cpp ***/ +#include +#include +#include +#include +#include +#include + +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER +#include + +#else + +#endif + +/** + * @brief The SARibbonMainWindowPrivate class + */ +class SARibbonMainWindow::PrivateData +{ + SA_RIBBON_DECLARE_PUBLIC(SARibbonMainWindow) +public: + PrivateData(SARibbonMainWindow* p); + void installFrameless(SARibbonMainWindow* p); + +public: + SARibbonMainWindow::RibbonTheme mCurrentRibbonTheme { SARibbonMainWindow::RibbonThemeOffice2021Blue }; + SARibbonSystemButtonBar* mWindowButtonGroup { nullptr }; +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + QWK::WidgetWindowAgent* mFramelessHelper { nullptr }; +#else + SAFramelessHelper* mFramelessHelper { nullptr }; +#endif +}; + +SARibbonMainWindow::PrivateData::PrivateData(SARibbonMainWindow* p) : q_ptr(p) +{ +} + +void SARibbonMainWindow::PrivateData::installFrameless(SARibbonMainWindow* p) +{ +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + mFramelessHelper = new QWK::WidgetWindowAgent(p); + mFramelessHelper->setup(p); +#else + mFramelessHelper = new SAFramelessHelper(p); +#endif +} + +//=================================================== +// SARibbonMainWindow +//=================================================== +SARibbonMainWindow::SARibbonMainWindow(QWidget* parent, bool useRibbon, const Qt::WindowFlags flags) + : QMainWindow(parent, flags), d_ptr(new SARibbonMainWindow::PrivateData(this)) +{ + connect(qApp, &QApplication::primaryScreenChanged, this, &SARibbonMainWindow::onPrimaryScreenChanged); + if (useRibbon) { + d_ptr->installFrameless(this); + setRibbonBar(createRibbonBar()); + setRibbonTheme(ribbonTheme()); + } +} + +SARibbonMainWindow::~SARibbonMainWindow() +{ +} + +/** + * @brief 返回ribbonbar,如果不是使用ribbon模式,返回nullptr + * @return + */ +SARibbonBar* SARibbonMainWindow::ribbonBar() const +{ + return qobject_cast< SARibbonBar* >(menuWidget()); +} + +/** + * @brief 设置ribbonbar + * @param bar + */ +void SARibbonMainWindow::setRibbonBar(SARibbonBar* bar) +{ + QWidget* old = QMainWindow::menuWidget(); + if (old) { + // 如果之前已经设置了menubar,要把之前的删除 + old->deleteLater(); + } + QMainWindow::setMenuWidget(bar); + const int th = bar->titleBarHeight(); + // 设置window按钮 + if (nullptr == d_ptr->mWindowButtonGroup) { + d_ptr->mWindowButtonGroup = RibbonSubElementFactory->createWindowButtonGroup(this); + } + SARibbonSystemButtonBar* wg = d_ptr->mWindowButtonGroup; + wg->setWindowStates(windowState()); + wg->setWindowTitleHeight(th); + wg->show(); +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + auto helper = d_ptr->mFramelessHelper; + helper->setTitleBar(bar); + // 以下这些窗口,需要允许点击 + helper->setHitTestVisible(wg); // IMPORTANT! + helper->setHitTestVisible(bar->ribbonTabBar()); // IMPORTANT! + helper->setHitTestVisible(bar->rightButtonGroup()); // IMPORTANT! + helper->setHitTestVisible(bar->applicationButton()); // IMPORTANT! + helper->setHitTestVisible(bar->quickAccessBar()); // IMPORTANT! + helper->setHitTestVisible(bar->ribbonStackedWidget()); // IMPORTANT! + if (wg->closeButton()) { + helper->setSystemButton(QWK::WindowAgentBase::Close, wg->closeButton()); + } + if (wg->minimizeButton()) { + helper->setSystemButton(QWK::WindowAgentBase::Minimize, wg->minimizeButton()); + } + if (wg->maximizeButton()) { + helper->setSystemButton(QWK::WindowAgentBase::Maximize, wg->maximizeButton()); + } +#else + bar->installEventFilter(this); + // 设置窗体的标题栏高度 + d_ptr->mFramelessHelper->setTitleHeight(th); + d_ptr->mFramelessHelper->setRubberBandOnResize(false); +#endif +} + +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + +/** + * @brief 如果ribbon中有自定义的窗口在标题栏等非点击区域加入后,想能点击,需要调用此接口告知可点击 + * @param w + * @param visible + */ +void SARibbonMainWindow::setFramelessHitTestVisible(const QWidget* w, bool visible) +{ + auto helper = d_ptr->mFramelessHelper; + helper->setHitTestVisible(w, visible); +} +#else + +SAFramelessHelper* SARibbonMainWindow::framelessHelper() +{ + return (d_ptr->mFramelessHelper); +} + +#endif + +bool SARibbonMainWindow::eventFilter(QObject* obj, QEvent* e) +{ +#if SARIBBON_USE_3RDPARTY_FRAMELESSHELPER +#else + // 这个过滤是为了把ribbonBar上的动作传递到mainwindow,再传递到frameless, + // 由于ribbonbar会遮挡掉frameless的区域,导致frameless无法捕获这些消息 + // 因此必须ribbonBar()->installEventFilter(this); + // 20240101发现installEventFilter后,SARibbonMainWindow没有执行这个回调 + if (obj == ribbonBar()) { + switch (e->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::Leave: + case QEvent::HoverMove: + case QEvent::MouseButtonDblClick: + QApplication::sendEvent(this, e); + + default: + break; + } + } +#endif + return (QMainWindow::eventFilter(obj, e)); +} + +/** + * @brief 获取系统按钮组,可以在此基础上添加其他按钮 + * @return + */ +SARibbonSystemButtonBar* SARibbonMainWindow::windowButtonBar() const +{ + return d_ptr->mWindowButtonGroup; +} + +/** + * @brief 此函数仅用于控制最小最大化和关闭按钮的显示 + */ +void SARibbonMainWindow::updateWindowFlag(Qt::WindowFlags flags) +{ + if (SARibbonSystemButtonBar* g = d_ptr->mWindowButtonGroup) { + g->updateWindowFlag(flags); + } +} + +/** + * @brief SARibbonMainWindow::setRibbonTheme + * + * 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 + * @code + * QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); + * @endcode + * @param theme + */ +void SARibbonMainWindow::setRibbonTheme(SARibbonMainWindow::RibbonTheme theme) +{ + sa_set_ribbon_theme(this, theme); + d_ptr->mCurrentRibbonTheme = theme; + if (SARibbonBar* bar = ribbonBar()) { + auto theme = ribbonTheme(); + // 尺寸修正 + switch (theme) { + case RibbonThemeWindows7: + case RibbonThemeOffice2013: + case RibbonThemeOffice2016Blue: + case RibbonThemeDark: + case RibbonThemeDark2: { + //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + //! office2013.qss的margin信息如下设置 + //! margin-top: 0px; + //! margin-right: 0px; + //! margin-left: 5px; + //! margin-bottom: 0px; + SARibbonTabBar* tab = bar->ribbonTabBar(); + if (!tab) { + break; + } + tab->setTabMargin(QMargins(5, 0, 0, 0)); + } break; + case RibbonThemeOffice2021Blue: { + SARibbonTabBar* tab = bar->ribbonTabBar(); + if (!tab) { + break; + } + //! 在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + //! office2021.qss的margin信息如下设置 + //! margin-top: 0px; + //! margin-right: 5px; + //! margin-left: 5px; + //! margin-bottom: 0px; + tab->setTabMargin(QMargins(5, 0, 5, 0)); + } + default: + break; + } + // 上下文标签颜色设置,以及基线颜色设置 + switch (theme) { + case RibbonThemeWindows7: + case RibbonThemeOffice2013: + case RibbonThemeDark: + bar->setContextCategoryColorList(QList< QColor >()); //< 设置空颜色列表会重置为默认色系 + break; + case RibbonThemeOffice2016Blue: + bar->setContextCategoryColorList(QList< QColor >() << QColor(18, 64, 120)); //< 设置空颜色列表会重置为默认色系 + break; + case RibbonThemeOffice2021Blue: + bar->setContextCategoryColorList(QList< QColor >() << QColor(0, 0, 0, 0)); //< 设置空颜色列表会重置为默认色系 + break; + default: + break; + } + // 基线颜色设置 + if (RibbonThemeOffice2013 == theme) { + bar->setTabBarBaseLineColor(QColor(186, 201, 219)); + } else { + bar->setTabBarBaseLineColor(QColor()); + } + } +} + +SARibbonMainWindow::RibbonTheme SARibbonMainWindow::ribbonTheme() const +{ + return (d_ptr->mCurrentRibbonTheme); +} + +bool SARibbonMainWindow::isUseRibbon() const +{ + return (nullptr != ribbonBar()); +} + +/** + * @brief 创建ribbonbar的工厂函数 + * @return + */ +SARibbonBar* SARibbonMainWindow::createRibbonBar() +{ + SARibbonBar* bar = new SARibbonBar(this); + bar->setContentsMargins(3, 0, 3, 0); + return bar; +} + +void SARibbonMainWindow::resizeEvent(QResizeEvent* e) +{ + QMainWindow::resizeEvent(e); + SARibbonBar* bar = ribbonBar(); + SARibbonSystemButtonBar* wg = d_ptr->mWindowButtonGroup; + + if (wg) { + if (bar) { + const int th = bar->titleBarHeight(); + if (th != wg->height()) { + wg->setWindowTitleHeight(th); + } + } + QSize wgSizeHint = wg->sizeHint(); + wg->setGeometry(frameGeometry().width() - wgSizeHint.width(), 0, wgSizeHint.width(), wgSizeHint.height()); + } + if (bar) { + if (wg) { + bar->setWindowButtonGroupSize(wg->size()); + } + if (bar->size().width() != (this->size().width())) { + bar->setFixedWidth(this->size().width()); + } + } +} + +void SARibbonMainWindow::changeEvent(QEvent* e) +{ + if (e) { + switch (e->type()) { + case QEvent::WindowStateChange: { + if (SARibbonSystemButtonBar* wg = d_ptr->mWindowButtonGroup) { + wg->setWindowStates(windowState()); + } + } break; + + default: + break; + } + } + QMainWindow::changeEvent(e); +} + +/** + * @brief 主屏幕切换触发的信号 + * @param screen + */ +void SARibbonMainWindow::onPrimaryScreenChanged(QScreen* screen) +{ + Q_UNUSED(screen); + // 主屏幕切换后,从新计算所有尺寸 + if (SARibbonBar* bar = ribbonBar()) { + qDebug() << "Primary Screen Changed"; + bar->updateRibbonGeometry(); + } +} + +void sa_set_ribbon_theme(QWidget* w, SARibbonMainWindow::RibbonTheme theme) +{ + QFile file; + switch (theme) { + case SARibbonMainWindow::RibbonThemeWindows7: + file.setFileName(":/theme/resource/theme-win7.qss"); + break; + case SARibbonMainWindow::RibbonThemeOffice2013: + file.setFileName(":/theme/resource/theme-office2013.qss"); + break; + case SARibbonMainWindow::RibbonThemeOffice2016Blue: + file.setFileName(":/theme/resource/theme-office2016-blue.qss"); + break; + case SARibbonMainWindow::RibbonThemeOffice2021Blue: + file.setFileName(":/theme/resource/theme-office2021-blue.qss"); + break; + case SARibbonMainWindow::RibbonThemeDark: + file.setFileName(":/theme/resource/theme-dark.qss"); + break; + case SARibbonMainWindow::RibbonThemeDark2: + file.setFileName(":/theme/resource/theme-dark2.qss"); + break; + default: + file.setFileName(":/theme/resource/theme-office2013.qss"); + break; + } + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } + // 有反馈用qstring接住文件内容,再设置进去才能生效(qt5.7版本) + QString qss = QString::fromUtf8(file.readAll()); + w->setStyleSheet(qss); +} + +/*** End of inlined file: SARibbonMainWindow.cpp ***/ + +#ifdef _MSC_VER +#pragma warning(pop) +#pragma pop_macro("_CRT_SECURE_NO_WARNINGS") +#endif diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbon.h b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbon.h new file mode 100644 index 0000000000000000000000000000000000000000..ddba346b5fe418d8de15d3c0ec5d42f73e9a3f23 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbon.h @@ -0,0 +1,3566 @@ +#ifndef SA_RIBBON_H +#define SA_RIBBON_H +// 定义此宏,将SA_RIBBON_EXPORT定义为空 +#ifndef SA_RIBBON_BAR_NO_EXPORT +#define SA_RIBBON_BAR_NO_EXPORT +#endif +// 定义此宏,将SA_COLOR_WIDGETS_API定义为空 +#ifndef SA_COLOR_WIDGETS_NO_DLL +#define SA_COLOR_WIDGETS_NO_DLL +#endif + +/*** Start of inlined file: SARibbonAmalgamTemplatePublicHeaders.h ***/ +// Global + +/*** Start of inlined file: SAColorWidgetsGlobal.h ***/ +#ifndef SACOLORWIDGETSGLOBAL_H +#define SACOLORWIDGETSGLOBAL_H +#include + +/** + * @def color-widgets的数字版本 MAJ.MIN.PAT + */ +#ifndef SA_COLOR_WIDGETS_VERSION_MAJ +#define SA_COLOR_WIDGETS_VERSION_MAJ 0 +#endif +/** + * @def color-widgets的数字版本 MAJ.MIN.PAT + */ +#ifndef SA_COLOR_WIDGETS_VERSION_MIN +#define SA_COLOR_WIDGETS_VERSION_MIN 1 +#endif +/** + * @def color-widgets的数字版本 MAJ.MIN.PAT + */ +#ifndef SA_COLOR_WIDGETS_VERSION_PAT +#define SA_COLOR_WIDGETS_VERSION_PAT 0 +#endif + +/** + * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 + */ +#ifndef SA_COLOR_WIDGETS_DECLARE_PRIVATE +#define SA_COLOR_WIDGETS_DECLARE_PRIVATE(classname) \ + class PrivateData; \ + friend class classname::PrivateData; \ + QScopedPointer< PrivateData > d_ptr; +#endif +/** + * @def 模仿Q_DECLARE_PUBLIC + */ +#ifndef SA_COLOR_WIDGETS_DECLARE_PUBLIC +#define SA_COLOR_WIDGETS_DECLARE_PUBLIC(classname) \ + friend class classname; \ + classname* q_ptr { nullptr }; +#endif + +#ifndef SA_COLOR_WIDGETS_NO_DLL +#if defined(SA_COLOR_WIDGETS_MAKE_LIB) // 定义此宏将构建library +#ifndef SA_COLOR_WIDGETS_API +#define SA_COLOR_WIDGETS_API Q_DECL_EXPORT +#endif +#else +#ifndef SA_COLOR_WIDGETS_API +#define SA_COLOR_WIDGETS_API Q_DECL_IMPORT +#endif +#endif +#else +#ifndef SA_COLOR_WIDGETS_API +#define SA_COLOR_WIDGETS_API +#endif +#endif + +#endif // SACOLORWIDGETSGLOBAL_H + +/*** End of inlined file: SAColorWidgetsGlobal.h ***/ + +/*** Start of inlined file: SARibbonGlobal.h ***/ +#ifndef SARIBBONGLOBAL_H +#define SARIBBONGLOBAL_H +#include +#include +#include + +/** + @file SARibbonGlobal.h + */ + +/** + * @def ribbon的数字版本 {MAJ}.MIN.PAT + */ +#ifndef SA_RIBBON_BAR_VERSION_MAJ +#define SA_RIBBON_BAR_VERSION_MAJ 2 +#endif +/** + * @def ribbon的数字版本 MAJ.{MIN}.PAT + */ +#ifndef SA_RIBBON_BAR_VERSION_MIN +#define SA_RIBBON_BAR_VERSION_MIN 0 +#endif +/** + * @def ribbon的数字版本 MAJ.MIN.{PAT} + */ +#ifndef SA_RIBBON_BAR_VERSION_PAT +#define SA_RIBBON_BAR_VERSION_PAT 1 +#endif + +#ifndef SA_RIBBON_BAR_NO_EXPORT +#if defined(SA_RIBBON_BAR_MAKE_LIB) // 定义此宏将构建library +#define SA_RIBBON_EXPORT Q_DECL_EXPORT +#else +#define SA_RIBBON_EXPORT Q_DECL_IMPORT +#endif +#endif + +#ifndef SA_RIBBON_EXPORT +#define SA_RIBBON_EXPORT +#endif + +/** + * @def 模仿Q_DECLARE_PRIVATE,但不用前置声明而是作为一个内部类 + */ +#ifndef SA_RIBBON_DECLARE_PRIVATE +#define SA_RIBBON_DECLARE_PRIVATE(classname) \ + class PrivateData; \ + friend class classname::PrivateData; \ + std::unique_ptr< PrivateData > d_ptr; +#endif +/** + * @def 模仿Q_DECLARE_PUBLIC + */ +#ifndef SA_RIBBON_DECLARE_PUBLIC +#define SA_RIBBON_DECLARE_PUBLIC(classname) \ + friend class classname; \ + classname* q_ptr { nullptr }; \ + PrivateData(const PrivateData&) = delete; \ + PrivateData& operator=(const PrivateData&) = delete; +#endif + +/** + * @brief 定义Ribbon的对其方式,目前支持左对齐和居中对其 + */ +enum class SARibbonAlignment +{ + AlignLeft, ///< 左对齐,tab栏左对齐,同时category也是左对齐 + AlignCenter ///< 居中对其,tab栏居中对齐,同时category也是居中对齐 +}; + +/** + * @def 属性,用于标记是否可以进行自定义,用于动态设置到@ref SARibbonCategory 和@ref SARibbonPannel + * 值为bool,在为true时,可以通过@ref SARibbonCustomizeWidget 改变这个SARibbonCategory和SARibbonPannel的布局, + * 默认不会有此属性,仅在有此属性且为true时才会在SARibbonCustomizeWidget中能显示为可设置 + */ +#ifndef SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE +#define SA_RIBBON_BAR_PROP_CAN_CUSTOMIZE "_sa_isCanCustomize" +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) +#ifndef SA_FONTMETRICS_WIDTH +#define SA_FONTMETRICS_WIDTH(fm, str) fm.horizontalAdvance(str) +#endif +#else +#ifndef SA_FONTMETRICS_WIDTH +#define SA_FONTMETRICS_WIDTH(fm, str) fm.width(str) +#endif +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT +#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPosition().toPoint() +#endif +#else +#ifndef SA_MOUSEEVENT_GLOBALPOS_POINT +#define SA_MOUSEEVENT_GLOBALPOS_POINT(MouseEventPtr) MouseEventPtr->globalPos() +#endif +#endif + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#ifndef SA_HOVEREVENT_POS_POINT +#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->position().toPoint() +#endif +#else +#ifndef SA_HOVEREVENT_POS_POINT +#define SA_HOVEREVENT_POS_POINT(HoverEventPtr) HoverEventPtr->pos() +#endif +#endif + +/** + @def 定义此宏,将打印和尺寸刷新相关的信息 + + 仅用于调试 + */ +#ifndef SA_DEBUG_PRINT_SIZE_HINT +#define SA_DEBUG_PRINT_SIZE_HINT 0 +#endif + +/** + @def 定义此宏,将打印事件 + + 仅用于调试 + */ +#ifndef SA_DEBUG_PRINT_EVENT +#define SA_DEBUG_PRINT_EVENT 0 +#endif + +/** + @def 定义此宏,qDebug将支持SARibbonBar的属性打印 + + 仅用于调试 + */ +#ifndef SA_DEBUG_PRINT_SARIBBONBAR +#define SA_DEBUG_PRINT_SARIBBONBAR 1 +#endif + +#endif // SARIBBONGLOBAL_H + +/*** End of inlined file: SARibbonGlobal.h ***/ + +// color widget + +/*** Start of inlined file: SAColorMenu.h ***/ +#ifndef SACOLORMENU_H +#define SACOLORMENU_H +#include + +class QWidgetAction; +class SAColorPaletteGridWidget; +class SAColorGridWidget; +class SAColorToolButton; +/** + * @brief 标准颜色菜单 + */ +class SA_COLOR_WIDGETS_API SAColorMenu : public QMenu +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorMenu) +public: + explicit SAColorMenu(QWidget* parent = nullptr); + explicit SAColorMenu(const QString& title, QWidget* parent = nullptr); + ~SAColorMenu(); + // 快速绑定colorbtn + void bindToColorToolButton(SAColorToolButton* btn); + // ThemeColorsPalette对应的action + QWidgetAction* themeColorsPaletteAction() const; + // CustomColorsWidget对应的action + QWidgetAction* getCustomColorsWidgetAction() const; + // 自定义颜色action + QAction* customColorAction() const; + // 获取ThemeColorsPalette + SAColorPaletteGridWidget* colorPaletteGridWidget() const; + // 获取自定义颜色grid + SAColorGridWidget* customColorsWidget() const; + // 构建无颜色action,默认无颜色action是没有的 + void enableNoneColorAction(bool on = true); + // 获取None Color Action,注意,enableNoneColorAction(true),之后才不是nullptr + QAction* noneColorAction() const; +public slots: + // 这是一个辅助槽函数,为了让用户自定义的其他action也能关联menu,可以调用此槽函数,实现selectedColor信号以及menu的隐藏 + void emitSelectedColor(const QColor& c); +signals: + /** + * @brief 选择了颜色触发的信号 + * @param c + */ + void selectedColor(const QColor& c); +private slots: + void onCustomColorActionTriggered(bool on); + void onNoneColorActionTriggered(bool on); + +private: + void init(const QList< QColor >& themeCls); +}; + +#endif // SACOLORMENU_H + +/*** End of inlined file: SAColorMenu.h ***/ + +/*** Start of inlined file: SAColorGridWidget.h ***/ +#ifndef SACOLORGRIDWIDGET_H +#define SACOLORGRIDWIDGET_H +#include +#include + +class QAbstractButton; +class SAColorToolButton; +/** + * @brief 一个grid的Color布局 + * + * 一个形如下面的颜色grid: + * + * □□□□□□□□□ + * + * □□□□□□□□□ + * + * □□□□□□□□□ + */ +class SA_COLOR_WIDGETS_API SAColorGridWidget : public QWidget +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorGridWidget) + Q_PROPERTY(int spacing READ spacing WRITE setSpacing) +public: + using FunColorBtn = std::function< void(SAColorToolButton*) >; + +public: + SAColorGridWidget(QWidget* par = nullptr); + ~SAColorGridWidget(); + // 设置列数,行数量会根据列数量来匹配,如果设置-1或者0,说明不限定列数量,这样会只有一行 + void setColumnCount(int c); + int columnCount() const; + // 设置当前的颜色列表 + void setColorList(const QList< QColor >& cls); + QList< QColor > getColorList() const; + // 间隔 + int spacing() const; + void setSpacing(int v); + // 获取颜色的数量 + int colorCount() const; + // 图标的尺寸 + void setColorIconSize(const QSize& s); + QSize colorIconSize() const; + // 设置颜色是否是checkable + void setColorCheckable(bool on = true); + bool isColorCheckable() const; + // 获取当前选中的颜色 + QColor currentCheckedColor() const; + // 获取index对于的colorbutton + SAColorToolButton* colorButton(int index) const; + // 垂直间距 + void setVerticalSpacing(int v); + int verticalSpacing() const; + // 水平间距 + void setHorizontalSpacing(int v); + int horizontalSpacing() const; + // 清除当前选中状态,这时没有颜色是选中的 + void clearCheckedState(); + // 对所有的colorbtn就行遍历处理,可以通过此函数进行tooltip设置等操作 + void iterationColorBtns(FunColorBtn fn); + // 设置行最小高度 + void setRowMinimumHeight(int row, int minSize); + // 让颜色块左对齐 + void setHorizontalSpacerToRight(bool on = true); +private slots: + void onButtonClicked(QAbstractButton* btn); + void onButtonPressed(QAbstractButton* btn); + void onButtonReleased(QAbstractButton* btn); + void onButtonToggled(QAbstractButton* btn, bool on); + +signals: + /** + * @brief 对于check模式,check的颜色触发的信号 + * @param c + * @param on + */ + void colorClicked(const QColor& c); + void colorPressed(const QColor& c); + void colorReleased(const QColor& c); + void colorToggled(const QColor& c, bool on); + +public: + virtual QSize sizeHint() const Q_DECL_OVERRIDE; +}; +namespace SA +{ +/** + * @brief 获取标准色列表(一共10种颜色) + * @return + */ +SA_COLOR_WIDGETS_API QList< QColor > getStandardColorList(); +} +#endif // SACOLORGRIDWIDGET_H + +/*** End of inlined file: SAColorGridWidget.h ***/ + +/*** Start of inlined file: SAColorPaletteGridWidget.h ***/ +#ifndef SACOLORPALETTEGRIDWIDGET_H +#define SACOLORPALETTEGRIDWIDGET_H +#include + +class QMenu; +class SAColorToolButton; +/** + * @brief 类似office的颜色选择窗口,有一排标准色,下面有一个颜色板,有3行浅色,有2行深色 + */ +class SA_COLOR_WIDGETS_API SAColorPaletteGridWidget : public QWidget +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorPaletteGridWidget) +public: + SAColorPaletteGridWidget(QWidget* par = nullptr); + SAColorPaletteGridWidget(const QList< QColor >& cls, QWidget* par = nullptr); + ~SAColorPaletteGridWidget(); + // 设置窗口维护的colorList + void setColorList(const QList< QColor >& cls); + QList< QColor > colorList() const; + // 设置颜色深浅比例factor,默认为{ 180, 160, 140, 75, 50 } + void setFactor(const QList< int >& factor); + QList< int > factor() const; + // 设置iconsize + void setColorIconSize(const QSize& s); + QSize colorIconSize() const; + // 设置颜色是否是checkable + void setColorCheckable(bool on = true); + bool isColorCheckable() const; +private slots: + void onMainColorClicked(const QColor& c); + void onPaletteColorClicked(const QColor& c); + +private: + void init(); +signals: + /** + * @brief 对于check模式,check的颜色触发的信号 + * @param c + * @param on + */ + void colorClicked(const QColor& c); +}; + +#endif // SACOLORPALETTEGRIDWIDGET_H + +/*** End of inlined file: SAColorPaletteGridWidget.h ***/ + +/*** Start of inlined file: SAColorToolButton.h ***/ +#ifndef SACOLORTOOLBUTTON_H +#define SACOLORTOOLBUTTON_H +#include + +class QPaintEvent; +class QResizeEvent; +class QPainter; +class QStylePainter; +/** + * @brief 这是一个只显示颜色的toolbutton + * + * + * 在ToolButtonIconOnly模式下,如果没有setIcon,则颜色占用所有区域,如下图所示 + * + * ┌─────┐ + * │color│ + * └─────┘ + * + * 如果在ToolButtonIconOnly模式下有图标,图标在上面显示,下面显示颜色,如下图所示 + * + * ┌─────┐ + * │icon │ + * │color│ + * └─────┘ + * + * 在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下,setIconSize 可以指定颜色的大小, + * 但只在ToolButtonTextBesideIcon,ToolButtonTextUnderIcon下有效 + * + * 如果没有设置图标,也就是setIcon(QIcon()),iconSize作为颜色块的大小 + * + * ┌─────────┐ + * │┌─┐ │ + * │└─┘ │ + * └─────────┘ + * + * 如果有图标,颜色条会在图标下方,为图标高度的1/4 为图标宽度一致,如若超过控件的大小,会自动缩小体积 + * + */ +class SA_COLOR_WIDGETS_API SAColorToolButton : public QToolButton +{ + Q_OBJECT + SA_COLOR_WIDGETS_DECLARE_PRIVATE(SAColorToolButton) +public: + explicit SAColorToolButton(QWidget* parent = nullptr); + ~SAColorToolButton(); + // 获取颜色 + QColor color() const; + // 设置Margins + void setMargins(const QMargins& mg); + QMargins margins() const; + // 绘制无颜色 + static void paintNoneColor(QPainter* p, const QRect& colorRect); +public slots: + // 设置颜色,会发射colorChanged信号 + void setColor(const QColor& c); + +protected: + // 获取关键的三个rect位置 + virtual void calcRect(const QStyleOptionToolButton& opt, QRect& iconRect, QRect& textRect, QRect& colorRect); + virtual void paintButton(QStylePainter* p, const QStyleOptionToolButton& opt); + virtual void paintIcon(QStylePainter* p, const QRect& iconRect, const QStyleOptionToolButton& opt); + virtual void paintText(QStylePainter* p, const QRect& textRect, const QStyleOptionToolButton& opt); + virtual void paintColor(QStylePainter* p, const QRect& colorRect, const QColor& color, const QStyleOptionToolButton& opt); + +protected: + virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + virtual QSize sizeHint() const Q_DECL_OVERRIDE; +private slots: + void onButtonClicked(bool checked = false); +signals: + /** + * @brief 颜色被点击的响应 + * @param color + */ + void colorClicked(const QColor& color, bool checked = false); + /** + * @brief 颜色改变信号 + * @param color + */ + void colorChanged(const QColor& color); +}; + +#endif // SACOLORTOOLBUTTON_H + +/*** End of inlined file: SAColorToolButton.h ***/ + +// sa ribbon + +/*** Start of inlined file: SAFramelessHelper.h ***/ +#ifndef SAFRAMELESSHELPER_H +#define SAFRAMELESSHELPER_H + +#include + +class QWidget; + +class SA_RIBBON_EXPORT SAFramelessHelper : public QObject +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SAFramelessHelper) + friend class SAPrivateFramelessWidgetData; + +public: + explicit SAFramelessHelper(QObject* parent); + ~SAFramelessHelper(); + // 激活窗体 + void activateOn(QWidget* topLevelWidget); + + // 移除窗体 + void removeFrom(QWidget* topLevelWidget); + + // 设置窗体移动 + void setWidgetMovable(bool movable); + + // 设置窗体缩放 + void setWidgetResizable(bool resizable); + + // 设置橡皮筋移动 + void setRubberBandOnMove(bool movable); + + // 设置橡皮筋缩放 + void setRubberBandOnResize(bool resizable); + + // 设置边框的宽度 + void setBorderWidth(int width); + + // 设置标题栏高度 + void setTitleHeight(int height); + bool widgetResizable(); + bool widgetMovable(); + bool rubberBandOnMove(); + bool rubberBandOnResisze(); + uint borderWidth(); + uint titleHeight(); + +protected: + // 事件过滤,进行移动、缩放等 + virtual bool eventFilter(QObject* obj, QEvent* event); +}; + +#endif // FRAMELESSHELPER_H + +/*** End of inlined file: SAFramelessHelper.h ***/ + +/*** Start of inlined file: SARibbonApplicationButton.h ***/ +#ifndef SARIBBONAPPLICATIONBUTTON_H +#define SARIBBONAPPLICATIONBUTTON_H +#include + +/** + * @brief The SARibbonApplicationButton class + * + * 默认的plicationButton,可以通过样式指定不一样的ApplicationButton + */ +class SA_RIBBON_EXPORT SARibbonApplicationButton : public QToolButton +{ + Q_OBJECT +public: + SARibbonApplicationButton(QWidget* parent = nullptr); + SARibbonApplicationButton(const QString& text, QWidget* parent = nullptr); + SARibbonApplicationButton(const QIcon& icon, const QString& text, QWidget* parent = nullptr); +}; + +#endif // SARIBBONAPPLICATIONBUTTON_H + +/*** End of inlined file: SARibbonApplicationButton.h ***/ + +/*** Start of inlined file: SARibbonSystemButtonBar.h ***/ +#ifndef SARIBBONSYSTEMBUTTONBAR_H +#define SARIBBONSYSTEMBUTTONBAR_H + +#include +#include + +/** + * \brief 窗口的最大最小化按钮 + * @note 内部有个SARibbonButtonGroupWidget,其ObjectName = SASystemButtonGroup + * 如果需要qss,可以进行特化处理: + * + * @code + * SARibbonButtonGroupWidget#SASystemButtonGroup + * @endcode + * + */ +class SA_RIBBON_EXPORT SARibbonSystemButtonBar : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonSystemButtonBar) +public: + SARibbonSystemButtonBar(QWidget* parent); + SARibbonSystemButtonBar(QWidget* parent, Qt::WindowFlags flags); + ~SARibbonSystemButtonBar(); + void setupMinimizeButton(bool on); + void setupMaximizeButton(bool on); + void setupCloseButton(bool on); + // 以最顶层的widget的WindowFlag作为WindowFlag + void updateWindowFlag(); + void updateWindowFlag(Qt::WindowFlags flags); + // 设置按钮的宽度比例,最终按钮宽度将按照此比例进行设置 + void setButtonWidthStretch(int close = 4, int max = 3, int min = 3); + // 标题栏高度 + void setWindowTitleHeight(int h); + int windowTitleHeight() const; + // 设置标准系统按钮的宽度 + void setWindowButtonWidth(int w); + int windowButtonWidth() const; + // 设置Qt::WindowStates + void setWindowStates(Qt::WindowStates s); + // 仅获取按钮的状态 + Qt::WindowFlags windowButtonFlags() const; + // 三个标准系统窗口按钮 + QAbstractButton* minimizeButton() const; + QAbstractButton* maximizeButton() const; + QAbstractButton* closeButton() const; + + // 图标尺寸 + void setIconSize(const QSize& ic); + QSize iconSize() const; + // 生成并添加一个action + QAction* addAction(QAction* a, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addAction(const QString& text, + const QIcon& icon, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addMenu(QMenu* menu, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + QAction* addSeparator(); + QAction* addWidget(QWidget* w); + + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + +protected: + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + +protected slots: + Q_SLOT void closeWindow(); + Q_SLOT void minimizeWindow(); + Q_SLOT void maximizeWindow(); +}; + +/** + * @brief The SARibbonSystemToolButton class + */ +class SARibbonSystemToolButton : public QToolButton +{ + Q_OBJECT +public: + SARibbonSystemToolButton(QWidget* p = nullptr); +}; + +#endif // SARIBBONSYSTEMBUTTONBAR_H + +/*** End of inlined file: SARibbonSystemButtonBar.h ***/ + +/*** Start of inlined file: SARibbonToolButton.h ***/ +#ifndef SARIBBONTOOLBUTTON_H +#define SARIBBONTOOLBUTTON_H + +#include +#include +/** + * @brief Ribbon界面适用的toolButton + * + * 相对于普通toolbutton,主要多了两个类型设置,@ref setButtonType 和 @ref setLargeButtonType + * + * @note @sa setIconSize 函数不在起作用,iconsize是根据当前尺寸动态调整的 + */ +class SA_RIBBON_EXPORT SARibbonToolButton : public QToolButton +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonToolButton) +public: + /** + * @brief 按钮样式 + */ + enum RibbonButtonType + { + LargeButton, + SmallButton + }; + Q_ENUM(RibbonButtonType) + +public: + SARibbonToolButton(QWidget* parent = Q_NULLPTR); + SARibbonToolButton(QAction* defaultAction, QWidget* parent = Q_NULLPTR); + ~SARibbonToolButton(); + // 标记按钮的样式,按钮的样式有不同的渲染方式 + RibbonButtonType buttonType() const; + void setButtonType(const RibbonButtonType& buttonType); + // 是否是小按钮 + bool isSmallRibbonButton() const; + // 是否是大按钮 + bool isLargeRibbonButton() const; + // 最小尺寸提示 + virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; + + // 获取间距 + int spacing() const; + // 更新尺寸 + void updateRect(); + + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + +public: + // 在lite模式下是否允许文字换行 + static void setEnableWordWrap(bool on); + static bool isEnableWordWrap(); + +protected: + virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + virtual void mouseMoveEvent(QMouseEvent* e) Q_DECL_OVERRIDE; + virtual void mousePressEvent(QMouseEvent* e) Q_DECL_OVERRIDE; + virtual void mouseReleaseEvent(QMouseEvent* e) Q_DECL_OVERRIDE; + virtual void focusOutEvent(QFocusEvent* e) Q_DECL_OVERRIDE; + virtual void leaveEvent(QEvent* e) Q_DECL_OVERRIDE; + virtual bool hitButton(const QPoint& pos) const Q_DECL_OVERRIDE; + virtual bool event(QEvent* e) Q_DECL_OVERRIDE; + // 事件改变 - 主要为了捕获字体的改变 + virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; + virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; + +protected: + // 绘制按钮 + virtual void paintButton(QPainter& p, const QStyleOptionToolButton& opt); + // 绘制图标 + virtual void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); + // 绘制文本 + virtual void paintText(QPainter& p, const QStyleOptionToolButton& opt, const QRect& textDrawRect); + // 绘制Indicator + virtual void paintIndicator(QPainter& p, const QStyleOptionToolButton& opt, const QRect& indicatorDrawRect); + +private: + static void drawArrow(const QStyle* style, + const QStyleOptionToolButton* toolbutton, + const QRect& rect, + QPainter* painter, + const QWidget* widget = 0); + +protected: +}; + +namespace SA +{ +QDebug operator<<(QDebug debug, const QStyleOptionToolButton& opt); +} +#endif // SARIBBONTOOLBUTTON_H + +/*** End of inlined file: SARibbonToolButton.h ***/ + +/*** Start of inlined file: SARibbonColorToolButton.h ***/ +#ifndef SARIBBONCOLORTOOLBUTTON_H +#define SARIBBONCOLORTOOLBUTTON_H + +class SAColorMenu; +/** + * @brief Refer to the color setting button in the office, which can display the color below the icon(参考office的颜色设置按钮,可以显示颜色在图标下方) + */ +class SA_RIBBON_EXPORT SARibbonColorToolButton : public SARibbonToolButton +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonColorToolButton) +public: + /** + * @brief 颜色样式 + */ + enum ColorStyle + { + ColorUnderIcon, ///< 颜色在icon下方,这个要求必须设置icon + ColorFillToIcon ///< 颜色作为icon,这个模式下在setColor会自动生成一个颜色icon替换掉原来的icon,因此setIcon函数没有作用 + }; + +public: + SARibbonColorToolButton(QWidget* parent = Q_NULLPTR); + SARibbonColorToolButton(QAction* defaultAction, QWidget* parent = Q_NULLPTR); + ~SARibbonColorToolButton(); + // 获取颜色 + QColor color() const; + // 设置颜色显示方案 + void setColorStyle(ColorStyle s); + ColorStyle colorStyle() const; + // 建立标准的颜色菜单 + SAColorMenu* setupStandardColorMenu(); +public slots: + // 设置颜色,会发射colorChanged信号 + void setColor(const QColor& c); +private slots: + void onButtonClicked(bool checked = false); +signals: + /** + * @brief 颜色被点击的响应 + * @param color + */ + void colorClicked(const QColor& color, bool checked = false); + /** + * @brief 颜色改变信号 + * @param color + */ + void colorChanged(const QColor& color); + +protected: + void paintIcon(QPainter& p, const QStyleOptionToolButton& opt, const QRect& iconDrawRect); +}; + +#endif // SARIBBONCOLORTOOLBUTTON_H + +/*** End of inlined file: SARibbonColorToolButton.h ***/ + +/*** Start of inlined file: SARibbonLineWidgetContainer.h ***/ +#ifndef SARIBBONLINEWIDGETCONTAINER_H +#define SARIBBONLINEWIDGETCONTAINER_H + +#include +#include +#include + +/** + * @brief 一个窗口容器,把窗口放置中间,前面后面都可以设置文本,主要用于放置在pannel上的小窗口 + * + * 实现如下效果: + * + * PrefixLabel|_Widget_|SuffixLabel + * + */ +class SA_RIBBON_EXPORT SARibbonLineWidgetContainer : public QWidget +{ +public: + SARibbonLineWidgetContainer(QWidget* par = nullptr); + + // 设置widget,不允许设置一个nullptr + void setWidget(QWidget* innerWidget); + + // 设置前缀 + void setPrefix(const QString& str); + + // 设置后缀 + void setSuffix(const QString& str); + + // 前缀文本框 + QLabel* labelPrefix() const; + + // 后缀文本框 + QLabel* labelSuffix() const; + +private: + // 两个文本 + QLabel* m_labelPrefix; + QLabel* m_labelSuffix; + QWidget* m_innerWidget; +}; + +#endif // SARIBBONWIDGETCONTAINER_H + +/*** End of inlined file: SARibbonLineWidgetContainer.h ***/ + +/*** Start of inlined file: SARibbonActionsManager.h ***/ +#ifndef SARIBBONACTIONSMANAGER_H +#define SARIBBONACTIONSMANAGER_H + +#include +#include +#include +#include +#include +#include +class SARibbonBar; +class SARibbonCategory; + +/** + * @brief 用于管理SARibbon的所有Action + * + * SARibbonActionsManager维护着两个表,一个是tag(标签)对应的Action list, + * 一个是所有接受SARibbonActionsManager管理的action list。 + * + * SARibbonActionsManager的标签对应一组actions,每个标签对应的action可以重复出现, + * 但SARibbonActionsManager维护的action list里只有一份action,不会重复出现。 + * + * tag用于对action list分组,每个tag的实体名字通过@ref setTagName 进行设置,在语言变化时需要及时调用 + * setTagName设置新的标签对应的文本。 + * + * SARibbonActionsManager默认预设了6个常用标签见@ref SARibbonActionsManager::ActionTag ,用户自定义标签需要在 + * SARibbonActionsManager::UserDefineActionTag值的基础上进行累加。 + * + * @ref filter (等同@ref actions )函数用于提取标签管理的action list,@ref allActions 函数返回SARibbonActionsManager + * 管理的所有标签。 + * + * 通过@ref autoRegisteActions 函数可以快速的建立action的管理,此函数会遍历@ref SARibbonBar下所有@ref SARibbonPannel 添加的action,并给予Category建立tag,正常使用用户仅需关注此autoRegisteActions函数即可 + * + * + */ +class SA_RIBBON_EXPORT SARibbonActionsManager : public QObject +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManager) + friend class SARibbonActionsManagerModel; + +public: + /** + * @brief 定义action的标签 + */ + enum ActionTag + { + UnknowActionTag = 0, ///< 未知的tag + CommonlyUsedActionTag = 0x01, ///< 预设tag-常用命令 + NotInFunctionalAreaActionTag = 0x02, ///< 预设tag-不在功能区命令 + AutoCategoryDistinguishBeginTag = 0x1000, ///< 自动按Category划分的标签起始,在@ref autoRegisteActions 函数会用到 + AutoCategoryDistinguishEndTag = 0x2000, ///< 自动按Category划分的标签结束,在@ref autoRegisteActions 函数会用到 + NotInRibbonCategoryTag = 0x2001, ///< 不在功能区的标签@ref autoRegisteActions 函数会遍历所有category的action + UserDefineActionTag = 0x8000 ///< 自定义标签,所有用户自定义tag要大于此tag + }; + SARibbonActionsManager(SARibbonBar* bar); + ~SARibbonActionsManager(); + // 设置tag对应的名字 + void setTagName(int tag, const QString& name); + + // 获取tag对应的名字 + QString tagName(int tag) const; + + // 移除tag,注意,这个函数非常耗时 + void removeTag(int tag); + + // 注册action + bool registeAction(QAction* act, int tag, const QString& key = QString(), bool enableEmit = true); + + // 取消action的注册 + void unregisteAction(QAction* act, bool enableEmit = true); + + // 过滤得到actions对应的引用,实际是一个迭代器 + QList< QAction* >& filter(int tag); + + // 通过tag筛选出系列action + QList< QAction* >& actions(int tag); + const QList< QAction* > actions(int tag) const; + + // 获取所有的标签 + QList< int > actionTags() const; + + // 通过key获取action + QAction* action(const QString& key) const; + + // 通过action找到key + QString key(QAction* act) const; + + // 返回所有管理的action数 + int count() const; + + // 返回所有管理的actions + QList< QAction* > allActions() const; + + // 自动加载action,返回tag对应的Category指针 + QMap< int, SARibbonCategory* > autoRegisteActions(SARibbonBar* bar); + + // 自动加载widget下的actions函数返回的action,返回加载的数量,这些 + QSet< QAction* > autoRegisteWidgetActions(QWidget* w, int tag, bool enableEmit = false); + + // 根据标题查找action + QList< QAction* > search(const QString& text); + + // 清除 + void clear(); + +signals: + + /** + * @brief 标签变化触发的信号,变化包括新增和删除 + */ + void actionTagChanged(int tag, bool isdelete); + +private slots: + void onActionDestroyed(QObject* o); + void onCategoryTitleChanged(const QString& title); + +private: + void removeAction(QAction* act, bool enableEmit = true); +}; + +/** + * @brief SARibbonActionsManager 对应的model + */ +class SA_RIBBON_EXPORT SARibbonActionsManagerModel : public QAbstractListModel +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonActionsManagerModel) +public: + explicit SARibbonActionsManagerModel(QObject* p = nullptr); + explicit SARibbonActionsManagerModel(SARibbonActionsManager* m, QObject* p = nullptr); + ~SARibbonActionsManagerModel(); + virtual int rowCount(const QModelIndex& parent) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + virtual QVariant data(const QModelIndex& index, int role) const override; + void setFilter(int tag); + void update(); + void setupActionsManager(SARibbonActionsManager* m); + void uninstallActionsManager(); + QAction* indexToAction(QModelIndex index) const; + void search(const QString& text); + +private slots: + void onActionTagChanged(int tag, bool isdelete); +}; + +#endif // SARIBBONACTIONSMANAGER_H + +/*** End of inlined file: SARibbonActionsManager.h ***/ + +/*** Start of inlined file: SARibbonLineEdit.h ***/ +#ifndef SARIBBONLINEEDIT_H +#define SARIBBONLINEEDIT_H + +#include + +/** + * @brief The SARibbonLineEdit class + */ +class SA_RIBBON_EXPORT SARibbonLineEdit : public QLineEdit +{ + Q_OBJECT +public: + SARibbonLineEdit(QWidget* parent = Q_NULLPTR); + SARibbonLineEdit(const QString& text, QWidget* parent = Q_NULLPTR); +}; + +#endif // SARIBBONLINEEDIT_H + +/*** End of inlined file: SARibbonLineEdit.h ***/ + +/*** Start of inlined file: SARibbonCheckBox.h ***/ +#ifndef SARIBBONCHECKBOX_H +#define SARIBBONCHECKBOX_H + +#include + +/** + * @brief The SARibbonCheckBox class + */ +class SA_RIBBON_EXPORT SARibbonCheckBox : public QCheckBox +{ + Q_OBJECT +public: + SARibbonCheckBox(QWidget* parent = Q_NULLPTR); + SARibbonCheckBox(const QString& text, QWidget* parent = Q_NULLPTR); +}; + +#endif // SARIBBONCHECKBOX_H + +/*** End of inlined file: SARibbonCheckBox.h ***/ + +/*** Start of inlined file: SARibbonComboBox.h ***/ +#ifndef SARIBBONCOMBOBOX_H +#define SARIBBONCOMBOBOX_H + +#include + +/** + * @brief QComboBox的Ribbon显示,可以显示QIcon和windowTitle在左侧 + */ +class SA_RIBBON_EXPORT SARibbonComboBox : public QComboBox +{ + Q_OBJECT +public: + SARibbonComboBox(QWidget* parent = Q_NULLPTR); +}; + +#endif // SARIBBONCOMBOBOX_H + +/*** End of inlined file: SARibbonComboBox.h ***/ + +/*** Start of inlined file: SARibbonButtonGroupWidget.h ***/ +#ifndef SARIBBONBUTTONGROUPWIDGET_H +#define SARIBBONBUTTONGROUPWIDGET_H + +#include +#include +#include +class SARibbonControlButton; +/** + * @brief 用于管理一组Action,类似于QToolBar + */ +class SA_RIBBON_EXPORT SARibbonButtonGroupWidget : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonButtonGroupWidget) +public: + using FpButtonIterate = std::function< bool(SARibbonControlButton*) >; + +public: + SARibbonButtonGroupWidget(QWidget* parent = Q_NULLPTR); + ~SARibbonButtonGroupWidget() Q_DECL_OVERRIDE; + + // 图标尺寸 + void setIconSize(const QSize& ic); + QSize iconSize() const; + // 生成并添加一个action + QAction* addAction(QAction* a, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addAction(const QString& text, + const QIcon& icon, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + QAction* addMenu(QMenu* menu, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + QAction* addSeparator(); + QAction* addWidget(QWidget* w); + // 从ButtonGroupWidget中把action对应的button提取出来,如果action没有对应的button,就返回nullptr + SARibbonControlButton* actionToRibbonControlToolButton(QAction* action); + + QSize sizeHint() const Q_DECL_OVERRIDE; + QSize minimumSizeHint() const Q_DECL_OVERRIDE; + +public: + bool iterate(FpButtonIterate fp); +signals: + + /** + * @brief 参考QToolBar::actionTriggered的信号 + * @param action + */ + void actionTriggered(QAction* action); + +protected: + virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; +}; + +#endif // SARIBBONBUTTONGROUPWIDGET_H + +/*** End of inlined file: SARibbonButtonGroupWidget.h ***/ + +/*** Start of inlined file: SARibbonStackedWidget.h ***/ +#ifndef SARIBBONSTACKEDWIDGET_H +#define SARIBBONSTACKEDWIDGET_H +#include + +class QHideEvent; +class QResizeEvent; + +/** + * @brief 有qdialog功能的stackwidget,用于在最小化时stack能像dialog那样弹出来 + */ +class SA_RIBBON_EXPORT SARibbonStackedWidget : public QStackedWidget +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonStackedWidget) +public: + SARibbonStackedWidget(QWidget* parent); + ~SARibbonStackedWidget(); + void setPopupMode(); + bool isPopupMode() const; + void setNormalMode(); + bool isNormalMode() const; + void exec(); + + // 设置stacked管理的窗口会随着stacked的大小变化而变化大小 + // 就算不激活也调整大小 + void setAutoResize(bool autoresize); + bool isAutoResize() const; + // 移动窗口 + void moveWidget(int from, int to); + +protected: + // void mouseReleaseEvent(QMouseEvent *e); + void hideEvent(QHideEvent* e) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; +signals: + /** + * @brief hidWindow + */ + void hidWindow(); +}; + +#endif // SARIBBONSTACKEDWIDGET_H + +/*** End of inlined file: SARibbonStackedWidget.h ***/ + +/*** Start of inlined file: SARibbonSeparatorWidget.h ***/ +#ifndef SARIBBONSEPARATORWIDGET_H +#define SARIBBONSEPARATORWIDGET_H + +#include +#include + +/// +/// \brief 用于显示分割线 +/// +class SA_RIBBON_EXPORT SARibbonSeparatorWidget : public QFrame +{ + Q_OBJECT +public: + SARibbonSeparatorWidget(QWidget* parent = nullptr); + virtual QSize sizeHint() const override; +}; + +#endif // SARIBBONSEPARATORWIDGET_H + +/*** End of inlined file: SARibbonSeparatorWidget.h ***/ + +/*** Start of inlined file: SARibbonCtrlContainer.h ***/ +#ifndef SARIBBONCTROLCONTAINER_H +#define SARIBBONCTROLCONTAINER_H + +#include +#include +class QStyleOption; + +/** + * @brief 用于装载一个窗体的容器,这个窗体会布满整个SARibbonCtrlContainer,但会预留空间显示icon或者title + * + * ---------------------- + * |icon|text| widget | + * ---------------------- + */ +class SA_RIBBON_EXPORT SARibbonCtrlContainer : public QWidget +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCtrlContainer) +public: + SARibbonCtrlContainer(QWidget* parent = Q_NULLPTR); + ~SARibbonCtrlContainer(); + + void setEnableShowIcon(bool b); + void setEnableShowTitle(bool b); + // 判断是否存在容器窗口 + bool hasContainerWidget() const; + // 图标 + void setIcon(const QIcon& i); + void setIcon(const QPixmap& pixmap); + QIcon icon() const; + // 图标 + void setText(const QString& t); + QString text() const; + // 设置窗口 + void setContainerWidget(QWidget* w); + QWidget* containerWidget(); + const QWidget* containerWidget() const; + // 获取线索icon的窗口 + QWidget* iconWidget() const; +}; + +#endif // SARIBBONCTROLCONTAINER_H + +/*** End of inlined file: SARibbonCtrlContainer.h ***/ + +/*** Start of inlined file: SARibbonQuickAccessBar.h ***/ +#ifndef SARIBBONQUICKACCESSBAR_H +#define SARIBBONQUICKACCESSBAR_H + +#include +#include +class SARibbonToolButton; +class SARibbonButtonGroupWidget; +/// +/// \brief ribbon左上顶部的快速响应栏 +/// +class SA_RIBBON_EXPORT SARibbonQuickAccessBar : public SARibbonCtrlContainer +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonQuickAccessBar) +public: + SARibbonQuickAccessBar(QWidget* parent = 0); + ~SARibbonQuickAccessBar(); + void addSeparator(); + void addAction(QAction* act, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::DelayedPopup); + void addWidget(QWidget* w); + void addMenu(QMenu* m, + Qt::ToolButtonStyle buttonStyle = Qt::ToolButtonIconOnly, + QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + // 获取内部管理的ButtonGroupWidget + SARibbonButtonGroupWidget* buttonGroupWidget(); + const SARibbonButtonGroupWidget* buttonGroupWidget() const; + // 设置图标尺寸 + void setIconSize(const QSize& s); + QSize iconSize() const; +}; + +#endif // SARIBBONQUICKACCESSBAR_H + +/*** End of inlined file: SARibbonQuickAccessBar.h ***/ + +/*** Start of inlined file: SARibbonTabBar.h ***/ +#ifndef SARIBBONTABBAR_H +#define SARIBBONTABBAR_H + +#include +#include + +/** + * @brief The SARibbonTabBar class + */ +class SA_RIBBON_EXPORT SARibbonTabBar : public QTabBar +{ + Q_OBJECT +public: + SARibbonTabBar(QWidget* parent = Q_NULLPTR); + const QMargins& tabMargin() const; + void setTabMargin(const QMargins& tabMargin); + +private: + QMargins m_tabMargin; +}; + +#endif // SARIBBONTABBAR_H + +/*** End of inlined file: SARibbonTabBar.h ***/ + +/*** Start of inlined file: SARibbonControlButton.h ***/ +#ifndef SARIBBONCONTROLBUTTON_H +#define SARIBBONCONTROLBUTTON_H +#include + +/** + * @brief 用于SARibbonGallery的control button + * + * 为了防止外部qss的影响,单独继承一个类 + */ +class SA_RIBBON_EXPORT SARibbonControlButton : public QToolButton +{ + Q_OBJECT +public: + SARibbonControlButton(QWidget* parent = 0); +}; + +/** + * @brief 用于SARibbonButtonGroupWidget的control button + * + * 为了防止SARibbonToolButton qss的影响,单独继承一个类 + */ +class SA_RIBBON_EXPORT SARibbonControlToolButton : public QToolButton +{ + Q_OBJECT +public: + SARibbonControlToolButton(QWidget* parent = 0); +}; + +#endif // SARIBBONPANNELTOOLBUTTON_H + +/*** End of inlined file: SARibbonControlButton.h ***/ + +/*** Start of inlined file: SARibbonMenu.h ***/ +#ifndef SARIBBONMENU_H +#define SARIBBONMENU_H + +#include + +/// +/// \brief 用在ribbon的menu +/// 可以通过StyleSheet设置样式而不影响QMenu +/// +class SA_RIBBON_EXPORT SARibbonMenu : public QMenu +{ + Q_OBJECT +public: + explicit SARibbonMenu(QWidget* parent = Q_NULLPTR); + explicit SARibbonMenu(const QString& title, QWidget* parent = Q_NULLPTR); + QAction* addRibbonMenu(SARibbonMenu* menu); + SARibbonMenu* addRibbonMenu(const QString& title); + SARibbonMenu* addRibbonMenu(const QIcon& icon, const QString& title); + QAction* addWidget(QWidget* w); +}; + +#endif // SARIBBONMENU_H + +/*** End of inlined file: SARibbonMenu.h ***/ + +/*** Start of inlined file: SARibbonPannelOptionButton.h ***/ +#ifndef SARIBBONPANNELOPTIONBUTTON_H +#define SARIBBONPANNELOPTIONBUTTON_H +#include + +class QAction; + +/** + * @brief Pannel右下角的操作按钮 + * + * 此按钮和一个action关联,使用@ref SARibbonPannel::addOptionAction 函数用于生成此按钮,正常来说 + * 用户并不需要直接操作此类,仅仅用于样式设计 + * 如果一定要重载此按钮,可以通过重载@ref SARibbonElementFactory + * 的 @ref SARibbonElementFactory::createRibbonPannelOptionButton来实现新的OptionButton + * + */ +class SA_RIBBON_EXPORT SARibbonPannelOptionButton : public QToolButton +{ + Q_OBJECT +public: + SARibbonPannelOptionButton(QWidget* parent = Q_NULLPTR); +}; + +#endif // SAROBBONPANNELOPTIONBUTTON_H + +/*** End of inlined file: SARibbonPannelOptionButton.h ***/ + +/*** Start of inlined file: SARibbonPannelItem.h ***/ +#ifndef SARIBBONPANNELITEM_H +#define SARIBBONPANNELITEM_H + +#include +#include +class SARibbonToolButton; +/** + * @brief 是对pannel所有子窗口的抽象,参考qt的toolbar + * + * 参考qt的toolbar,pannel所有子窗口内容都通过QAction进行抽象,包括gallery这些窗口,也是通过QAction进行抽象 + * QAction最终会转换为SARibbonPannelItem,每个SARibbonPannelItem都含有一个widget,SARibbonPannel的布局 + * 就基于SARibbonPannelItem + * + * 无窗口的action会在内部生成一个SARibbonToolButton, + */ +class SA_RIBBON_EXPORT SARibbonPannelItem : public QWidgetItem +{ +public: + /** + * @brief 定义了行的占比,ribbon中有large,media和small三种占比 + */ + enum RowProportion + { + None, ///< 为定义占比,这时候将会依据expandingDirections来判断,如果能有Qt::Vertical,就等同于Large,否则就是Small + Large, ///< 大占比,一个widget的高度会充满整个pannel + Medium, ///< 中占比,在@ref SARibbonPannel::pannelLayoutMode 为 @ref SARibbonPannel::ThreeRowMode 时才会起作用,且要同一列里两个都是Medium时,会在三行中占据两行 + Small ///< 小占比,占SARibbonPannel的一行,Medium在不满足条件时也会变为Small,但不会变为Large + }; + SARibbonPannelItem(QWidget* widget); + bool isEmpty() const Q_DECL_OVERRIDE; + + short rowIndex; ///< 记录当前item属于第几行,hide模式下为-1 + int columnIndex; ///< 记录当前item属于第几列,hide模式下为-1 + QRect itemWillSetGeometry; ///< 在调用SARibbonPannelLayout::updateGeomArray会更新这个此处,实际设置的时候会QWidgetItem::setGeometry设置Geometry + QAction* action; /// < 记录action,参考QToolBarLayoutItem + bool customWidget; ///< 对于没有窗口的action,实际也会有一个SARibbonToolButton,在销毁时要delete掉 + SARibbonPannelItem::RowProportion rowProportion; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion +}; +#ifndef SA_ActionPropertyName_RowProportion +#define SA_ActionPropertyName_RowProportion "_sa_RowProportion" +#endif +#ifndef SA_ActionPropertyName_ToolButtonPopupMode +#define SA_ActionPropertyName_ToolButtonPopupMode "_sa_ToolButtonPopupMode" +#endif +#ifndef SA_ActionPropertyName_ToolButtonStyle +#define SA_ActionPropertyName_ToolButtonStyle "_sa_ToolButtonStyle" +#endif +#endif // SARIBBONPANNELITEM_H + +/*** End of inlined file: SARibbonPannelItem.h ***/ + +/*** Start of inlined file: SARibbonPannelLayout.h ***/ +#ifndef SARIBBONPANNELLAYOUT_H +#define SARIBBONPANNELLAYOUT_H + +#include + +class QToolButton; +class SARibbonPannel; +class SARibbonPannelLabel; +/** + * @brief 针对SARibbonPannel的布局 + * + * SARibbonPannelLayout实际是一个列布局,每一列有2~3行,看窗口定占几行 + * + * 核心函数: @ref SARibbonPannelLayout::createItem + * + * @note QLayout::contentsMargins 函数不会启作用,如果要设置contentsMargins,使用@sa setPannelContentsMargins + */ +class SA_RIBBON_EXPORT SARibbonPannelLayout : public QLayout +{ + Q_OBJECT + friend class SARibbonPannel; + +public: + SARibbonPannelLayout(QWidget* p = 0); + ~SARibbonPannelLayout(); + // SARibbonPannelLayout additem 无效 + void addItem(QLayoutItem* item) Q_DECL_OVERRIDE; + // QLayout 所必须的重载函数 + QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE; + QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE; + int count() const Q_DECL_OVERRIDE; + bool isEmpty() const Q_DECL_OVERRIDE; + void invalidate() Q_DECL_OVERRIDE; + Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; + void setGeometry(const QRect& rect) Q_DECL_OVERRIDE; + QSize minimumSize() const Q_DECL_OVERRIDE; + QSize sizeHint() const Q_DECL_OVERRIDE; + + // 获取ribbonpannel + SARibbonPannel* ribbonPannel() const; + // SARibbonPannel主要通过此函数来添加action + void insertAction(int index, QAction* act, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); + // 设置OptionAction,如果要去除,传入nullptr指针即可 + void setOptionAction(QAction* action); + bool isHaveOptionAction() const; + // 通过action获取SARibbonPannelItem + SARibbonPannelItem* pannelItem(QAction* action) const; + // 获取最后一个添加的item + SARibbonPannelItem* lastItem() const; + // 获取最后生成的窗口 + QWidget* lastWidget() const; + // 移动两个item + void move(int from, int to); + // 判断是否需要重新布局 + bool isDirty() const; + // 更新尺寸 + void updateGeomArray(); + // 通过action获取SARibbonPannelItem的索引 + int indexByAction(QAction* action) const; + // 标题高度 + int pannelTitleHeight() const; + void setPannelTitleHeight(int newTitleHeight); + // 判断是否存在标题 + bool isEnableShowPannelTitle() const; + void setEnableShowPannelTitle(bool on); + // 返回大按钮的高度 + int largeButtonHeight() const; + // 标题区域和按钮的间隔 + int pannelTitleSpace() const; + void setPannelTitleSpace(int newTitleSpace); + // pannel 标题的label + SARibbonPannelLabel* pannelTitleLabel() const; + +protected: + // 获取optionAction 按钮尺寸 + QSize optionActionButtonSize() const; + // 布局action + void doLayout(); + // 把action转换为item,对于纯Action,此函数会创建SARibbonToolButton, + // rp用于告诉Layout生成什么样的窗口,详细见SARibbonPannelItem::RowProportion + SARibbonPannelItem* createItem(QAction* action, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::None); + void updateGeomArray(const QRect& setrect); + // 重新计算扩展条码,此函数必须在updateGeomArray函数之后调用 + void recalcExpandGeomArray(const QRect& setrect); + // 返回optionActionButton的尺寸 + +private: + // 根据列数,计算窗口的宽度,以及最大宽度 + void columnWidthInfo(int colindex, int& width, int& maximum) const; + // 设置titlelabel + void setPannelTitleLabel(SARibbonPannelLabel* newTitleLabel); + +private: + QList< SARibbonPannelItem* > m_items; + int m_columnCount { 0 }; ///< 记录有多少列 + bool m_expandFlag { false }; ///< 标记是否是会扩展的 + QSize m_sizeHint; ///< sizeHint返回的尺寸 + bool m_dirty { true }; ///< 用于标记是否需要刷新元素,参考QToolBarLayout源码 + int m_largeHeight { 0 }; ///< 记录大图标的高度 + int m_titleHeight { 15 }; ///< 标题区域高度 + int m_titleSpace { 2 }; ///< 标题区域和按钮的间隔 + bool m_enableShowTitle { true }; ///< 是否运行显示pannel标题 + SARibbonPannelLabel* m_titleLabel { nullptr }; ///< titlelabel指针 + QRect m_titleLabelGeometry; ///< titlelabel的位置 + QToolButton* m_optionActionBtn { nullptr }; ///< optionAction对应的button + QRect m_optionActionBtnGeometry; ///< optionAction的位置 +}; + +#endif // SARIBBONPANNELLAYOUT_H + +/*** End of inlined file: SARibbonPannelLayout.h ***/ + +/*** Start of inlined file: SARibbonPannel.h ***/ +#ifndef SARIBBONPANNEL_H +#define SARIBBONPANNEL_H + +#include +#include +#include +class SARibbonMenu; +class SARibbonGallery; +class QGridLayout; +class SARibbonPannelOptionButton; +class SARibbonPannelLayout; +class SARibbonCategory; +class SARibbonBar; + +/** + * @brief SARibbonPannel的标题label,此类用于qss + */ +class SA_RIBBON_EXPORT SARibbonPannelLabel : public QLabel +{ + Q_OBJECT +public: + SARibbonPannelLabel(QWidget* parent = nullptr); +}; + +/** + * @brief pannel页窗口,pannel是ribbon的面板用于承放控件 + * + * ribbon的pannel分为两行模式和三行模式,以office为代表的ribbon为3行模式,以WPS为代表的“紧凑派”就是2行模式, + * SARibbon可通过SARibbonBar的@ref SARibbonBar::RibbonStyle 来指定模式(通过函数@ref SARibbonBar::setRibbonStyle ) + * + * 在pannel中,可以通过@ref setExpanding 函数指定pannel水平扩展,如果pannel里面没有能水平扩展的控件,将会留白,因此, + * 建议在pannel里面有水平扩展的控件如(@ref SARibbonGallery )才指定这个函数 + * + * pannel的布局通过@ref SARibbonPannelLayout 来实现,如果有其他布局,可以通过继承@ref + * SARibbonElementCreateDelegate::createRibbonPannel 函数返回带有自己布局的pannel,但你必须继承对应的虚函数 + */ +class SA_RIBBON_EXPORT SARibbonPannel : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonPannel) + friend class SARibbonCategory; + friend class SARibbonCategoryPrivate; + friend class SARibbonCustomizeWidgetPrivate; + friend class SARibbonPannelLayout; + Q_PROPERTY(bool isCanCustomize READ isCanCustomize WRITE setCanCustomize) + Q_PROPERTY(bool isExpanding READ isExpanding WRITE setExpanding) + Q_PROPERTY(QString pannelName READ pannelName WRITE setPannelName) +public: + enum PannelLayoutMode + { + ThreeRowMode, ///< 三行布局模式,office就是三行布局模式,pannel能布置3行小toolbutton,默认模式 + TwoRowMode ///< 两行布局模式,wps的后续布局模式就是两行布局模式,pannel能布置2行小toolbutton + }; + Q_ENUM(PannelLayoutMode) +public: + SARibbonPannel(QWidget* parent = nullptr); + SARibbonPannel(const QString& name, QWidget* parent = nullptr); + ~SARibbonPannel() Q_DECL_OVERRIDE; + using QWidget::addAction; + + // 把action加入到pannel + void addAction(QAction* action, SARibbonPannelItem::RowProportion rp); + // 生成并添加一个action + void addAction(QAction* act, QToolButton::ToolButtonPopupMode popMode, SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); + // 把action加入到pannel,并以大图标显示 + void addLargeAction(QAction* action); + // 把action加入到pannel,在三行模式下会以中图标显示 + void addMediumAction(QAction* action); + // 把action加入到pannel,并以小图标显示 + void addSmallAction(QAction* action); + + // 把action加入到pannel,并以小图标显示 + void addSmallAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); + // 把action加入到pannel,并以大图标显示 + void addLargeAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); + // 把action加入到pannel,在三行模式下会以中图标显示 + void addMediumAction(QAction* action, QToolButton::ToolButtonPopupMode popMode); + + QAction* addAction(const QString& text, + const QIcon& icon, + QToolButton::ToolButtonPopupMode popMode, + SARibbonPannelItem::RowProportion rp = SARibbonPannelItem::Large); + + // 添加menu + void addMenu(QMenu* menu, SARibbonPannelItem::RowProportion rp, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + + // 添加普通大菜单 + void addLargeMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + + // 添加普通小按钮菜单 + void addSmallMenu(QMenu* menu, QToolButton::ToolButtonPopupMode popMode = QToolButton::InstantPopup); + + // 添加窗口 + QAction* addWidget(QWidget* w, SARibbonPannelItem::RowProportion rp); + + // 添加窗口,占用ribbon的一行 + QAction* addSmallWidget(QWidget* w); + + // 添加窗口,占用ribbon的一行 + QAction* addMediumWidget(QWidget* w); + + // 添加窗口,占用所有行 + QAction* addLargeWidget(QWidget* w); + + // 添加一个Gallery + SARibbonGallery* addGallery(bool expanding = true); + + // 添加分割线 + QAction* addSeparator(); + + // 从pannel中把action对应的button提取出来,如果action没有对应的button,就返回nullptr + SARibbonToolButton* actionToRibbonToolButton(QAction* action); + + // 设置操作action,如果要去除,传入nullptr指针即可,SARibbonPannel不会对QAction的所有权进行管理 + // OptionAction也会触发actionTriggered信号 + void setOptionAction(QAction* action); + + // 判断是否存在OptionAction + bool isHaveOptionAction() const; + + // 获取所有的buttons + QList< SARibbonToolButton* > ribbonToolButtons() const; + + // 获取PannelLayoutMode + PannelLayoutMode pannelLayoutMode() const; + void setPannelLayoutMode(PannelLayoutMode mode); + // 更新按钮的尺寸,这个函数在pannel的布局状态变换后需要调用刷新 + void resetToolButtonSize(); + + // 判断是否为2行模式 + bool isTwoRow() const + { + return (TwoRowMode == pannelLayoutMode()); + } + + // 把pannel设置为扩展模式,此时会撑大水平区域 + void setExpanding(bool isExpanding = true); + + // 是否是扩展模式 + bool isExpanding() const; + + // 标题栏高度 + void setTitleHeight(int h); + int titleHeight() const; + + // 是否显示标题,显示标题后,标题的高度需要设置,默认高度为15 + bool isEnableShowTitle() const; + void setEnableShowTitle(bool on); + + // action对应的布局index,此操作一般用于移动moveAction,其他意义不大 + int actionIndex(QAction* act) const; + + // 移动action + void moveAction(int from, int to); + + // 判断是否可以自定义 + bool isCanCustomize() const; + void setCanCustomize(bool b); + + // 标题 + QString pannelName() const; + void setPannelName(const QString& title); + + // 大图标的高度 + int largeButtonHeight() const; + // 获取布局对应的item,此函数目前仅仅在自定义过程中用到 + const QList< SARibbonPannelItem* >& ribbonPannelItem() const; + // 获取pannel layout + SARibbonPannelLayout* pannelLayout() const; + // 更新布局 + void updateItemGeometry(); + // 获取category指针,如果没有parent,或者不在category管理,返回nullptr + SARibbonCategory* category() const; + // 获取ribbonBar指针,如果没有返回nullptr + SARibbonBar* ribbonBar() const; + // + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE; +signals: + + /** + * @brief 等同于QToolBar::actionTriggered + * @param action + */ + void actionTriggered(QAction* action); + + /** + * @brief pannel的标题发生了改变 + * @param n + */ + void pannelNameChanged(const QString& n); + +public: + // pannel高度推荐 + static int pannelHeightHint(const QFontMetrics& fm, PannelLayoutMode layMode, int pannelTitleHeight); + // 把action的行属性设置进action中,action自身携带了行属性 + static void setActionRowProportionProperty(QAction* action, SARibbonPannelItem::RowProportion rp); + // 获取action的行属性 + static SARibbonPannelItem::RowProportion getActionRowProportionProperty(QAction* action); + // 把action的PopupMode属性设置进action中,action自身携带了PopupMode属性 + static void setActionToolButtonPopupModeProperty(QAction* action, QToolButton::ToolButtonPopupMode popMode); + // 获取action的PopupMode属性 + static QToolButton::ToolButtonPopupMode getActionToolButtonPopupModeProperty(QAction* action); + // 把action的ToolButtonStyle属性设置进action中,action自身携带了ToolButtonStyle属性 + static void setActionToolButtonStyleProperty(QAction* action, Qt::ToolButtonStyle buttonStyle); + // 获取action的ToolButtonStyle属性 + static Qt::ToolButtonStyle getActionToolButtonStyleProperty(QAction* action); + +protected: + virtual bool event(QEvent* e) Q_DECL_OVERRIDE; + virtual void actionEvent(QActionEvent* e) Q_DECL_OVERRIDE; + virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; +}; + +#endif // SARIBBONPANNEL_H + +/*** End of inlined file: SARibbonPannel.h ***/ + +/*** Start of inlined file: SARibbonCategory.h ***/ +#ifndef SARIBBONCATEGORY_H +#define SARIBBONCATEGORY_H + +#include + +#include +#include +#include + +class QHBoxLayout; +class QWheelEvent; +class SARibbonBar; +class SARibbonCategoryLayout; +/** + * @brief 一项ribbon tab页 + * @note SARibbonCategory的windowTitle影响了其在SARibbonBar的标签显示, + * 如果要改标签名字,直接调用SARibbonCategory的setWindowTitle函数 + */ +class SA_RIBBON_EXPORT SARibbonCategory : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCategory) + friend class SARibbonBar; + friend class SARibbonContextCategory; + Q_PROPERTY(bool isCanCustomize READ isCanCustomize WRITE setCanCustomize) + Q_PROPERTY(QString categoryName READ categoryName WRITE setCategoryName) +public: + using FpPannelIterate = std::function< bool(SARibbonPannel*) >; + +public: + SARibbonCategory(QWidget* p = nullptr); + SARibbonCategory(const QString& name, QWidget* p = nullptr); + ~SARibbonCategory(); + + // category的名字 + QString categoryName() const; + + // 设置category名字,等同setWindowTitle + void setCategoryName(const QString& title); + + // 设置pannel的模式 + SARibbonPannel::PannelLayoutMode pannelLayoutMode() const; + void setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m); + + // 添加pannel + SARibbonPannel* addPannel(const QString& title); + + // 添加pannel + void addPannel(SARibbonPannel* pannel); + + // qt designer专用 + Q_INVOKABLE void addPannel(QWidget* pannel); + + // 插入pannel + SARibbonPannel* insertPannel(const QString& title, int index); + + // 通过名字查找pannel + SARibbonPannel* pannelByName(const QString& title) const; + + // 通过ObjectName查找pannel + SARibbonPannel* pannelByObjectName(const QString& objname) const; + + // 通过索引找到pannel,如果超过索引范围,会返回nullptr + SARibbonPannel* pannelByIndex(int index) const; + + // 查找pannel的index + int pannelIndex(SARibbonPannel* p) const; + + // 移动一个Pannel从from index到to index + void movePannel(int from, int to); + + // 把pannel从Category中移除,不会销毁,此时pannel的所有权归还操作者 + bool takePannel(SARibbonPannel* pannel); + + // 移除Pannel,Category会直接回收SARibbonPannel内存 + bool removePannel(SARibbonPannel* pannel); + bool removePannel(int index); + + // 返回所有的Pannel + QList< SARibbonPannel* > pannelList() const; + + // + QSize sizeHint() const Q_DECL_OVERRIDE; + + // 如果是ContextCategory,此函数返回true + bool isContextCategory() const; + + // pannel的个数 + int pannelCount() const; + + // 判断是否可以自定义 + bool isCanCustomize() const; + void setCanCustomize(bool b); + + // 设置pannel的标题栏高度 + int pannelTitleHeight() const; + void setPannelTitleHeight(int h); + + // 设置pannel是否显示标题栏 + bool isEnableShowPannelTitle() const; + void setEnableShowPannelTitle(bool on); + + // 设置Category的对齐方式 + void setCategoryAlignment(SARibbonAlignment al); + SARibbonAlignment categoryAlignment() const; + + // 获取对应的ribbonbar,如果没有加入ribbonbar的管理,此值为null + SARibbonBar* ribbonBar() const; + + // 刷新category的尺寸布局 + void updateItemGeometry(); + + // 此函数会遍历Category下的所有pannel,执行函数指针,函数指针返回false则停止迭代 + bool iterate(FpPannelIterate fp); +signals: + /** + * @brief category标题发生了改变信号 + * @param n + */ + void categoryNameChanged(const QString& n); + +protected: + virtual bool event(QEvent* e) Q_DECL_OVERRIDE; + // 处理滚轮事件 + void wheelEvent(QWheelEvent* event) Q_DECL_OVERRIDE; + // + void changeEvent(QEvent* event) Q_DECL_OVERRIDE; + + // 标记这个是上下文标签 + void markIsContextCategory(bool isContextCategory = true); + + // 获取SARibbonCategoryLayoutlayout + SARibbonCategoryLayout* categoryLayout() const; +}; + +/** + * @brief SARibbonCategory无法完全显示时,显示的调整按钮 + * + * 重新定义是为了防止被外部的样式影响,同时可以使用SARibbonCategoryScrollButton的样式定义 + */ +class SA_RIBBON_EXPORT SARibbonCategoryScrollButton : public QToolButton +{ + Q_OBJECT +public: + SARibbonCategoryScrollButton(Qt::ArrowType arr, QWidget* p = nullptr); +}; + +#endif // SARIBBONCATEGORY_H + +/*** End of inlined file: SARibbonCategory.h ***/ + +/*** Start of inlined file: SARibbonCategoryLayout.h ***/ +#ifndef SARIBBONCATEGORYLAYOUT_H +#define SARIBBONCATEGORYLAYOUT_H + +#include +#include +#include + +class SARibbonPannel; +class SARibbonCategoryLayoutItem; +class SARibbonSeparatorWidget; + +/** + * @brief The SARibbonCategoryLayout class + */ +class SA_RIBBON_EXPORT SARibbonCategoryLayout : public QLayout +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCategoryLayout) +public: + SARibbonCategoryLayout(SARibbonCategory* parent); + ~SARibbonCategoryLayout(); + + SARibbonCategory* ribbonCategory() const; + + virtual void addItem(QLayoutItem* item) Q_DECL_OVERRIDE; + virtual QLayoutItem* itemAt(int index) const Q_DECL_OVERRIDE; + virtual QLayoutItem* takeAt(int index) Q_DECL_OVERRIDE; + SARibbonCategoryLayoutItem* takePannelItem(int index); + SARibbonCategoryLayoutItem* takePannelItem(SARibbonPannel* pannel); + bool takePannel(SARibbonPannel* pannel); + virtual int count() const Q_DECL_OVERRIDE; + void setGeometry(const QRect& rect) Q_DECL_OVERRIDE; + QSize sizeHint() const Q_DECL_OVERRIDE; + QSize minimumSize() const Q_DECL_OVERRIDE; + Qt::Orientations expandingDirections() const Q_DECL_OVERRIDE; + void invalidate() Q_DECL_OVERRIDE; + // 追加一个pannel + void addPannel(SARibbonPannel* pannel); + // 插入一个pannel + void insertPannel(int index, SARibbonPannel* pannel); + // + QSize categoryContentSize() const; + // 更新尺寸 + void updateGeometryArr(); + + // 执行位置调整 + void doLayout(); + + // 返回所有pannels + QList< SARibbonPannel* > pannels() const; + + // 通过obj name获取pannel + SARibbonPannel* pannelByObjectName(const QString& objname) const; + // 通过pannel name获取pannel + SARibbonPannel* pannelByName(const QString& pannelname) const; + // 通过索引找到pannel,如果超过索引范围,会返回nullptr + SARibbonPannel* pannelByIndex(int i) const; + // 移动pannel + void movePannel(int from, int to); + // pannel的数量 + int pannelCount() const; + // pannel的索引 + int pannelIndex(SARibbonPannel* p) const; + // 获取所有的pannel + QList< SARibbonPannel* > pannelList() const; + // 执行滚轮事件 + void scroll(int px); + // 判断是否有滚动过 + bool isScrolled() const; + // category的总宽度 + int categoryTotalWidth() const; + // 设置Category的对齐方式 + void setCategoryAlignment(SARibbonAlignment al); + SARibbonAlignment categoryAlignment() const; +private slots: + void onLeftScrollButtonClicked(); + void onRightScrollButtonClicked(); +}; + +/** + * @brief SARibbonCategoryLayoutItem,用于标识SARibbonCategoryLayout的item + */ +class SA_RIBBON_EXPORT SARibbonCategoryLayoutItem : public QWidgetItem +{ +public: + SARibbonCategoryLayoutItem(SARibbonPannel* w); + SARibbonSeparatorWidget* separatorWidget; + // 把内部的widget转换为pannel + SARibbonPannel* toPannelWidget(); + QRect mWillSetGeometry; ///< pannel将要设置的Geometry + QRect mWillSetSeparatorGeometry; ///< pannel将要设置的Separator的Geometry +}; +#endif // SARIBBONCATEGORYLAYOUT_H + +/*** End of inlined file: SARibbonCategoryLayout.h ***/ + +/*** Start of inlined file: SARibbonContextCategory.h ***/ +#ifndef SARIBBONCONTEXTCATEGORY_H +#define SARIBBONCONTEXTCATEGORY_H + +#include + +/** + * @brief 管理上下文标签的类 + */ +class SA_RIBBON_EXPORT SARibbonContextCategory : public QObject +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonContextCategory) +public: + SARibbonContextCategory(QWidget* parent = 0); + ~SARibbonContextCategory(); + // 上下文目录添加下属目录 + SARibbonCategory* addCategoryPage(const QString& title); + void addCategoryPage(SARibbonCategory* category); + // 获取上下文标签下管理的标签个数 + int categoryCount() const; + + // 设置id + void setId(const QVariant& id); + QVariant id() const; + + // 设置上下文颜色 + void setContextColor(const QColor color); + QColor contextColor() const; + + // 上下文标签的内容 + QString contextTitle() const; + void setContextTitle(const QString& contextTitle); + + // 获取对应的tab页 + SARibbonCategory* categoryPage(int index); + + // 获取所有的SARibbonCategory* + QList< SARibbonCategory* > categoryList() const; + + // 移除category + bool takeCategory(SARibbonCategory* category); + + // 判断上下文是否维护了此SARibbonCategory + bool isHaveCategory(SARibbonCategory* category) const; +signals: + /** + * @brief 标签加入上下文 + * @param category + */ + void categoryPageAdded(SARibbonCategory* category); + + /** + * @brief 标签从上下文移除 + * @param category + */ + void categoryPageRemoved(SARibbonCategory* category); + + /** + * @brief 上下文的标题发生改变 + * @param title + */ + void contextTitleChanged(const QString& title); + + /** + * @brief 上下文标签维护的标签页名字发生了改变 + * @param category 发生改变的上下文标签页 + * @param title 新名字 + */ + void categoryTitleChanged(SARibbonCategory* category, const QString& title); +private slots: + void onCategoryTitleChanged(const QString& title); + +protected: + // 获取父级窗口 + QWidget* parentWidget() const; + virtual bool eventFilter(QObject* watched, QEvent* e) override; +}; + +#endif // SARIBBONCONTEXTCATEGORY_H + +/*** End of inlined file: SARibbonContextCategory.h ***/ + +/*** Start of inlined file: SARibbonGalleryItem.h ***/ +#ifndef SARIBBONGALLERYITEM_H +#define SARIBBONGALLERYITEM_H + +#include +#include +#include +#include +class SARibbonGalleryGroup; + +/// +/// \brief 类似QStandardItem的GalleryItem +/// +class SA_RIBBON_EXPORT SARibbonGalleryItem +{ +public: + SARibbonGalleryItem(); + SARibbonGalleryItem(const QString& text, const QIcon& icon); + SARibbonGalleryItem(QAction* act); + virtual ~SARibbonGalleryItem(); + // 设置角色 + void setData(int role, const QVariant& data); + virtual QVariant data(int role) const; + + // 设置文字描述 + void setText(const QString& text); + QString text() const; + + // 设置tooltip + void setToolTip(const QString& text); + QString toolTip() const; + + // 设置图标 + void setIcon(const QIcon& ico); + QIcon icon() const; + + // 设置是否可见 + bool isSelectable() const; + void setSelectable(bool isSelectable); + + // 设置是否可选 + bool isEnable() const; + void setEnable(bool isEnable); + + // 设置item的flag + void setFlags(Qt::ItemFlags flag); + virtual Qt::ItemFlags flags() const; + + // 设置action + void setAction(QAction* act); + QAction* action(); + + // 文字对齐方式 + void setTextAlignment(Qt::Alignment a); + Qt::Alignment textAlignment() const; + +private: + friend class SARibbonGalleryGroupModel; + QMap< int, QVariant > m_datas; + Qt::ItemFlags m_flags; + QAction* m_action; +}; + +#endif // SARIBBONGALLERYITEM_H + +/*** End of inlined file: SARibbonGalleryItem.h ***/ + +/*** Start of inlined file: SARibbonGalleryGroup.h ***/ +#ifndef SARIBBONGALLERYGROUP_H +#define SARIBBONGALLERYGROUP_H + +#include +#include +#include + +/// +/// \brief SARibbonGalleryGroup对应的显示代理 +/// +class SA_RIBBON_EXPORT SARibbonGalleryGroupItemDelegate : public QStyledItemDelegate +{ +public: + SARibbonGalleryGroupItemDelegate(SARibbonGalleryGroup* group, QObject* parent = Q_NULLPTR); + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const Q_DECL_OVERRIDE; + + virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const Q_DECL_OVERRIDE; + virtual void paintIconOnly(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual void paintIconWithText(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + virtual void paintIconWithTextWordWrap(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + +private: + SARibbonGalleryGroup* m_group; +}; + +/// +/// \brief SARibbonGalleryGroup对应的model +/// +class SA_RIBBON_EXPORT SARibbonGalleryGroupModel : public QAbstractListModel +{ + Q_OBJECT +public: + SARibbonGalleryGroupModel(QObject* parent = Q_NULLPTR); + ~SARibbonGalleryGroupModel(); + virtual int rowCount(const QModelIndex& parent) const Q_DECL_OVERRIDE; + virtual Qt::ItemFlags flags(const QModelIndex& index) const Q_DECL_OVERRIDE; + virtual QVariant data(const QModelIndex& index, int role) const Q_DECL_OVERRIDE; + virtual QModelIndex index(int row, int column, const QModelIndex& parent) const Q_DECL_OVERRIDE; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role) Q_DECL_OVERRIDE; + void clear(); + SARibbonGalleryItem* at(int row) const; + void insert(int row, SARibbonGalleryItem* item); + SARibbonGalleryItem* take(int row); + void append(SARibbonGalleryItem* item); + +private: + QList< SARibbonGalleryItem* > m_items; +}; + +/** + * @brief Gallery的组 + * + * 组负责显示管理Gallery Item + */ +class SA_RIBBON_EXPORT SARibbonGalleryGroup : public QListView +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonGalleryGroup) +public: + /** + * @brief GalleryGroup显示的样式 + */ + enum GalleryGroupStyle + { + IconWithText, ///< 图标带文字 + IconWithWordWrapText, ///< 图标带文字,文字会换行显示,此模式只会对DisplayOneRow生效,如果不是DisplayOneRow,等同IconWithText + IconOnly ///< 只有图标 + }; + + /** + * @brief 定义Gallery在一个pannel下面显示的图标行数 + */ + enum DisplayRow + { + DisplayOneRow = 1, ///< 显示1行,默认 + DisplayTwoRow = 2, + DisplayThreeRow = 3 + }; + + SARibbonGalleryGroup(QWidget* w = 0); + + virtual ~SARibbonGalleryGroup(); + // 重新计算grid尺寸 + void setRecalcGridSizeBlock(bool on = true); + bool isRecalcGridSizeBlock() const; + void recalcGridSize(); + void recalcGridSize(int galleryHeight); + // 设置显示的样式 + void setGalleryGroupStyle(GalleryGroupStyle style); + GalleryGroupStyle galleryGroupStyle() const; + // 添加一个item + void addItem(const QString& text, const QIcon& icon); + void addItem(SARibbonGalleryItem* item); + // 以一个aciton作为item添加 + void addActionItem(QAction* act); + void addActionItemList(const QList< QAction* >& acts); + + // 构建一个model,这个model的父类是SARibbonGalleryGroup,如果要共享model,需要手动处理model的父类 + void setupGroupModel(); + SARibbonGalleryGroupModel* groupModel(); + // 标题 + void setGroupTitle(const QString& title); + QString groupTitle() const; + void selectByIndex(int i); + // 设置显示的行数 + void setDisplayRow(DisplayRow r); + DisplayRow displayRow() const; + // 设置grid最小的宽度,默认为0(不限制),可以限定grid的宽度 + void setGridMinimumWidth(int w); + int gridMinimumWidth() const; + // 设置grid最大的宽度,默认为0(不限制),可以限定grid的宽度 + void setGridMaximumWidth(int w); + int gridMaximumWidth() const; + // 获取SARibbonGalleryGroup管理的actiongroup + QActionGroup* actionGroup() const; +private slots: + void onItemClicked(const QModelIndex& index); + void onItemEntered(const QModelIndex& index); +signals: + void groupTitleChanged(const QString& title); + /** + * @brief 等同QActionGroup的triggered + * 所有加入SARibbonGalleryGroup的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup + * @param action + */ + void triggered(QAction* action); + /** + * @brief 等同QActionGroup的triggered + * 所有加入SARibbonGalleryGroup的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup + * @note 此属性需要通过QAbstractItemView::entered(const QModelIndex &index)激活,因此要保证设置了setMouseTracking(true) + * @param action + */ + void hovered(QAction* action); +}; + +#endif // SARIBBONGALLERYGROUP_H + +/*** End of inlined file: SARibbonGalleryGroup.h ***/ + +/*** Start of inlined file: SARibbonGallery.h ***/ +#ifndef SARIBBONGALLERY_H +#define SARIBBONGALLERY_H + +#include +#include + +#include +class QLabel; +class QVBoxLayout; +class SARibbonGalleryViewport; + +/** + * @brief 针对SARibbonGallery控件的按钮 + */ +class SA_RIBBON_EXPORT SARibbonGalleryButton : public QToolButton +{ + Q_OBJECT +public: + SARibbonGalleryButton(QWidget* parent = 0); +}; + +/** + * @brief Gallery控件 + * + * Gallery控件是由一个当前激活的@sa SARibbonGalleryGroup 和弹出的 @sa SARibbonGalleryViewport 组成 + * + * @sa SARibbonGalleryGroup 是继承@sa QListView actions通过icon展示出来,相关的属性可以按照QListView设置 + * + * @sa SARibbonGalleryViewport 是一个内部有垂直布局的窗体,在弹出激活时,把管理的SARibbonGalleryGroup都展示出来 + * + * 示例如下: + * @code + * SARibbonGallery* gallery = pannel1->addGallery(); + * QList< QAction* > galleryActions; + * ...create many actions ... + * SARibbonGalleryGroup* group1 = gallery->addCategoryActions(tr("Files"), galleryActions); + * galleryActions.clear(); + * ...create many actions ... + * gallery->addCategoryActions(tr("Apps"), galleryActions); + * gallery->setCurrentViewGroup(group1); + * @endcode + */ +class SA_RIBBON_EXPORT SARibbonGallery : public QFrame +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonGallery) +public: + SARibbonGallery(QWidget* parent = 0); + virtual ~SARibbonGallery(); + virtual QSize sizeHint() const Q_DECL_OVERRIDE; + // 添加一个GalleryGroup + SARibbonGalleryGroup* addGalleryGroup(); + // 添加一个GalleryGroup + void addGalleryGroup(SARibbonGalleryGroup* group); + // 快速添加一组actions + SARibbonGalleryGroup* addCategoryActions(const QString& title, QList< QAction* > actions); + // 设置当前显示的SARibbonGalleryGroup + void setCurrentViewGroup(SARibbonGalleryGroup* group); + // 获取当前显示的SARibbonGalleryGroup + SARibbonGalleryGroup* currentViewGroup() const; + // 获取弹出窗口指针 + SARibbonGalleryViewport* getPopupViewPort() const; + +public: + // 设置最右边三个控制按钮的最大宽度(默认15) + static void setGalleryButtonMaximumWidth(int w); +signals: + /** + * @brief 转发管理的SARibbonGalleryGroup::triggered + * 所有加入SARibbonGallery的action都会被一个QActionGroup管理,可以通过@sa getActionGroup 获取到对应的actiongroup + * @param action + */ + void triggered(QAction* action); + /** + * @brief 转发管理的SARibbonGalleryGroup::hovered + * @note 此属性需要确保SARibbonGalleryGroup::setMouseTracking(true) + * @param action + */ + void hovered(QAction* action); + +public slots: + // 上翻页 + virtual void pageUp(); + // 下翻页 + virtual void pageDown(); + // 显示更多触发,默认弹出内部管理的SARibbonGalleryViewport,用户可重载此函数实现自定义的弹出 + virtual void showMoreDetail(); +protected slots: + void onItemClicked(const QModelIndex& index); + virtual void onTriggered(QAction* action); + +private: + SARibbonGalleryViewport* ensureGetPopupViewPort(); + +protected: + void resizeEvent(QResizeEvent* event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent* event) Q_DECL_OVERRIDE; +}; + +/// +/// \brief SARibbonGallery的Viewport class +/// +class SARibbonGalleryViewport : public QWidget +{ + Q_OBJECT +public: + SARibbonGalleryViewport(QWidget* parent); + // 添加窗口不带标题 + void addWidget(QWidget* w); + // 添加窗口,带标题 + void addWidget(QWidget* w, const QString& title); + // 通过SARibbonGalleryGroup获取对应的标题,用户可以通过此函数设置QLabel的属性 + QLabel* titleLabel(QWidget* w); + // 移除窗口 + void removeWidget(QWidget* w); +public slots: + void onTitleChanged(QWidget* w, const QString& title); + +private: + QVBoxLayout* m_layout; + QMap< QWidget*, QLabel* > _widgetToTitleLable; ///< QWidget和lable的对应 +}; + +#endif // SARIBBONGALLERY_H + +/*** End of inlined file: SARibbonGallery.h ***/ + +/*** Start of inlined file: SARibbonBar.h ***/ +#ifndef SARIBBONBAR_H +#define SARIBBONBAR_H + +#include +#include +#include + +class QAbstractButton; +class SARibbonElementFactory; +class SARibbonTabBar; +class SARibbonButtonGroupWidget; +class SARibbonQuickAccessBar; +class SARibbonStackedWidget; + +/** + @brief SARibbonBar继承于QMenuBar,在SARibbonMainWindow中直接替换了原来的QMenuBar + + 通过setRibbonStyle函数设置ribbon的风格: + + @code + void setRibbonStyle(RibbonStyles v); + @endcode + + SARibbonBar参考office和wps,提供了四种风格的Ribbon模式,@ref SARibbonBar::RibbonStyles + + 如果想ribbon占用的空间足够小,WpsLiteStyleTwoRow模式能比OfficeStyle节省35%的高度空间 + + 如何生成ribbon?先看看一个传统的Menu/ToolBar是如何生成的: + + @code + void MainWindow::MainWindow() + { + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + QToolBar *fileToolBar = addToolBar(tr("File")); + //生成action + QAction *newAct = new QAction(newIcon, tr("&New"), this); + fileMenu->addAction(newAct); + fileToolBar->addAction(newAct); + + QAction *openAct = new QAction(openIcon, tr("&Open..."), this); + fileMenu->addAction(openAct); + fileToolBar->addAction(openAct); + } + @endcode + + 传统的Menu/ToolBar主要通过QMenu的addMenu添加菜单,通过QMainWindow::addToolBar生成QToolBar, + 再把QAction设置进QMenu和QToolBar中 + + SARibbonBar和传统方法相似,不过相对于传统的Menu/ToolBar QMenu和QToolBar是平级的, + Ribbon是有明显的层级关系,SARibbonBar下面是 @ref SARibbonCategory, + SARibbonCategory下面是@ref SARibbonPannel ,SARibbonPannel下面是@ref SARibbonToolButton , + SARibbonToolButton管理着QAction + + 因此,生成一个ribbon只需以下几个函数: + @code + SARibbonCategory * SARibbonBar::addCategoryPage(const QString& title); + SARibbonPannel * SARibbonCategory::addPannel(const QString& title); + SARibbonToolButton * SARibbonPannel::addLargeAction(QAction *action); + SARibbonToolButton * SARibbonPannel::addSmallAction(QAction *action); + @endcode + + 因此生成步骤如下: + + @code + //成员变量 + SARibbonCategory* categoryMain; + SARibbonPannel* FilePannel; + + //建立ui + void setupRibbonUi() + { + ...... + //ribbonwindow为SARibbonMainWindow + SARibbonBar* ribbon = ribbonwindow->ribbonBar(); + ribbon->setRibbonStyle(SARibbonBar::WpsLiteStyle); + //添加一个Main标签 + categoryMain = ribbon->addCategoryPage(QStringLiteral("Main")); + //Main标签下添加一个File Pannel + FilePannel = categoryMain->addPannel(QStringLiteral("FilePannel")); + //开始为File Pannel添加action + FilePannel->addLargeAction(actionNew); + FilePannel->addLargeAction(actionOpen); + FilePannel->addLargeAction(actionSave); + FilePannel->addSmallAction(actionImportMesh); + FilePannel->addSmallAction(actionImportGeometry); + } + @endcode + */ +class SA_RIBBON_EXPORT SARibbonBar : public QMenuBar +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonBar) + friend class SARibbonMainWindow; + Q_PROPERTY(RibbonStyles ribbonStyle READ currentRibbonStyle WRITE setRibbonStyle) + Q_PROPERTY(bool minimumMode READ isMinimumMode WRITE setMinimumMode) + Q_PROPERTY(bool minimumModeButton READ haveShowMinimumModeButton WRITE showMinimumModeButton) + Q_PROPERTY(QColor windowTitleTextColor READ windowTitleTextColor WRITE setWindowTitleTextColor) + Q_PROPERTY(QColor tabBarBaseLineColor READ tabBarBaseLineColor WRITE setTabBarBaseLineColor) + Q_PROPERTY(Qt::Alignment windowTitleAligment READ windowTitleAligment WRITE setWindowTitleAligment) + Q_PROPERTY(bool enableWordWrap READ isEnableWordWrap WRITE setEnableWordWrap) + Q_PROPERTY(bool enableShowPannelTitle READ isEnableShowPannelTitle WRITE setEnableShowPannelTitle) + Q_PROPERTY(bool tabOnTitle READ isTabOnTitle WRITE setTabOnTitle) + Q_PROPERTY(SARibbonPannel::PannelLayoutMode pannelLayoutMode READ pannelLayoutMode WRITE setPannelLayoutMode) + +public: + enum RibbonStyleFlag + { + RibbonStyleLoose = 0x0001, // bit:0000 0001 + RibbonStyleCompact = 0x0002, // bit:0000 0010 + RibbonStyleThreeRow = 0x0010, // bit:0001 0000 + RibbonStyleTwoRow = 0x0020, // bit:0010 0000 + + RibbonStyleLooseThreeRow = RibbonStyleLoose | RibbonStyleThreeRow, ///< 宽松结构,3行模式 + RibbonStyleCompactThreeRow = RibbonStyleCompact | RibbonStyleThreeRow, ///< 紧凑结构,3行模式 + RibbonStyleLooseTwoRow = RibbonStyleLoose | RibbonStyleTwoRow, ///< 宽松结构,2行模式 + RibbonStyleCompactTwoRow = RibbonStyleCompact | RibbonStyleTwoRow ///< 紧凑结构,2行模式 + }; + Q_ENUM(RibbonStyleFlag) + Q_DECLARE_FLAGS(RibbonStyles, RibbonStyleFlag) + Q_FLAG(RibbonStyles) + + /** + * @brief 定义当前ribbon 的状态 + */ + enum RibbonMode + { + MinimumRibbonMode, ///< 缩小模式 + NormalRibbonMode ///< 正常模式 + }; + Q_ENUM(RibbonMode) + + using FpCategoryIterate = std::function< bool(SARibbonCategory*) >; + using FpPannelIterate = SARibbonCategory::FpPannelIterate; + +public: + // 判断RibbonStyle是否为2行模式 + static bool isTwoRowStyle(RibbonStyles s); + static bool isThreeRowStyle(RibbonStyles s); + // 判断是否是office样式 + static bool isLooseStyle(RibbonStyles s); + static bool isCompactStyle(RibbonStyles s); + // 获取版本信息 + static QString versionString(); + + // 获取默认的上下文标签颜色列表 + static QList< QColor > defaultContextCategoryColorList(); + + // + static void initHighDpi(); + +public: + // 构造函数 + SARibbonBar(QWidget* parent = nullptr); + ~SARibbonBar() Q_DECL_OVERRIDE; + // 获取applicationButton + QAbstractButton* applicationButton(); + + // 设置applicationButton + void setApplicationButton(QAbstractButton* btn); + + // 获取tabbar + SARibbonTabBar* ribbonTabBar(); + + // 添加一个标签 + SARibbonCategory* addCategoryPage(const QString& title); + void addCategoryPage(SARibbonCategory* category); + + // 为了支持Qt designer,添加的一个重载函数 + Q_INVOKABLE void addCategoryPage(QWidget* category); + + // 添加一个category,category的位置在index,如果当前category数量少于index,将插入到最后 + SARibbonCategory* insertCategoryPage(const QString& title, int index); + void insertCategoryPage(SARibbonCategory* category, int index); + + // 通过名字查找Category + SARibbonCategory* categoryByName(const QString& title) const; + + // 通过ObjectName查找Category + SARibbonCategory* categoryByObjectName(const QString& objname) const; + + // 通过索引找到category,如果超过索引范围,会返回nullptr + SARibbonCategory* categoryByIndex(int index) const; + + // 隐藏category,并不会删除或者取走,只是隐藏 + void hideCategory(SARibbonCategory* category); + + // 显示被隐藏的category + void showCategory(SARibbonCategory* category); + + // 判断这个category是否在显示状态,也就是tabbar有这个category + bool isCategoryVisible(const SARibbonCategory* c) const; + + // 获取category的索引 + int categoryIndex(const SARibbonCategory* c) const; + + // 移动一个Category从from index到to index + void moveCategory(int from, int to); + + // 获取当前显示的所有的SARibbonCategory,包含未显示的SARibbonContextCategory的SARibbonCategory也一并返回 + QList< SARibbonCategory* > categoryPages(bool getAll = true) const; + + // 移除SARibbonCategory + void removeCategory(SARibbonCategory* category); + + // 添加一个上下文标签 + SARibbonContextCategory* addContextCategory(const QString& title, const QColor& color = QColor(), const QVariant& id = QVariant()); + void addContextCategory(SARibbonContextCategory* context); + + // 显示一个上下文标签 + void showContextCategory(SARibbonContextCategory* context); + + // 隐藏一个上下文标签 + void hideContextCategory(SARibbonContextCategory* context); + + // 判断上下文是否是在显示状态 + bool isContextCategoryVisible(SARibbonContextCategory* context); + + // 设置上下文标签的显示或隐藏 + void setContextCategoryVisible(SARibbonContextCategory* context, bool visible); + + // 获取所有的上下文标签 + QList< SARibbonContextCategory* > contextCategoryList() const; + + // 移除ContextCategory + void destroyContextCategory(SARibbonContextCategory* context); + + // 设置为隐藏模式 + void setMinimumMode(bool isHide); + + // 当前Ribbon是否是隐藏模式 + bool isMinimumMode() const; + + // 设置显示隐藏ribbon按钮 + void showMinimumModeButton(bool isShow = true); + + // 是否显示隐藏ribbon按钮 + bool haveShowMinimumModeButton() const; + + // 隐藏ribbon对应的action + QAction* minimumModeAction() const; + + // 当前的模式 + RibbonMode currentRibbonState() const; + + // ribbon tab的高度 + int tabBarHeight() const; + void setTabBarHeight(int h, bool resizeByNow = true); + + // 标题栏的高度 + int titleBarHeight() const; + void setTitleBarHeight(int h, bool resizeByNow = true); + + // category的高度 + int categoryHeight() const; + void setCategoryHeight(int h, bool resizeByNow = true); + + // 获取正常模式下的mainBar的高度 + int normalModeMainBarHeight() const; + + // 最小模式下的MainBar高度 + int minimumModeMainBarHeight() const; + + // 激活tabbar右边的按钮群 + SARibbonButtonGroupWidget* activeRightButtonGroup(); + + // 右侧按钮群 + SARibbonButtonGroupWidget* rightButtonGroup(); + + // 激活QuickAccessBar + SARibbonQuickAccessBar* activeQuickAccessBar(); + + // 快速响应栏 + SARibbonQuickAccessBar* quickAccessBar(); + + // 设置ribbon的风格 + void setRibbonStyle(RibbonStyles v); + RibbonStyles currentRibbonStyle() const; + + // 设置当前ribbon的index + void setCurrentIndex(int index); + + // 返回当前的tab索引 + int currentIndex(); + + // 确保标签显示出来 + void raiseCategory(SARibbonCategory* category); + + // 判断当前的行数 + bool isTwoRowStyle() const; + bool isThreeRowStyle() const; + + // 判断当前的样式 + bool isLooseStyle() const; + bool isCompactStyle() const; + + // 更新ribbon的布局数据,此函数适用于一些关键性尺寸变化,换起ribbon下面元素的布局,在发现刷新问题时,可以调用此函数 + void updateRibbonGeometry(); + + // 设置pannel的模式 + SARibbonPannel::PannelLayoutMode pannelLayoutMode() const; + void setPannelLayoutMode(SARibbonPannel::PannelLayoutMode m); + + // 设置tab在title上面,这样可以省略title区域 + void setTabOnTitle(bool on); + bool isTabOnTitle() const; + + // tabbar 底部会绘制一条线条,此接口定义线条颜色 + void setTabBarBaseLineColor(const QColor& clr); + QColor tabBarBaseLineColor() const; + + // 设置标题颜色,如果不设置标题颜色,默认是SARibbonBar的qss的color属性 + void setWindowTitleTextColor(const QColor& clr); + QColor windowTitleTextColor() const; + + // 设置标题的对齐方式 + void setWindowTitleAligment(Qt::Alignment al); + Qt::Alignment windowTitleAligment() const; + + // 设置按钮允许换行,注意图标大小是由文字决定的,两行文字会让图标变小,如果想图标变大,文字不换行是最好的 + void setEnableWordWrap(bool on); + bool isEnableWordWrap() const; + + // 设置pannel的标题栏高度 + int pannelTitleHeight() const; + void setPannelTitleHeight(int h); + + // 设置pannel是否显示标题栏 + bool isEnableShowPannelTitle() const; + void setEnableShowPannelTitle(bool on); + + // 获取SARibbonStackedWidget,谨慎使用此函数 + SARibbonStackedWidget* ribbonStackedWidget(); + + // 设置是否显示标题 + void setTitleVisible(bool on = false); + bool isTitleVisible() const; + + // 上下文标签的颜色列表,上下文标签显示的时候,会从颜色列表中取颜色进行标签的渲染 + void setContextCategoryColorList(const QList< QColor >& cls); + QList< QColor > contextCategoryColorList() const; + + // 设置ribbon的对齐方式 + void setRibbonAlignment(SARibbonAlignment al); + SARibbonAlignment ribbonAlignment() const; + + // 此函数会遍历SARibbonBar下的所有Category,执行函数指针(bool(SARibbonCategory*)),函数指针返回false则停止迭代 + bool iterate(FpCategoryIterate fp); + // 此函数会遍历SARibbonBar下的所有Category,并迭代所有的pannel,执行函数指针(bool(SARibbonPannel*)),函数指针返回false则停止迭代 + bool iterate(FpPannelIterate fp); + + // 设置边角widget可见性,对于mdi窗口,会出现TopLeftCorner和TopRightCorner两个corner widget + void setCornerWidgetVisible(bool on, Qt::Corner c = Qt::TopLeftCorner); +signals: + + /** + @brief 应用按钮点击响应 - 左上角的按钮,通过关联此信号触发应用按钮点击的效果 + + 例如想点击按钮后弹出一个全屏的窗口(如office这些) + */ + void applicationButtonClicked(); + + /** + @brief 标签页变化触发的信号 + @param index + */ + void currentRibbonTabChanged(int index); + + /** + @brief ribbon的状态发生了变化后触发此信号 + @param nowState 变更之后的ribbon状态 + */ + void ribbonModeChanged(SARibbonBar::RibbonMode nowState); + + /** + @brief ribbon的状态发生了变化后触发此信号 + @param nowStyle 变更之后的ribbon样式 + */ + void ribbonStyleChanged(SARibbonBar::RibbonStyles nowStyle); + + /** + @brief 标题栏高度发生了变化的信号 + @param oldHeight + @param newHeight + */ + void titleBarHeightChanged(int oldHeight, int newHeight); + +protected: + bool eventFilter(QObject* obj, QEvent* e) override; + // 根据情况重置tabbar的宽度,主要针对wps模式 + int calcMinTabBarWidth() const; + // 更新 + void updateCategoryTitleToTabName(); + // 告知WindowButtonGroup的尺寸 + void setWindowButtonGroupSize(const QSize& s); +protected slots: + void onWindowTitleChanged(const QString& title); + void onWindowIconChanged(const QIcon& i); + void onCategoryWindowTitleChanged(const QString& title); + void onStackWidgetHided(); + virtual void onCurrentRibbonTabChanged(int index); + virtual void onCurrentRibbonTabClicked(int index); + virtual void onCurrentRibbonTabDoubleClicked(int index); + void onContextsCategoryPageAdded(SARibbonCategory* category); + void onContextsCategoryCategoryNameChanged(SARibbonCategory* category, const QString& title); + void onTabMoved(int from, int to); + +private: + int tabIndex(SARibbonCategory* obj); + void resizeAll(); + void resizeInLooseStyle(); + void resizeInCompactStyle(); + void paintInLooseStyle(); + void paintInCompactStyle(); + void resizeStackedContainerWidget(); + + // 刷新所有ContextCategoryManagerData,这个在单独一个Category删除时调用 + void updateContextCategoryManagerData(); + void synchronousCategoryData(bool autoUpdate = true); + +protected: + virtual void paintEvent(QPaintEvent* e) Q_DECL_OVERRIDE; + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + virtual void moveEvent(QMoveEvent* e) Q_DECL_OVERRIDE; + virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; + virtual bool event(QEvent* e) Q_DECL_OVERRIDE; + virtual void paintTabbarBaseLine(QPainter& painter); + virtual void paintWindowTitle(QPainter& painter, const QString& title, const QRect& titleRegion); + virtual void paintContextCategoryTab(QPainter& painter, const QString& title, QRect contextRect, const QColor& color); +#if SA_DEBUG_PRINT_SARIBBONBAR + SA_RIBBON_EXPORT friend QDebug operator<<(QDebug debug, const SARibbonBar& ribbon); +#endif +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(SARibbonBar::RibbonStyles) + +#endif // SARIBBONBAR_H + +/*** End of inlined file: SARibbonBar.h ***/ + +/*** Start of inlined file: SARibbonElementFactory.h ***/ +#ifndef SARIBBONELEMENTFACTORY_H +#define SARIBBONELEMENTFACTORY_H + +#include +#include +#include +#include + +class QWidget; +class SARibbonBar; +class SARibbonTabBar; +class SARibbonApplicationButton; +class SARibbonCategory; +class SARibbonContextCategory; +class SARibbonPannel; +class SARibbonSeparatorWidget; +class SARibbonGallery; +class SARibbonGalleryGroup; +class SARibbonToolButton; +class SARibbonControlButton; +class SARibbonControlToolButton; +class SARibbonButtonGroupWidget; +class SARibbonStackedWidget; +class SARibbonQuickAccessBar; +class SARibbonPannelOptionButton; +class SARibbonSystemButtonBar; + +/// +/// \brief SARibbon的子元素创建的工厂,SARibbon内部创建子元素都通过SARibbonElementFactory来创建 +/// +/// 由于SARibbonBar是一个复合控件,很多子窗口组合而成,有些部件的创建如果想继承,那么就需要这个工厂类来处理 +/// 如SARibbonCategory,可以重载此类的createRibbonCategory,返回重载的类的实例 +/// +class SA_RIBBON_EXPORT SARibbonElementFactory +{ +public: + SARibbonElementFactory(); + virtual ~SARibbonElementFactory(); + virtual SARibbonTabBar* createRibbonTabBar(QWidget* parent); + virtual SARibbonApplicationButton* createRibbonApplicationButton(QWidget* parent); + virtual SARibbonCategory* createRibbonCategory(QWidget* parent); + virtual SARibbonContextCategory* createRibbonContextCategory(QWidget* parent); + virtual SARibbonPannel* createRibbonPannel(QWidget* parent); + virtual SARibbonSeparatorWidget* createRibbonSeparatorWidget(QWidget* parent); + virtual SARibbonGallery* createRibbonGallery(QWidget* parent); + virtual SARibbonGalleryGroup* createRibbonGalleryGroup(QWidget* parent); + virtual SARibbonToolButton* createRibbonToolButton(QWidget* parent); + virtual SARibbonControlButton* createRibbonControlButton(QWidget* parent); + virtual SARibbonControlToolButton* createRibbonControlToolButton(QWidget* parent); + virtual SARibbonStackedWidget* createRibbonStackedWidget(SARibbonBar* parent); + virtual SARibbonButtonGroupWidget* craeteButtonGroupWidget(QWidget* parent); + virtual SARibbonQuickAccessBar* createQuickAccessBar(QWidget* parent); + virtual SARibbonSystemButtonBar* createWindowButtonGroup(QWidget* parent); + virtual SARibbonPannelOptionButton* createRibbonPannelOptionButton(SARibbonPannel* pannel); +}; + +#endif // SARIBBONELEMENTFACTORY_H + +/*** End of inlined file: SARibbonElementFactory.h ***/ + +/*** Start of inlined file: SARibbonElementManager.h ***/ +#ifndef SARIBBONELEMENTMANAGER_H +#define SARIBBONELEMENTMANAGER_H + +/** + @brief 此类是一个全局单例,用于管理SARibbonElementFactory + + 如果你有自己的某个部件要重写,首先你需要有自己的ElementFactory + @code + class MyRibbonElementFactory : public SARibbonElementFactory{ + public: + ... + virtual SARibbonPannel* createRibbonPannel(QWidget* parent){ + return new MyRibbonPannel(parent); + } + }; + @endcode + + 然后,你需要在ribbonbar创建之前把自己的ElementFactory设置进去 + + 这个一般会在main函数中进行 + + @code + SARibbonElementManager::instance()->setupFactory(new MyRibbonElementFactory); + @endcode + + 这样,SARibbon创建的pannel就是你自己重写的MyRibbonPannel + + */ +class SA_RIBBON_EXPORT SARibbonElementManager +{ +protected: + SARibbonElementManager(); + +public: + virtual ~SARibbonElementManager(); + static SARibbonElementManager* instance(); + SARibbonElementFactory* factory(); + void setupFactory(SARibbonElementFactory* fac); + +private: + QScopedPointer< SARibbonElementFactory > mFactory; +}; +#ifndef RibbonSubElementMgr +#define RibbonSubElementMgr SARibbonElementManager::instance() +#endif +#ifndef RibbonSubElementFactory +#define RibbonSubElementFactory SARibbonElementManager::instance()->factory() +#endif + +#endif // SARIBBONELEMENTMANAGER_H + +/*** End of inlined file: SARibbonElementManager.h ***/ + +/*** Start of inlined file: SARibbonCustomizeData.h ***/ +#ifndef SARIBBONCUSTOMIZEDATA_H +#define SARIBBONCUSTOMIZEDATA_H + +#include +class SARibbonBar; +class SARibbonMainWindow; + +/** + * @brief 记录所有自定义操作的数据类 + * @note 此数据依赖于@ref SARibbonActionsManager 要在SARibbonActionsManager之后使用此类 + */ +class SA_RIBBON_EXPORT SARibbonCustomizeData +{ +public: + enum ActionType + { + UnknowActionType = 0, ///< 未知操作 + AddCategoryActionType, ///< 添加category操作(1) + AddPannelActionType, ///< 添加pannel操作(2) + AddActionActionType, ///< 添加action操作(3) + RemoveCategoryActionType, ///< 删除category操作(4) + RemovePannelActionType, ///< 删除pannel操作(5) + RemoveActionActionType, ///< 删除action操作(6) + ChangeCategoryOrderActionType, ///< 改变category顺序的操作(7) + ChangePannelOrderActionType, ///< 改变pannel顺序的操作(8) + ChangeActionOrderActionType, ///< 改变action顺序的操作(9) + RenameCategoryActionType, ///< 对category更名操作(10) + RenamePannelActionType, ///< 对Pannel更名操作(11) + VisibleCategoryActionType ///< 对category执行隐藏/显示操作(12) + }; + SARibbonCustomizeData(); + SARibbonCustomizeData(ActionType type, SARibbonActionsManager* mgr = nullptr); + // 获取CustomizeData的action type + ActionType actionType() const; + + // 设置CustomizeData的action type + void setActionType(ActionType a); + + // 判断是否是一个正常的CustomizeData + bool isValid() const; + + // 应用SARibbonCustomizeData + bool apply(SARibbonBar* bar) const; + + // 获取actionmanager指针 + SARibbonActionsManager* actionManager(); + + // 设置ActionsManager + void setActionsManager(SARibbonActionsManager* mgr); + + // 对应AddCategoryActionType + static SARibbonCustomizeData makeAddCategoryCustomizeData(const QString& title, int index, const QString& objName); + + // 对应AddPannelActionType + static SARibbonCustomizeData makeAddPannelCustomizeData(const QString& title, int index, const QString& categoryobjName, const QString& objName); + + // 对应AddActionActionType + static SARibbonCustomizeData makeAddActionCustomizeData(const QString& key, + SARibbonActionsManager* mgr, + SARibbonPannelItem::RowProportion rp, + const QString& categoryObjName, + const QString& pannelObjName); + + // 对应RenameCategoryActionType + static SARibbonCustomizeData makeRenameCategoryCustomizeData(const QString& newname, const QString& categoryobjName); + + // 对应RenamePannelActionType + static SARibbonCustomizeData makeRenamePannelCustomizeData(const QString& newname, + const QString& categoryobjName, + const QString& pannelObjName); + + // 对应RemoveCategoryActionType + static SARibbonCustomizeData makeRemoveCategoryCustomizeData(const QString& categoryobjName); + + // 对应ChangeCategoryOrderActionType + static SARibbonCustomizeData makeChangeCategoryOrderCustomizeData(const QString& categoryobjName, int moveindex); + + // 对应ChangePannelOrderActionType + static SARibbonCustomizeData makeChangePannelOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + int moveindex); + + // 对应ChangeActionOrderActionType + static SARibbonCustomizeData makeChangeActionOrderCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr, + int moveindex); + + // 对应RemovePannelActionType + static SARibbonCustomizeData makeRemovePannelCustomizeData(const QString& categoryobjName, const QString& pannelObjName); + + // 对应RemoveActionActionType + static SARibbonCustomizeData makeRemoveActionCustomizeData(const QString& categoryobjName, + const QString& pannelObjName, + const QString& key, + SARibbonActionsManager* mgr); + + // 对应VisibleCategoryActionType + static SARibbonCustomizeData makeVisibleCategoryCustomizeData(const QString& categoryobjName, bool isShow); + + // 判断是否可以自定义,如果某个action不想被编辑,可以通过此函数设置 + static bool isCanCustomize(QObject* obj); + static void setCanCustomize(QObject* obj, bool canbe = true); + + // 对QList进行简化 + static QList< SARibbonCustomizeData > simplify(const QList< SARibbonCustomizeData >& csd); + +public: + /** + * @brief 记录顺序的参数 + * + * 在actionType==AddCategoryActionType时,此参数记录Category的insert位置, + * 在actionType==AddPannelActionType时,此参数记录pannel的insert位置, + * 在actionType==AddActionActionType时,此参数记录pannel的insert位置 + */ + int indexValue; + + /** + * @brief 记录标题、索引等参数 + * + * 在actionType==AddCategoryActionType时,key为category标题, + * 在actionType==AddPannelActionType时,key为pannel标题, + * 在actionType==AddActionActionType时,key为action的查询依据,基于SARibbonActionsManager::action查询 + */ + QString keyValue; + + /** + * @brief 记录categoryObjName,用于定位Category + */ + QString categoryObjNameValue; + + /** + * @brief 记录pannelObjName,saribbon的Customize索引大部分基于objname + */ + QString pannelObjNameValue; + + SARibbonPannelItem::RowProportion actionRowProportionValue; ///< 行的占比,ribbon中有large,media和small三种占比,见@ref RowProportion +private: + ActionType m_type; ///< 标记这个data是category还是pannel亦或是action + SARibbonActionsManager* m_actionsManagerPointer; +}; +Q_DECLARE_METATYPE(SARibbonCustomizeData) + +typedef QList< SARibbonCustomizeData > SARibbonCustomizeDataList; + +#endif // SARIBBONCUSTOMIZEDATA_H + +/*** End of inlined file: SARibbonCustomizeData.h ***/ + +/*** Start of inlined file: SARibbonCustomizeWidget.h ***/ +#ifndef SARIBBONCUSTOMIZEWIDGET_H +#define SARIBBONCUSTOMIZEWIDGET_H + +#include + +// SARibbonCustomizeWidget 特有 +class SARibbonCustomizeWidgetUi; +class SARibbonMainWindow; +class SARibbonBar; +// +class QStandardItemModel; +class QStandardItem; +class QAbstractButton; +// +class QXmlStreamWriter; +class QXmlStreamReader; + +/** + * @brief 自定义界面窗口 + * + * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 + * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeWidget::fromXml 同步配置文件,这样再次修改后的配置文件就一致 + */ +class SA_RIBBON_EXPORT SARibbonCustomizeWidget : public QWidget +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonCustomizeWidget) +public: + // 保留接口 + SARibbonCustomizeWidget(SARibbonMainWindow* ribbonWindow, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + // 对于不使用SARibbonMainWindow的情况,使用此构造函数 + SARibbonCustomizeWidget(SARibbonBar* ribbonbar, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + ~SARibbonCustomizeWidget(); + + /** + * @brief 定义ribbon树的显示类型 + */ + enum RibbonTreeShowType + { + ShowAllCategory, ///< 显示所有Category,包括contextcategory + ShowMainCategory ///< 显示主要的category,不包含上下文 + }; + + /** + * @brief QStandardItem对应的role + */ + enum ItemRole + { + LevelRole = Qt::UserRole + 1, ///< 代表这是层级,有0:category 1:pannel 2:item + PointerRole = Qt::UserRole + 2, ///< 代表这是存放指针。根据LevelRole来进行转 + CanCustomizeRole = Qt::UserRole + 3, ///< 代表个item是可以自定义的.bool + CustomizeRole = Qt::UserRole + 4, ///< 代表这个是自定义的item,bool,主要用于那些自己添加的标签和pannel,有此角色必有CanCustomizeRole + CustomizeObjNameRole = Qt::UserRole + 5 ///< 记录了临时的自定义内容的obj名 QString + }; + + // 设置action管理器 + void setupActionsManager(SARibbonActionsManager* mgr); + + // 判断用户是否有要存储的内容,对应save动作 + bool isApplied() const; + + // 判断用户是否有改动内容,对应apply动作 + bool isCached() const; + + // 获取model + const QStandardItemModel* model() const; + + // 根据当前的radiobutton选项来更新model + void updateModel(); + + // 更新model + void updateModel(RibbonTreeShowType type); + + // 应用所有的设定 + bool applys(); + + // 转换为xml + bool toXml(QXmlStreamWriter* xml) const; + bool toXml(const QString& xmlpath) const; + + // 从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + void fromXml(QXmlStreamReader* xml); + void fromXml(const QString& xmlpath); + + // 应用xml配置,可以结合customize_datas_from_xml和customize_datas_apply函数 + static bool fromXml(QXmlStreamReader* xml, SARibbonBar* bar, SARibbonActionsManager* mgr); + + // 缓存应用的动作,这些动作不会被clear清除,用于本地存储 + void makeActionsApplied(); + + // 清除applied的动作,cancel操作后需要清空已应用的动作 + void clearApplied(); + // 清除缓存动作,在执行applys函数后,如果要继续调用,应该clear,否则会导致异常 + void clearCache(); + // 清除所有动作,不包含本地读取的数据 + void clear(); + +protected: + // 把QList进行裁剪,把一些动作合并 + void simplify(); + + SARibbonPannelItem::RowProportion selectedRowProportion() const; + + QAction* selectedAction() const; + QAction* itemToAction(QStandardItem* item) const; + + QStandardItem* selectedItem() const; + + // 获取选中的ribbon tree 的level + int selectedRibbonLevel() const; + + // 根据选中的item判断 + int itemLevel(QStandardItem* item) const; + + // 设置某个item被选中 + void setSelectItem(QStandardItem* item, bool ensureVisible = true); + + // 判断itemn能否改动,可以改动返回true + bool isItemCanCustomize(QStandardItem* item) const; + bool isSelectedItemCanCustomize() const; + + // 判断item是否是自定义的item + bool isCustomizeItem(QStandardItem* item) const; + bool isSelectedItemIsCustomize() const; + + // 删除一个item + void removeItem(QStandardItem* item); + +private slots: + void onComboBoxActionIndexCurrentIndexChanged(int index); + void onRadioButtonGroupButtonClicked(QAbstractButton* b); + void onPushButtonNewCategoryClicked(); + void onPushButtonNewPannelClicked(); + void onPushButtonRenameClicked(); + void onPushButtonAddClicked(); + void onPushButtonDeleteClicked(); + void onListViewSelectClicked(const QModelIndex& index); + void onTreeViewResultClicked(const QModelIndex& index); + void onToolButtonUpClicked(); + void onToolButtonDownClicked(); + void onItemChanged(QStandardItem* item); + void onLineEditSearchActionTextEdited(const QString& text); + void onPushButtonResetClicked(); + +private: + void init(SARibbonBar* ribbonbar); + void initConnection(); + +private: + SARibbonCustomizeWidgetUi* ui; +}; + +/** + * @brief 转换为xml + * + * 此函数仅会写element,不会写document相关内容,因此如果需要写document, + * 需要在此函数前调用QXmlStreamWriter::writeStartDocument(),在此函数后调用QXmlStreamWriter::writeEndDocument() + * @param xml QXmlStreamWriter指针 + * @note 注意,在传入QXmlStreamWriter之前,需要设置编码为utf-8:xml->setCodec("utf-8"); + * @note 由于QXmlStreamWriter在QString作为io时,是不支持编码的,而此又无法保证自定义过程不出现中文字符, + * 因此,QXmlStreamWriter不应该通过QString进行构造,如果需要用到string,也需要通过QByteArray构造,如: + * @param cds 基于QList生成的步骤 + * @return 如果出现异常,返回false,如果没有自定义数据也会返回false + */ +bool SA_RIBBON_EXPORT sa_customize_datas_to_xml(QXmlStreamWriter* xml, const QList< SARibbonCustomizeData >& cds); + +/** + * @brief 通过xml获取QList + * @param xml + * @return QList + */ +QList< SARibbonCustomizeData > SA_RIBBON_EXPORT sa_customize_datas_from_xml(QXmlStreamReader* xml, SARibbonActionsManager* mgr); + +/** + * @brief 应用QList + * @param cds + * @param w SARibbonBar指针 + * @return 成功应用的个数 + */ +int SA_RIBBON_EXPORT sa_customize_datas_apply(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); + +/** + * @brief 反向取消应用 + * @param cds + * @param w SARibbonBar指针 + * @return 成功应用的个数 + */ +int SA_RIBBON_EXPORT sa_customize_datas_reverse(const QList< SARibbonCustomizeData >& cds, SARibbonBar* w); + +/** + * @brief 直接加载xml自定义ribbon配置文件用于ribbon的自定义显示 + * @param filePath xml配置文件 + * @param w 主窗体 + * @param mgr action管理器 + * @return 成功返回true + * @note 重复加载一个配置文件会发生异常,为了避免此类事件发生,一般通过一个变量保证只加载一次,如: + * @code + * static bool has_call = false; + * if (!has_call) { + * has_call = sa_apply_customize_from_xml_file("customize.xml", this, m_actMgr); + * } + * @endcode + */ +bool SA_RIBBON_EXPORT sa_apply_customize_from_xml_file(const QString& filePath, SARibbonBar* bar, SARibbonActionsManager* mgr); + +#endif // SARIBBONCUSTOMIZEWIDGET_H + +/*** End of inlined file: SARibbonCustomizeWidget.h ***/ + +/*** Start of inlined file: SARibbonCustomizeDialog.h ***/ +#ifndef SARIBBONCUSTOMIZEDIALOG_H +#define SARIBBONCUSTOMIZEDIALOG_H + +#include + +class SARibbonActionsManager; +class SARibbonCustomizeDialogUi; +class QXmlStreamWriter; + +/** + * @brief SARibbonCustomizeWidget的对话框封装 + * + * 此功能依赖于@ref SARibbonActionsManager ,SARibbonActionsManager建议作为mianwindow的成员变量, + * SARibbonActionsManager可以快速绑定所有QAction,详细见SARibbonActionsManager的说明 + * + * @note SARibbon的自定义是基于步骤的,如果在窗口生成前调用了@ref sa_apply_customize_from_xml_file 类似函数 + * 那么在对话框生成前为了保证同步需要调用@ref SARibbonCustomizeDialog::fromXml 同步配置文件,这样再次修改后的配置文件就一致 + */ +class SA_RIBBON_EXPORT SARibbonCustomizeDialog : public QDialog +{ + Q_OBJECT +public: + SARibbonCustomizeDialog(SARibbonMainWindow* ribbonWindow, QWidget* p = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); + // 设置action管理器 + void setupActionsManager(SARibbonActionsManager* mgr); + + // 判断用户是否有要存储的内容,对应save动作 + bool isApplied() const; + + // 判断用户是否有改动内容,对应apply动作 + bool isCached() const; + + // 应用所有的设定 + bool applys(); + + // 清除所有动作 + void clear(); + + // 转换为xml + bool toXml(QXmlStreamWriter* xml) const; + bool toXml(const QString& xmlpath) const; + + // 从xml中加载QList,对于基于配置文件的设置,对话框显示前建议调用此函数,保证叠加设置的正确记录 + void fromXml(QXmlStreamReader* xml); + void fromXml(const QString& xmlpath); + + // 返回SARibbonCustomizeWidget窗口指针 + SARibbonCustomizeWidget* customizeWidget() const; + +private: + void initConnection(); + + SARibbonCustomizeDialogUi* ui; +}; + +#endif // SARIBBONCUSTOMIZEDIALOG_H + +/*** End of inlined file: SARibbonCustomizeDialog.h ***/ + +/*** Start of inlined file: SARibbonMainWindow.h ***/ +#ifndef SARIBBONMAINWINDOW_H +#define SARIBBONMAINWINDOW_H + +#include + +#if !SARIBBON_USE_3RDPARTY_FRAMELESSHELPER +class SAFramelessHelper; +#endif + +class SARibbonBar; +class SARibbonSystemButtonBar; +class QScreen; +/** + * @brief 如果要使用SARibbonBar,必须使用此类代替QMainWindow + * + * 由于ribbon的风格和传统的Toolbar风格差异较大, + * SARibbonBar使用需要把原有的QMainWindow替换为SARibbonMainWindow, + * SARibbonMainWindow是个无边框窗体,继承自QMainWindow(目前使用第三方的无边框方案https://github.com/wangwenx190/framelesshelper), + * 其构造函数的参数useRibbon用于指定是否使用ribbon风格,默认为true + * + * @code + * SARibbonMainWindow(QWidget* parent = nullptr,bool useRibbon = true); + * @endcode + * + * 如果想换回非ribbon风格,只需要把useRibbon设置为false即可, + * 成员函数isUseRibbon用于判断当前是否为ribbon模式,这个函数在兼容传统Toolbar风格和ribbon风格时非常有用。 + * + * @code + * bool isUseRibbon() const; + * @endcode + * + * @ref SARibbonMainWindow 提供了几种常用的ribbon样式,样式可见@ref RibbonTheme + * 通过@ref setRibbonTheme 可改变ribbon的样式,用户也可通过qss自己定义自己的样式 + * + */ +class SA_RIBBON_EXPORT SARibbonMainWindow : public QMainWindow +{ + Q_OBJECT + SA_RIBBON_DECLARE_PRIVATE(SARibbonMainWindow) + friend class SARibbonBar; + Q_PROPERTY(RibbonTheme ribbonTheme READ ribbonTheme WRITE setRibbonTheme) +public: + /** + * @brief Ribbon主题,可以通过qss定制ribbon的主题,定制方法可参看源码中office2013.qss + * + * 注意,由于有些qss的尺寸,在C++代码中无法获取到,因此针对用户自定义的qss主题,有些尺寸是需要手动设置进去的 + * + * 例如ribbon tab的margin信息,在QTabBar是无法获取到,而这个影响了SARibbonContextCategory的绘制, + * 因此,在设置qss后需要针对margin信息重新设置进SARibbonTabBar中 + */ + enum RibbonTheme + { + RibbonThemeOffice2013, ///< office2013主题 + RibbonThemeOffice2016Blue, ///< office2016-蓝色主题 + RibbonThemeOffice2021Blue, ///< office2021-蓝色主题 + RibbonThemeWindows7, ///< win7主题 + RibbonThemeDark, ///< 暗色主题 + RibbonThemeDark2 + }; + Q_ENUM(RibbonTheme) +public: + SARibbonMainWindow(QWidget* parent = nullptr, bool useRibbon = true, const Qt::WindowFlags flags = {}); + ~SARibbonMainWindow() Q_DECL_OVERRIDE; + // 返回SARibbonBar + SARibbonBar* ribbonBar() const; + // 设置ribbonbar + void setRibbonBar(SARibbonBar* bar); +#if !SARIBBON_USE_3RDPARTY_FRAMELESSHELPER + // 返回SAFramelessHelper + SAFramelessHelper* framelessHelper(); +#else + // 如果ribbon中有自定义的窗口在标题栏等非点击区域加入后,想能点击,需要调用此接口告知可点击 + void setFramelessHitTestVisible(const QWidget* w, bool visible = true); +#endif + // 此函数仅用于控制最小最大化和关闭按钮的显示 + void updateWindowFlag(Qt::WindowFlags flags); + + // 注意主题在构造函数设置主题会不完全生效,使用QTimer投放到队列最后执行即可 + // QTimer::singleShot(0, this, [ this ]() { this->setRibbonTheme(SARibbonMainWindow::RibbonThemeDark); }); + void setRibbonTheme(RibbonTheme theme); + RibbonTheme ribbonTheme() const; + // 判断当前是否使用ribbon模式 + bool isUseRibbon() const; + // 把ribbonbar的事件传递到frameless + virtual bool eventFilter(QObject* obj, QEvent* e) Q_DECL_OVERRIDE; + // 获取最大化,最小化,关闭按钮所在的bar。可以通过此函数在最大最小化按钮旁边设置内容 + SARibbonSystemButtonBar* windowButtonBar() const; + +protected: + // 创建ribbonbar的工厂函数 + SARibbonBar* createRibbonBar(); + virtual void resizeEvent(QResizeEvent* e) Q_DECL_OVERRIDE; + virtual void changeEvent(QEvent* e) Q_DECL_OVERRIDE; +private slots: + void onPrimaryScreenChanged(QScreen* screen); +}; + +/** + * @brief 全局的设置ribbon theme函数 + * + * 之所以提供此函数,是因为在某些情况下,SARibbonBar不用在SARibbonMainWindow情况下的时候,也需要设置主题, + * 但主题设置是在SARibbonMainWindow下的为了能在非SARibbonMainWindow下也能设置主题,这里提供@sa sa_set_ribbon_theme函数, + * 可以这样使用: + * @code + * @endcode + * + * @param w + * @param theme + */ +void SA_RIBBON_EXPORT sa_set_ribbon_theme(QWidget* w, SARibbonMainWindow::RibbonTheme theme); + +#endif // SARIBBONMAINWINDOW_H + +/*** End of inlined file: SARibbonMainWindow.h ***/ + +/*** End of inlined file: SARibbonAmalgamTemplatePublicHeaders.h ***/ + +#endif diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbon.pri b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbon.pri new file mode 100644 index 0000000000000000000000000000000000000000..c8e05f33001de7895a749f652faf50b84c9a8d8e --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbon.pri @@ -0,0 +1,51 @@ +#------------------------------------------------- +# +# 此文件给SARibbon静态编译使用 +# 适用于SARibbon.h/SARibbon.cpp这两个文件 +# 如果使用动态库,不要使用此文件 +# +# +# 使用方法见例子:src\example\SimpleExample\ +# +#------------------------------------------------- + + +###################################### +# Config | 配置 +###################################### +#SA_RIBBON_CONFIG 用于定义一些编译选项: +# SA_RIBBON_CONFIG+=use_frameless +# 此选项将使用frameless第三方库,这个选项在SARibbonBar.pri中会自动判断,如果,达到frameless的使用要求将会自动定义 +# frameless第三方库必须C++17且只有几个版本的qt可用,目前支持(qt5.14,qt5.15,qt6.4以上) +# 除了上诉版本SA_RIBBON_CONFIG中不会加入use_frameless +# frameless库能实现Ubuntu下和mac下的显示,同时多屏幕的支持也较好 +# 使用frameless库,需要定义QWindowKit的安装目录,默认在SARIBBON_BIN_DIR +# SA_RIBBON_QWindowKit_Install_DIR = $$SARIBBON_BIN_DIR +###################################### +# 集成模式默认不使用frameless,如果使用,需要自己引入qwk依赖 +SA_RIBBON_CONFIG -= use_frameless + + +# 这里判断SA_RIBBON_CONFIG是否包含use_frameless,如果包含将引入frameless库,并定义SARIBBON_USE_3RDPARTY_FRAMELESSHELPER为1 +contains( SA_RIBBON_CONFIG, use_frameless ) { + !contains(CONFIG,C++17){ + CONFIG += c++17 + } + # 定义SARIBBON_USE_3RDPARTY_FRAMELESSHELPER为1 + DEFINES += SARIBBON_USE_3RDPARTY_FRAMELESSHELPER=1 +}else{ + DEFINES += SARIBBON_USE_3RDPARTY_FRAMELESSHELPER=0 +} + + +SOURCES += \ + $$PWD/SARibbon.cpp + +HEADERS += \ + $$PWD/SARibbon.h + +RESOURCES += \ + $$PWD/SARibbonBar/resource.qrc + +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/CMakeLists.txt b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f558a02d991ba96dbdf93758d67a76b2e41cd38 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/CMakeLists.txt @@ -0,0 +1,59 @@ + +# Cmake的命令不区分打下写,例如message,set等命令;但Cmake的变量区分大小写 +# 为统一风格,本项目的Cmake命令全部采用小写,变量全部采用大写加下划线组合。 + +cmake_minimum_required(VERSION 3.5) +project(SARibbon-3rdparty-build + LANGUAGES CXX + DESCRIPTION "SARibbon : 3rdparty build" +) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +if(MSVC) + # CMAKE_CXX_STANDARD对有些版本的msvc无效 + set(CMAKE_CXX_FLAGS"${CMAKE_CXX_FLAGS} /std:c++17") +endif() +# 编译错误error C2039: “max”: 不是“std”的成员 +# 避免和min/max宏冲突 +add_definitions(-DNOMINMAX) + +######################################################## +# 安装路径设置 +######################################################## +# load Qt library, minimum version required is 5.12 +# cn:Qt库加载,最低版本要求为5.12 +set(SARIBBON_MIN_QT_VERSION 5.12) +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} ${SARIBBON_MIN_QT_VERSION} COMPONENTS + Core + Gui + Widgets + REQUIRED +) +# 平台判断 +if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") + set(SARIBBON_PLATFORM "x86") +else() + set(SARIBBON_PLATFORM "x64") +endif() +# The bin file directory is one level above the current directory +# cn:bin文件目录在当前目录的上上一级 +set(SARIBBON_BIN_NAME bin_qt${QT_VERSION}_${CMAKE_CXX_COMPILER_ID}_${SARIBBON_PLATFORM}) +set(SARIBBON_BIN_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../${SARIBBON_BIN_NAME}) +# windows系统下,默认直接安装到当前文件夹下 +if(WIN32) + set(CMAKE_INSTALL_PREFIX "${SARIBBON_BIN_DIR}") +endif() +######################################################## +# 第三方库参数设置 +######################################################## +# +set(QWINDOWKIT_BUILD_WIDGETS ON) +set(QWINDOWKIT_BUILD_EXAMPLES OFF) +set(QWINDOWKIT_BUILD_QUICK OFF) +set(QWINDOWKIT_INSTALL ON) +######################################################## +# 安装第三方库 +######################################################## +add_subdirectory(qwindowkit) diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/CMakeLists.txt.user b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/CMakeLists.txt.user new file mode 100644 index 0000000000000000000000000000000000000000..f672f628409376a001ff890a86ffcc5dac3924e9 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/CMakeLists.txt.user @@ -0,0 +1,422 @@ + + + + + + EnvironmentId + {eea41e89-5ee5-4719-acc7-c4f11a9daec2} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 8 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop Qt 5.15.2 MSVC2019 64bit + Desktop Qt 5.15.2 MSVC2019 64bit + qt.qt5.5152.win64_msvc2019_64_kit + 1 + 0 + 0 + + Debug + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + C:\Users\fawei\Documents\code\SARibbon\src\SARibbonBar\build-3rdparty-Desktop_Qt_5_15_2_MSVC2019_64bit-Debug + + + + + all + install + + false + + true + 构建 + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + 构建 + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + Release + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + C:\Users\fawei\Documents\code\SARibbon\src\SARibbonBar\build-3rdparty-Desktop_Qt_5_15_2_MSVC2019_64bit-Release + + + + + all + install + + false + + true + 构建 + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + 构建 + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + C:\Users\fawei\Documents\code\SARibbon\src\SARibbonBar\build-3rdparty-Desktop_Qt_5_15_2_MSVC2019_64bit-RelWithDebInfo + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + C:\Users\fawei\Documents\code\SARibbon\src\SARibbonBar\build-3rdparty-Desktop_Qt_5_15_2_MSVC2019_64bit-Profile + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + CMakeProjectManager.CMakeBuildConfiguration + + + MinSizeRel + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + C:\Users\fawei\Documents\code\SARibbon\src\SARibbonBar\build-3rdparty-Desktop_Qt_5_15_2_MSVC2019_64bit-MinSizeRel + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Minimum Size Release + CMakeProjectManager.CMakeBuildConfiguration + + 5 + + + 0 + 部署 + 部署 + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + + 2 + + false + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/.clang-format b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..e2f17207031d3791e91f1791e4f5c280bcf8f200 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/.clang-format @@ -0,0 +1,130 @@ +# References: +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# https://code.qt.io/cgit/qt/qt5.git/tree/_clang-format + +BasedOnStyle: LLVM + +Standard: c++17 + +# 指针和引用的对齐方式。 +# 可能的值有: +# PAS_Left (在配置中: Left) 指针左对齐。 +# PAS_Right (在配置中: Right) 指针右对齐。 +# PAS_Middle (在配置中: Middle) 指针中间对齐。 +PointerAlignment: Right + +# public/protected/private 等访问修饰符偏移量 +AccessModifierOffset: -4 + +# 缩进长度 +IndentWidth: 4 + +# 连续空行的最大数 +MaxEmptyLinesToKeep: 999 + +# 在OC中的@property后面添加一个空格。例如:使用“@property (readonly)”而不是“@property(readonly)” +ObjCSpaceAfterProperty: true + +# OC块中所拍的字符数 +ObjCBlockIndentWidth: 4 + +# 取决于值, 语句“int f() { return 0; }”可以被放到一个单行。 +# 可能的值有: +# SFS_None (在配置中: None) 从不合并方法或函数到单独的一行。 +# SFS_Empty (在配置中: Empty) 仅合并空的函数。 +# SFS_Inline (在配置中: Inline) 仅合并类中定义的方法或函数. 意味着 “empty”. +# SFS_All (在配置中: All) 合并所有的方法适应单行. +AllowShortFunctionsOnASingleLine: None + +# 如果为真(true), 语句“if (a) return;” 能被放到单行。 +AllowShortIfStatementsOnASingleLine: false + +# 如果为真(true), 对齐注释。 +AlignTrailingComments: true + +# 如果为真,对齐连续的宏定义 +AlignConsecutiveMacros: true + +# 如果为真(true),将会在“[”之后和“]”之前插入空格。 +SpacesInSquareBrackets: false + +# 如果为真(true), 将会在“(”之后和“)”之前插入空格。 +SpacesInParentheses : false + +# 如果为真(true), 校准连续的声明。 +# 这将会校准连续多行的声明的名字。这将会导致像下面这样的格式: +# int aaaa = 12; +# float b = 23; +# std::string ccc = 23; +AlignConsecutiveDeclarations: false + +# 如果为真(true),连续调整多行 +# 这将会调整连续行中的分配操作符。这将会导致像下面这样的格式: +# int aaaa = 12; +# int b = 23; +# int ccc = 23; +AlignConsecutiveAssignments: false + +# 如果为假(false),移除分配操作符(=)前空格。 +SpaceBeforeAssignmentOperators: true + +# 如果为真(true), 将会在字面量容器中插入空格(例如 OC和Javascript的数组和字典字面量)。 +SpacesInContainerLiterals: false + +# 缩进case标签 +IndentCaseLabels: true + +# 如果表达式中包含函数调用,并且函数调用因为表达式太长被放到了下一行,是否缩进 +IndentWrappedFunctionNames: true + +# 如果为真(true), 保持块的起始空行。 +# true: false: +# if (foo) { vs. if (foo) { +# bar(); +# bar(); } +# } +KeepEmptyLinesAtTheStartOfBlocks: true + +# 允许所有参数都被放在下一行 +AllowAllParametersOfDeclarationOnNextLine: false + +# 使用C风格强制类型转换后,是否在中间添加一个空格 +SpaceAfterCStyleCast: true + +# 在模板定义后换行 +AlwaysBreakTemplateDeclarations: Yes + +# Tab长度 +TabWidth: 4 + +# 是否使用Tab +UseTab: Never + +# 在括号后对齐参数 +# someLongFunction(argument1, +# argument2); +AlignAfterOpenBracket: Align + +# 名字空间内部缩进 +NamespaceIndentation: All + +# 一行最长列数 +ColumnLimit: 100 + +# 按层次缩进宏定义 +IndentPPDirectives: AfterHash + +# 预处理语句缩进为 2 +PPIndentWidth: 2 + +# 数组元素对齐 +AlignArrayOfStructures: Left + +# 不对头文件排序 +SortIncludes: Never + +FixNamespaceComments: false + +StatementMacros: ['__qas_attr__', '__qas_exclude__', '__qas_include__'] + +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/.gitignore b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..04490f0e36d233401bd68041327647e290b7156e --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/.gitignore @@ -0,0 +1,98 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.log +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +# *.res +# *.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +__pycache__ +*.pyc + +# Binaries +# -------- +*.dll +*.exe + +.DS_Store +*/.DS_Store + +build-src* +build/ +cmake-build* +*.user +*.lnk +_workingDir* +.vscode +.idea +.cache +cache +.vs +out/ +CMakeSettings.json +# /vcpkg +/data +/*.natvis + +*.sublime-* +setup-vcpkg.json +setup-vcpkg-temp* \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/.gitmodules b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..1d7f9e5d8ee3c1c7a6ce7632a313a9874f56d3e9 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/.gitmodules @@ -0,0 +1,4 @@ +[submodule "qmsetup"] + path = qmsetup + url = ../../stdware/qmsetup.git + branch = main \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/CMakeLists.txt b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0fb89c8db6f11ca185d434b3351b982b349e8e5c --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/CMakeLists.txt @@ -0,0 +1,84 @@ +cmake_minimum_required(VERSION 3.19) + +project(QWindowKit VERSION 1.0.1.0 LANGUAGES CXX) + +# ---------------------------------- +# Build Options +# ---------------------------------- +option(QWINDOWKIT_BUILD_STATIC "Build static libraries" OFF) +option(QWINDOWKIT_BUILD_WIDGETS "Build widgets module" ON) +option(QWINDOWKIT_BUILD_QUICK "Build quick module" ON) +option(QWINDOWKIT_BUILD_EXAMPLES "Build examples" OFF) +option(QWINDOWKIT_BUILD_DOCUMENTATIONS "Build documentations" OFF) +option(QWINDOWKIT_INSTALL "Install library" ON) + +option(QWINDOWKIT_ENABLE_QT_WINDOW_CONTEXT "Enable Qt Window Context anyway" OFF) +option(QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS "Enable system borders on Windows" ON) +option(QWINDOWKIT_ENABLE_STYLE_AGENT "Enable building style agent" ON) + +# ---------------------------------- +# CMake Settings +# ---------------------------------- +if(MSVC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /manifest:no") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") + + if(NOT DEFINED CMAKE_DEBUG_POSTFIX) + set(CMAKE_DEBUG_POSTFIX "d") + endif() +elseif(MINGW) + set(CMAKE_STATIC_LIBRARY_PREFIX "") + set(CMAKE_SHARED_LIBRARY_PREFIX "") +endif() + +if(QWINDOWKIT_INSTALL) + include(GNUInstallDirs) + include(CMakePackageConfigHelpers) +endif() + +# ---------------------------------- +# Project Variables +# ---------------------------------- +set(QWINDOWKIT_VERSION ${PROJECT_VERSION}) +set(QWINDOWKIT_INSTALL_NAME ${PROJECT_NAME}) + +# ---------------------------------- +# Find basic dependencies +# ---------------------------------- +find_package(qmsetup QUIET) + +if(NOT TARGET qmsetup::library) + # Modify this variable according to your project structure + set(_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/qmsetup) + + # Import install function + include("${_source_dir}/cmake/modules/InstallPackage.cmake") + + # Install package in place + set(_package_path) + qm_install_package(qmsetup + SOURCE_DIR ${_source_dir} + BUILD_TYPE Release + RESULT_PATH _package_path + ) + + # Find package again + find_package(qmsetup REQUIRED PATHS ${_package_path}) + + # Update import path + set(qmsetup_DIR ${_package_path} CACHE PATH "" FORCE) +endif() + +qm_import(Filesystem) +qm_init_directories() + +# ---------------------------------- +# Add source modules +# ---------------------------------- +add_subdirectory(src) + +if(QWINDOWKIT_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/LICENSE b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..36395aa97778f042cf02bd327081623a7ca03a5f --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) + Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/README.md b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/README.md new file mode 100644 index 0000000000000000000000000000000000000000..39a33fa147a1801f58352e2d14903c13e993d005 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/README.md @@ -0,0 +1,279 @@ +# QWindowKit + +Cross-platform window customization framework for Qt Widgets and Qt Quick. + +This project inherited most of [wangwenx190 FramelessHelper](https://github.com/wangwenx190/framelesshelper) +implementation, with a complete refactoring and upgrading of the architecture. + +Feature requests are welcome. + +## Join with Us :triangular_flag_on_post: + +You can join our [Discord channel](https://discord.gg/grrM4Tmesy). You can share your findings, thoughts and ideas on improving / implementing FramelessHelper functionalities on more platforms and apps! + +## Supported Platforms + ++ Microsoft Windows ++ Apple macOS (11+) ++ GNU/Linux + +## Features + ++ Full support of Windows 11 Snap Layout ++ Better workaround to handle Windows 10 top border issue ++ Support Mac system buttons geometry customization ++ Simpler APIs, more detailed documentations and comments + +## Gallery + +### Windows 11 (With Snap Layout) + +![image](./docs/images/win11.png) + +### Windows 10 (And 7, Vista) + +![image](./docs/images/win10.png) + +### macOS & Linux + +| macOS | Linux (Ubuntu 20.04) | +|:-------------------------------:|:---------------------------------:| +| ![image](./docs/images/mac.png) | ![image](./docs/images/linux.png) | + +## Requirements + +| Component | Requirement | Details | +|:---------:|:-----------:|:-------------------------:| +| Qt | \>=5.12 | Core, Gui, Widgets, Quick | +| Compiler | \>=C++17 | MSVC 2019, GCC, Clang | +| CMake | \>=3.19 | >=3.20 is recommended | + +### Tested Compilers + ++ Windows + + MSVC: 2019, 2022 + + MinGW (GCC): 13.2.0 ++ macOS + + Clang 14.0.3 ++ Ubuntu + + GCC: 9.4.0 + +## Dependencies + ++ Qt 5.12 or higher ++ [qmsetup](https://github.com/stdware/qmsetup) + +## Integrate + +### Configure Options + ++ `QWINDOWKIT_BUILD_DOCUMENTATIONS` + + If you have installed `Doxygen`, you can **enable** this option so that the documentations will also be built and installed. + + If not, you can read the comments in *qdoc* style in `cpp` files to get detailed usages of the public APIs. + ++ `QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS` + + If you don't want the system borders on Windows 10/11, you can **disable** this option. + + If so, the Windows 10 top border issue will disappear. However, part of the client edge area will be occupied as the resizing margins. + ++ `QWINDOWKIT_ENABLE_QT_WINDOW_CONTEXT` + + If you want to use pure Qt emulated frameless implementation, you can **enable** this option. + + If so, all system native features will be lost. + ++ `QWINDOWKIT_ENABLE_STYLE_AGENT` + + Select whether to exclude the style component by **disabling** this option according to your requirements and your Qt version. + +### Build & Install + +```sh +git clone --recursive https://github.com/stdware/qwindowkit +cd qwindowkit + +cmake -B build -S . \ + -Dqmsetup_DIR= \ # Optional + -DCMAKE_INSTALL_PREFIX=/path/install \ + -G "Ninja Multi-Config" + +cmake --build build --target install --config Debug +cmake --build build --target install --config Release +``` + +You can also include this directory as a subproject if you choose CMake as your build system. + +For other build systems, you need to install with CMake first and include the corresponding configuration files in your +project. + +### Import + +#### CMake Project + +```sh +cmake -B build -DQWindowKit_DIR=/path/install/cmake/QWindowKit +``` + +```cmake +find_package(QWindowKit REQUIRED) +target_link_libraries(widgets_app PUBLIC QWindowKit::Widgets) +target_link_libraries(quick_app PUBLIC QWindowKit::Quick) +``` + +#### QMake Project + +```cmake +# WidgetsApp.pro +include("/path/install/share/QWindowKit/qmake/QWKWidgets.pri") + +# QuickApp.pro +include("/path/install/share/QWindowKit/qmake/QWKQuick.pri") +``` + +#### Visual Studio Project + +TODO + +## Quick Start + +### Qt Widgets Application + +#### Setup Window Agent + +First, setup `WidgetWindowAgent` for your top `QWidget` instance. (Each window needs its own agent.) + +```c++ +#include + +MyWidget::MyWidget(QWidget *parent) { + // ... + auto agent = new QWK::WidgetWindowAgent(this); + agent->setup(this); + // ... +} +``` + +If you don't want to derive a new widget class or change the constructor, you can initialize the agent after the window +constructs. + +```c++ +auto w = new MyWidget(); +auto agent = new QWK::WidgetWindowAgent(w); +agent->setup(w); +``` + +#### Construct Title bar + +Then, construct your title bar widget, without which the window lacks the basic interaction feature, and it's better to +put it into the window's layout. + +You can use the [`WindowBar`](examples/shared/widgetframe/windowbar.h) provided by `WidgetFrame` in the examples as the +container of your title bar components. + +Let `WidgetWindowAgent` know which widget the title bar is. + +```c++ +agent->setTitleBar(myTitleBar); +``` + +Next, set system button hints to let `WidgetWindowAgent` know the role of the child widgets, which is important for the +Snap Layout to work. + +```c++ +agent->setSystemButton(QWK::WindowAgentBase::WindowIcon, myTitleBar->iconButton()); +agent->setSystemButton(QWK::WindowAgentBase::Minimize, myTitleBar->minButton()); +agent->setSystemButton(QWK::WindowAgentBase::Maximize, myTitleBar->maxButton()); +agent->setSystemButton(QWK::WindowAgentBase::Close, myTitleBar->closeButton()); +``` + +Doing this does not mean that these buttons' click events are automatically associated with window actions, you still need to manually connect the signals and slots to emulate the native window behaviors. + +On macOS, this step can be skipped because it is better to use the buttons provided by the system. + +Last but not least, set hit-test visible hint to let `WidgetWindowAgent` know other widgets that desire to receive mouse events. + +```c++ +agent->setHitTestVisible(myTitleBar->menuBar(), true); +``` + +The rest region within the title bar will be regarded as the draggable area for the user to move the window. + + + +### Qt Quick Application + +#### Initialization + +Make sure you have registered `QWindowKit` into QtQuick: + +```cpp +#include + +int main(int argc, char *argv[]) +{ + // ... + QQmlApplicationEngine engine; + // ... + QWK::registerTypes(&engine); + // ... +} +``` + +#### Setup Window Components + +Then you can use `QWindowKit` data types and classes by importing it's URI: + +```qml +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QWindowKit 1.0 + +Window { + id: window + visible: false // We hide it first, so we can move the window to our desired position silently. + Component.onCompleted: { + windowAgent.setup(window) + window.visible = true + } + WindowAgent { + id: windowAgent + // ... + } +} +``` + +You can omit the version number or use "auto" instead of "1.0" for the module URI if you are using Qt6. + +### Learn More + +See [examples](examples) for more demo use cases. The examples have no High DPI support. + ++ QWindowKit Internals [TODO] ++ [FramelessHelper Related](docs/framelesshelper-related.md) + +## TODO + ++ Fix 5.15 window abnormal behavior ++ More documentations ++ When do we support Linux native features? + +## Special Thanks + ++ [Maplespe](https://github.com/Maplespe) ++ [zhiyiYo](https://github.com/zhiyiYo) + +## License + +QWindowKit is licensed under the [Apache 2.0 License](./LICENSE). + + diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/framelesshelper-related.md b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/framelesshelper-related.md new file mode 100644 index 0000000000000000000000000000000000000000..67bb7b73c02fc72eedcecdd785ab85dbd407df91 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/framelesshelper-related.md @@ -0,0 +1,89 @@ +# FramelessHelper 2.x + +Cross-platform window customization framework for Qt Widgets and Qt Quick. Supports Windows, Linux and macOS. + +## Join with Us :triangular_flag_on_post: + +You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate with us. You can share your findings, thoughts and ideas on improving / implementing FramelessHelper functionalities on more platforms and apps! + +## More + +### Title Bar Design Guidance + +- Microsoft: +- KDE: +- GNOME: +- Apple: + +## Platform Notes + +### Windows + +- If DWM composition is disabled in some very rare cases (only possible on Windows 7), the top-left corner and top-right corner will appear in round shape. The round corners can be restored to square if you re-enable DWM composition. +- There's an OpenGL driver bug which will cause some frameless windows have a strange black bar right on top of your homemade title bar, and it also makes the controls in your windows shifted to the bottom-right corner for some pixels. It's a bug of your graphics card driver, specifically, your OpenGL driver, not FramelessHelper. There are some solutions provided by our users but some of them may not work in all conditions, you can pick one from them: + + Solution | Principle + -------- | --------- + Upgrade the graphics driver | Try to use a newer driver which may ship with the fix + Change the system theme to "Basic" (in contrary to "Windows Aero") | Let Windows use pure software rendering + If there are multiple graphics cards, use another one instead | Try to use a different driver which may don't have such bug at all + Upgrade the system to at least Windows 11 | Windows 11 redesigned the windowing system so the bug can no longer be triggered + Remove the `WS_THICKFRAME` and `WS_OVERLAPPED` styles from the window, and maybe also add the `WS_POPUP` style at the same time, and don't do anything inside the `WM_NCCALCSIZE` block (just return `false` directly or remove/comment out the whole block) | Try to mirror Qt's `FramelessWindowHint`'s behavior + Use `Qt::FramelessWindowHint` instead of doing the `WM_NCCALCSIZE` trick | Qt's rendering code path is totally different between these two solutions + Force Qt to use the ANGLE backend instead of the Desktop OpenGL | ANGLE will translate OpenGL directives into D3D ones + Force Qt to use pure software rendering instead of rendering through OpenGL | Qt is not using OpenGL at all + Force Qt to use the Mesa 3D libraries instead of normal OpenGL | Try to use a different OpenGL implementation + Use Direct3D/Vulkan/Metal instead of OpenGL | Just don't use the buggy OpenGL + + If you are lucky enough, one of them may fix the issue for you. If not, you may try to use multiple solutions together. **But I can't guarantee the issue can 100% be fixed.** +- Due to there are many sub-versions of Windows 10, it's highly recommended to use the latest version of Windows 10, at least **no older than Windows 10 1809**. If you try to use this framework on some very old Windows 10 versions such as 1507 or 1607, there may be some compatibility issues. Using this framework on Windows 7 is also supported but not recommended. To get the most stable behavior and the best appearance, you should use it on the latest version of Windows 10 or Windows 11. +- To make the snap layout work as expected, there are some additional rules for your homemade system buttons to follow: + - **Add a manifest file to your application. In the manifest file, you need to claim your application supports Windows 11 explicitly. This step is VERY VERY IMPORTANT. Without this step, the snap layout feature can't be enabled.** + - Call `setSystemButton()` for each button (it can be any *QWidget* or *QQuickItem*) to let FramelessHelper know which is the minimize/maximize/close button. + +### Linux + +- FramelessHelper will force your application to use the _XCB_ platform plugin when running on Wayland. +- The resize area is inside of the window. + +### macOS + +- Some users reported that the window is not resizable on some old macOS versions. + +## Special Thanks + +*Ordered by first contribution time (it may not be very accurate, sorry)* + +- [Yuhang Zhao](https://github.com/wangwenx190): Help me create this project. This project is mainly based on his code. +- [Julien](https://github.com/JulienMaille): Help me test this library on many various environments and help me fix the bugs we found. Contributed many code to improve this library. The MainWindow example is mostly based on his code. +- [Altair Wei](https://github.com/altairwei): Help me fix quite some small bugs and give me many important suggestions, the 2.x version is also inspired by his idea during our discussions. +- [Kenji Mouri](https://github.com/MouriNaruto): Give me a lot of help on Win32 native developing. +- [Dylan Liu](https://github.com/mentalfl0w): Help me improve the build process on macOS. +- [SineStriker](https://github.com/SineStriker): Spent over a whole week helping me improve the Snap Layout implementation, fixing potential bugs and also give me a lot of professional and useful suggestions. Without his great effort, the new implementation may never come. +- And also thanks to other contributors not listed here! Without their valuable help, this library wouldn't have such good quality and user experience! + +## License + +```text +MIT License + +Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/linux.png b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/linux.png new file mode 100644 index 0000000000000000000000000000000000000000..9ccd8b6ed8828b50256d46fa038a368dd3e0d651 Binary files /dev/null and b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/linux.png differ diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/mac.png b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/mac.png new file mode 100644 index 0000000000000000000000000000000000000000..f4aecca86a657ff4cc57e50b7e41ca548f6117bb Binary files /dev/null and b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/mac.png differ diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/win10.png b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/win10.png new file mode 100644 index 0000000000000000000000000000000000000000..7975626a45111b26204dd21c3ec2097d9814b17f Binary files /dev/null and b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/win10.png differ diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/win11.png b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/win11.png new file mode 100644 index 0000000000000000000000000000000000000000..af54f3d63e384f018486e5d942124a72fa992ff3 Binary files /dev/null and b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/docs/images/win11.png differ diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/CMakeLists.txt b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5d271813b9aededcce26ba36a9aa2e1db24f5223 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/CMakeLists.txt @@ -0,0 +1,23 @@ +set(QWK_EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +macro(qwk_add_example _target) + set(CMAKE_AUTOMOC ON) + set(CMAKE_AUTOUIC ON) + set(CMAKE_AUTORCC ON) + + add_executable(${_target}) + qm_configure_target(${_target} ${ARGN}) + qm_add_win_rc(${_target} ICON ${QWK_EXAMPLES_DIR}/shared/resources/app/example.ico) + qm_add_win_manifest(${_target}) + qm_add_mac_bundle(${_target} ICON ${QWK_EXAMPLES_DIR}/shared/resources/app/example.icns) +endmacro() + +add_subdirectory(shared) + +if(QWINDOWKIT_BUILD_WIDGETS) + add_subdirectory(mainwindow) +endif() + +if(QWINDOWKIT_BUILD_QUICK) + add_subdirectory(qml) +endif() \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/CMakeLists.txt b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c29f2b803feff31b3ef5faa22d2131950b7fe1ca --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/CMakeLists.txt @@ -0,0 +1,14 @@ +project(QWKExample_MainWindow) + +file(GLOB _src *.h *.cpp) + +qwk_add_example(${PROJECT_NAME} + SOURCES ${_src} mainwindow.qrc ../shared/resources/shared.qrc + QT_LINKS Core Gui Widgets + LINKS QWKWidgets WidgetFrame +) + +set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED TRUE +) diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/dark-style.qss b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/dark-style.qss new file mode 100644 index 0000000000000000000000000000000000000000..e01c0dc143959882bc9b85316b1299ef00fb3332 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/dark-style.qss @@ -0,0 +1,158 @@ +/* Window bar */ + +QWK--WindowBar[bar-active=true] { + background-color: #3C3C3C; +} + +QWK--WindowBar[bar-active=false] { + background-color: #505050; +} + + +/* Title label */ + +QWK--WindowBar>QLabel#win-title-label { + padding: 0; + border: none; + color: #ECECEC; + background-color: transparent; + min-height: 28px; +} + + +/* System buttons */ + +QWK--WindowBar>QAbstractButton[system-button=true] { + qproperty-iconSize: 12px 12px; + min-width: 50px; + border: none; + padding: 0; + background-color: transparent; +} + +QWK--WindowBar>QAbstractButton#min-button { + qproperty-iconNormal: url(":/window-bar/minimize.svg"); + qproperty-iconSize: 12px 12px; +} + +QWK--WindowBar>QAbstractButton#min-button:hover, +QWK--WindowBar>QAbstractButton#min-button:pressed { + background-color: rgba(255, 255, 255, 15%); +} + +QWK--WindowBar>QAbstractButton#max-button { + qproperty-iconNormal: url(":/window-bar/maximize.svg"); + qproperty-iconChecked: url(":/window-bar/restore.svg"); +} + +QWK--WindowBar>QAbstractButton#max-button:hover, +QWK--WindowBar>QAbstractButton#max-button:pressed { + background-color: rgba(255, 255, 255, 15%); +} + +QWK--WindowBar>QAbstractButton#close-button { + qproperty-iconNormal: url(":/window-bar/close.svg"); +} + +QWK--WindowBar>QAbstractButton#close-button:hover, +QWK--WindowBar>QAbstractButton#close-button:pressed { + background-color: #e81123; +} + + +/* Icon button */ + +QWK--WindowBar>QAbstractButton#icon-button { + qproperty-iconNormal: url(":/app/example.png"); + qproperty-iconSize: 18px 18px; + min-width: 40px; + border: none; + padding: 0; + background-color: transparent; +} + + +/* Menu Bar */ + +QMenuBar { + background-color: transparent; + border: none; +} + +QMenuBar>QToolButton#qt_menubar_ext_button { + qproperty-icon: url(":/window-bar/more-line.svg"); +} + +QMenuBar>QToolButton#qt_menubar_ext_button:hover, +QMenuBar>QToolButton#qt_menubar_ext_button:pressed { + background-color: rgba(255, 255, 255, 10%); +} + +QMenuBar::item { + color: #CCCCCC; + border: none; + padding: 8px 12px; +} + +QMenuBar::item:selected { + background-color: rgba(255, 255, 255, 10%); +} + + +/* Menu */ + +QMenu { + padding: 4px; + background: #303030; + border: 1px solid transparent; +} + +QMenu::indicator { + left: 6px; + width: 20px; + height: 20px; +} + +QMenu::icon { + left: 6px; +} + +QMenu::item { + background: transparent; + color: #CCCCCC; + padding: 6px 24px; +} + +QMenu::item:selected { + color: white; + background-color: #0060C0; +} + +QMenu::item:disabled { + color: #666666; + background-color: transparent; +} + +QMenu::separator { + height: 2px; + background-color: #5B5B5B; + margin: 6px 0; +} + + +/* Window */ + +MainWindow { + background-color: #1E1E1E; +} + +MainWindow[custom-style=true] { + background-color: transparent; +} + +QWidget#clock-widget { + font-size: 75px; + color: #FEFEFE; + font-weight: bold; + background-color: transparent; +} \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/light-style.qss b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/light-style.qss new file mode 100644 index 0000000000000000000000000000000000000000..c314c7fe68f5d3c695822128594435d8da22464e --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/light-style.qss @@ -0,0 +1,156 @@ +/* Window bar */ + +QWK--WindowBar[bar-active=true] { + background-color: #195ABE; +} + +QWK--WindowBar[bar-active=false] { + background-color: #195ABE; +} + + +/* Title label */ + +QWK--WindowBar>QLabel#win-title-label { + padding: 0; + border: none; + color: #ECECEC; + background-color: transparent; + min-height: 28px; +} + + +/* System buttons */ + +QWK--WindowBar>QAbstractButton[system-button=true] { + qproperty-iconSize: 12px 12px; + min-width: 50px; + border: none; + padding: 0; + background-color: transparent; +} + +QWK--WindowBar>QAbstractButton#min-button { + qproperty-iconNormal: url(":/window-bar/minimize.svg"); + qproperty-iconSize: 12px 12px; +} + +QWK--WindowBar>QAbstractButton#min-button:hover, +QWK--WindowBar>QAbstractButton#min-button:pressed { + background-color: rgba(0, 0, 0, 15%); +} + +QWK--WindowBar>QAbstractButton#max-button { + qproperty-iconNormal: url(":/window-bar/maximize.svg"); + qproperty-iconChecked: url(":/window-bar/restore.svg"); +} + +QWK--WindowBar>QAbstractButton#max-button:hover, +QWK--WindowBar>QAbstractButton#max-button:pressed { + background-color: rgba(0, 0, 0, 15%); +} + +QWK--WindowBar>QAbstractButton#close-button { + qproperty-iconNormal: url(":/window-bar/close.svg"); +} + +QWK--WindowBar>QAbstractButton#close-button:hover, +QWK--WindowBar>QAbstractButton#close-button:pressed { + background-color: #e81123; +} + + +/* Icon button */ + +QWK--WindowBar>QAbstractButton#icon-button { + qproperty-iconNormal: url(":/app/example.png"); + qproperty-iconSize: 18px 18px; + min-width: 40px; + border: none; + padding: 0; + background-color: transparent; +} + + +/* Menu Bar */ + +QMenuBar { + background-color: transparent; + border: none; +} + +QMenuBar>QToolButton#qt_menubar_ext_button { + qproperty-icon: url(":/window-bar/more-line.svg"); +} + +QMenuBar>QToolButton#qt_menubar_ext_button:hover, +QMenuBar>QToolButton#qt_menubar_ext_button:pressed { + background-color: rgba(255, 255, 255, 10%); +} + +QMenuBar::item { + color: #EEEEEE; + border: none; + padding: 8px 12px; +} + +QMenuBar::item:selected { + background-color: rgba(255, 255, 255, 10%); +} + + +/* Menu */ + +QMenu { + padding: 4px; + background: white; + border: 1px solid #E0E0E0; +} + +QMenu::indicator { + left: 6px; + width: 20px; + height: 20px; +} + +QMenu::icon { + left: 6px; +} + +QMenu::item { + background: transparent; + color: #333333; + padding: 6px 24px; +} + +QMenu::item:selected { + background-color: rgba(0, 0, 0, 10%); +} + +QMenu::item:disabled { + color: #CCCCCC; +} + +QMenu::separator { + height: 2px; + background-color: #CCCCCC; + margin: 6px 0; +} + + +/* Window */ + +MainWindow { + background-color: #F3F3F3; +} + +MainWindow[custom-style=true] { + background-color: transparent; +} + +QWidget#clock-widget { + font-size: 75px; + color: #333333; + font-weight: bold; + background-color: transparent; +} \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/main.cpp b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..002360e749a6c90ac032a099b84b12669d26647c --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/main.cpp @@ -0,0 +1,36 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include "mainwindow.h" + +int main(int argc, char *argv[]) { + qputenv("QT_WIN_DEBUG_CONSOLE", "attach"); + qputenv("QSG_INFO", "1"); +#if 0 + qputenv("QT_WIDGETS_RHI", "1"); + qputenv("QSG_RHI_BACKEND", "d3d12"); + qputenv("QSG_RHI_HDR", "scrgb"); + qputenv("QT_QPA_DISABLE_REDIRECTION_SURFACE", "1"); + + QGuiApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif + + QApplication a(argc, argv); + +#if 0 && defined(Q_OS_WINDOWS) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QApplication::setFont([]() { + QFont f("Microsoft YaHei"); + f.setStyleStrategy(QFont::PreferAntialias); + f.setPixelSize(15); + return f; + }()); +#endif + + MainWindow w; + w.show(); + return a.exec(); +} \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/mainwindow.cpp b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/mainwindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c27bdddd01693613f2b748ede8e71d5494741674 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/mainwindow.cpp @@ -0,0 +1,354 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#include "mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +# include +#else +# include +#endif + +#include + +#include +#include + +class ClockWidget : public QLabel { +public: + explicit ClockWidget(QWidget *parent = nullptr) : QLabel(parent) { + startTimer(100); + setAlignment(Qt::AlignCenter); + } + + ~ClockWidget() override = default; + +protected: + void timerEvent(QTimerEvent *event) override { + setText(QTime::currentTime().toString(QStringLiteral("hh:mm:ss"))); + } +}; + +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { + installWindowAgent(); + + auto clockWidget = new ClockWidget(); + clockWidget->setObjectName(QStringLiteral("clock-widget")); + clockWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setCentralWidget(clockWidget); + + loadStyleSheet(Dark); + + setWindowTitle(tr("Example MainWindow")); + resize(800, 600); +} + +static inline void emulateLeaveEvent(QWidget *widget) { + Q_ASSERT(widget); + if (!widget) { + return; + } + QTimer::singleShot(0, widget, [widget]() { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + const QScreen *screen = widget->screen(); +#else + const QScreen *screen = widget->windowHandle()->screen(); +#endif + const QPoint globalPos = QCursor::pos(screen); + if (!QRect(widget->mapToGlobal(QPoint{0, 0}), widget->size()).contains(globalPos)) { + QCoreApplication::postEvent(widget, new QEvent(QEvent::Leave)); + if (widget->testAttribute(Qt::WA_Hover)) { + const QPoint localPos = widget->mapFromGlobal(globalPos); + const QPoint scenePos = widget->window()->mapFromGlobal(globalPos); + static constexpr const auto oldPos = QPoint{}; + const Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) + const auto event = + new QHoverEvent(QEvent::HoverLeave, scenePos, globalPos, oldPos, modifiers); + Q_UNUSED(localPos); +#elif (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)) + const auto event = new QHoverEvent(QEvent::HoverLeave, localPos, globalPos, oldPos, modifiers); + Q_UNUSED(scenePos); +#else + const auto event = new QHoverEvent(QEvent::HoverLeave, localPos, oldPos, modifiers); + Q_UNUSED(scenePos); +#endif + QCoreApplication::postEvent(widget, event); + } + } + }); +} + +MainWindow::~MainWindow() = default; + +bool MainWindow::event(QEvent *event) { + switch (event->type()) { + case QEvent::WindowActivate: { + auto menu = menuWidget(); + menu->setProperty("bar-active", true); + style()->polish(menu); + break; + } + + case QEvent::WindowDeactivate: { + auto menu = menuWidget(); + menu->setProperty("bar-active", false); + style()->polish(menu); + break; + } + + default: + break; + } + return QMainWindow::event(event); +} + +void MainWindow::installWindowAgent() { + // 1. Setup window agent + windowAgent = new QWK::WidgetWindowAgent(this); + windowAgent->setup(this); + + // 2. Construct your title bar + auto menuBar = [this]() { + auto menuBar = new QMenuBar(); + + // Virtual menu + auto file = new QMenu(tr("File(&F)"), menuBar); + file->addAction(new QAction(tr("New(&N)"), menuBar)); + file->addAction(new QAction(tr("Open(&O)"), menuBar)); + file->addSeparator(); + + auto edit = new QMenu(tr("Edit(&E)"), menuBar); + edit->addAction(new QAction(tr("Undo(&U)"), menuBar)); + edit->addAction(new QAction(tr("Redo(&R)"), menuBar)); + + // Theme action + auto darkAction = new QAction(tr("Enable dark theme"), menuBar); + darkAction->setCheckable(true); + connect(darkAction, &QAction::triggered, this, [this](bool checked) { + loadStyleSheet(checked ? Dark : Light); // + }); + connect(this, &MainWindow::themeChanged, darkAction, [this, darkAction]() { + darkAction->setChecked(currentTheme == Dark); // + }); + +#ifdef Q_OS_WIN + auto dwmBlurAction = new QAction(tr("Enable DWM blur"), menuBar); + dwmBlurAction->setCheckable(true); + connect(dwmBlurAction, &QAction::toggled, this, [this](bool checked) { + if (!windowAgent->setWindowAttribute(QStringLiteral("dwm-blur"), checked)) { + return; + } + setProperty("custom-style", checked); + style()->polish(this); + }); + + auto acrylicAction = new QAction(tr("Enable acrylic material"), menuBar); + acrylicAction->setCheckable(true); + connect(acrylicAction, &QAction::toggled, this, [this](bool checked) { + if (!windowAgent->setWindowAttribute(QStringLiteral("acrylic-material"), true)) { + return; + } + setProperty("custom-style", checked); + style()->polish(this); + }); + + auto micaAction = new QAction(tr("Enable mica"), menuBar); + micaAction->setCheckable(true); + connect(micaAction, &QAction::toggled, this, [this](bool checked) { + if (!windowAgent->setWindowAttribute(QStringLiteral("mica"), checked)) { + return; + } + setProperty("custom-style", checked); + style()->polish(this); + }); + + auto micaAltAction = new QAction(tr("Enable mica alt"), menuBar); + micaAltAction->setCheckable(true); + connect(micaAltAction, &QAction::toggled, this, [this](bool checked) { + if (!windowAgent->setWindowAttribute(QStringLiteral("mica-alt"), checked)) { + return; + } + setProperty("custom-style", checked); + style()->polish(this); + }); +#elif defined(Q_OS_MAC) + auto darkBlurAction = new QAction(tr("Dark blur"), menuBar); + darkBlurAction->setCheckable(true); + connect(darkBlurAction, &QAction::toggled, this, [this](bool checked) { + if (!windowAgent->setWindowAttribute(QStringLiteral("blur-effect"), "dark")) { + return; + } + if (checked) { + setProperty("custom-style", true); + style()->polish(this); + } + }); + + auto lightBlurAction = new QAction(tr("Light blur"), menuBar); + lightBlurAction->setCheckable(true); + connect(lightBlurAction, &QAction::toggled, this, [this](bool checked) { + if (!windowAgent->setWindowAttribute(QStringLiteral("blur-effect"), "light")) { + return; + } + if (checked) { + setProperty("custom-style", true); + style()->polish(this); + } + }); + + auto noBlurAction = new QAction(tr("No blur"), menuBar); + noBlurAction->setCheckable(true); + connect(noBlurAction, &QAction::toggled, this, [this](bool checked) { + if (!windowAgent->setWindowAttribute(QStringLiteral("blur-effect"), "none")) { + return; + } + if (checked) { + setProperty("custom-style", false); + style()->polish(this); + } + }); + + auto macStyleGroup = new QActionGroup(menuBar); + macStyleGroup->addAction(darkBlurAction); + macStyleGroup->addAction(lightBlurAction); + macStyleGroup->addAction(noBlurAction); +#endif + + // Real menu + auto settings = new QMenu(tr("Settings(&S)"), menuBar); + settings->addAction(darkAction); + +#ifdef Q_OS_WIN + settings->addSeparator(); + settings->addAction(dwmBlurAction); + settings->addAction(acrylicAction); + settings->addAction(micaAction); + settings->addAction(micaAltAction); +#elif defined(Q_OS_MAC) + settings->addAction(darkBlurAction); + settings->addAction(lightBlurAction); + settings->addAction(noBlurAction); +#endif + + menuBar->addMenu(file); + menuBar->addMenu(edit); + menuBar->addMenu(settings); + return menuBar; + }(); + menuBar->setObjectName(QStringLiteral("win-menu-bar")); + + auto titleLabel = new QLabel(); + titleLabel->setAlignment(Qt::AlignCenter); + titleLabel->setObjectName(QStringLiteral("win-title-label")); + +#ifndef Q_OS_MAC + auto iconButton = new QWK::WindowButton(); + iconButton->setObjectName(QStringLiteral("icon-button")); + iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + auto minButton = new QWK::WindowButton(); + minButton->setObjectName(QStringLiteral("min-button")); + minButton->setProperty("system-button", true); + minButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + auto maxButton = new QWK::WindowButton(); + maxButton->setCheckable(true); + maxButton->setObjectName(QStringLiteral("max-button")); + maxButton->setProperty("system-button", true); + maxButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + + auto closeButton = new QWK::WindowButton(); + closeButton->setObjectName(QStringLiteral("close-button")); + closeButton->setProperty("system-button", true); + closeButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); +#endif + + auto windowBar = new QWK::WindowBar(); +#ifndef Q_OS_MAC + windowBar->setIconButton(iconButton); + windowBar->setMinButton(minButton); + windowBar->setMaxButton(maxButton); + windowBar->setCloseButton(closeButton); +#endif + windowBar->setMenuBar(menuBar); + windowBar->setTitleLabel(titleLabel); + windowBar->setHostWidget(this); + + windowAgent->setTitleBar(windowBar); +#ifndef Q_OS_MAC + windowAgent->setSystemButton(QWK::WindowAgentBase::WindowIcon, iconButton); + windowAgent->setSystemButton(QWK::WindowAgentBase::Minimize, minButton); + windowAgent->setSystemButton(QWK::WindowAgentBase::Maximize, maxButton); + windowAgent->setSystemButton(QWK::WindowAgentBase::Close, closeButton); +#endif + windowAgent->setHitTestVisible(menuBar, true); + +#ifdef Q_OS_MAC + windowAgent->setSystemButtonAreaCallback([](const QSize &size) { + static constexpr const int width = 75; + return QRect(QPoint(size.width() - width, 0), QSize(width, size.height())); // + }); +#endif + + setMenuWidget(windowBar); + + // 3. Adds simulated mouse events to the title bar buttons +#ifdef Q_OS_WINDOWS + // Emulate Window system menu button behaviors + connect(iconButton, &QAbstractButton::clicked, windowAgent, [this, iconButton] { + iconButton->setProperty("double-click-close", false); + + // Pick a suitable time threshold + QTimer::singleShot(75, windowAgent, [this, iconButton]() { + if (iconButton->property("double-click-close").toBool()) + return; + windowAgent->showSystemMenu(iconButton->mapToGlobal(QPoint{0, iconButton->height()})); + }); + }); + connect(iconButton, &QWK::WindowButton::doubleClicked, this, [iconButton, this]() { + iconButton->setProperty("double-click-close", true); + close(); + }); +#endif + +#ifndef Q_OS_MAC + connect(windowBar, &QWK::WindowBar::minimizeRequested, this, &QWidget::showMinimized); + connect(windowBar, &QWK::WindowBar::maximizeRequested, this, [this, maxButton](bool max) { + if (max) { + showMaximized(); + } else { + showNormal(); + } + + // It's a Qt issue that if a QAbstractButton::clicked triggers a window's maximization, + // the button remains to be hovered until the mouse move. As a result, we need to + // manually send leave events to the button. + emulateLeaveEvent(maxButton); + }); + connect(windowBar, &QWK::WindowBar::closeRequested, this, &QWidget::close); +#endif +} + +void MainWindow::loadStyleSheet(Theme theme) { + if (!styleSheet().isEmpty() && theme == currentTheme) + return; + currentTheme = theme; + + if (QFile qss(theme == Dark ? QStringLiteral(":/dark-style.qss") + : QStringLiteral(":/light-style.qss")); + qss.open(QIODevice::ReadOnly | QIODevice::Text)) { + setStyleSheet(QString::fromUtf8(qss.readAll())); + Q_EMIT themeChanged(); + } +} diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/mainwindow.h b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/mainwindow.h new file mode 100644 index 0000000000000000000000000000000000000000..53514a1b4f73b88db1c7f0cc41eddb5a2d313641 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/mainwindow.h @@ -0,0 +1,42 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace QWK { + class WidgetWindowAgent; + class StyleAgent; +} + +class MainWindow : public QMainWindow { + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow() override; + + enum Theme { + Dark, + Light, + }; + Q_ENUM(Theme) + +Q_SIGNALS: + void themeChanged(); + +protected: + bool event(QEvent *event) override; + +private: + void installWindowAgent(); + void loadStyleSheet(Theme theme); + + Theme currentTheme{}; + + QWK::WidgetWindowAgent *windowAgent; +}; + +#endif // MAINWINDOW_H diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/mainwindow.qrc b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/mainwindow.qrc new file mode 100644 index 0000000000000000000000000000000000000000..af208ee602e846f1b89a4c38da36dc669c6410f5 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/mainwindow/mainwindow.qrc @@ -0,0 +1,6 @@ + + + dark-style.qss + light-style.qss + + \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/CMakeLists.txt b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9b834fa821c4810cafb90093584ff4bd47519123 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/CMakeLists.txt @@ -0,0 +1,14 @@ +project(QWKExample_QML) + +file(GLOB _src *.h *.cpp *.qrc) + +qwk_add_example(${PROJECT_NAME} + SOURCES ${_src} ../shared/resources/shared.qrc + QT_LINKS Core Gui Qml Quick + LINKS QWKQuick +) + +set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED TRUE +) diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/QWKButton.qml b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/QWKButton.qml new file mode 100644 index 0000000000000000000000000000000000000000..91569f7bb714ff6acca7b82ce219dc0ee0b26322 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/QWKButton.qml @@ -0,0 +1,39 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Button { + id: root + width: height * 1.5 + leftPadding: 0 + topPadding: 0 + rightPadding: 0 + bottomPadding: 0 + leftInset: 0 + topInset: 0 + rightInset: 0 + bottomInset: 0 + property alias source: image.source + contentItem: Item { + Image { + id: image + anchors.centerIn: parent + mipmap: true + width: 12 + height: 12 + } + } + background: Rectangle { + color: { + if (!root.enabled) { + return "gray"; + } + if (root.pressed) { + return Qt.rgba(0, 0, 0, 0.15); + } + if (root.hovered) { + return Qt.rgba(0, 0, 0, 0.15); + } + return "transparent"; + } + } +} \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/main.cpp b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3daf57f645a4740dcc8ecd327353f746b085d431 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/main.cpp @@ -0,0 +1,33 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include + +#include + +int main(int argc, char *argv[]) { + qputenv("QT_WIN_DEBUG_CONSOLE", "attach"); + qputenv("QSG_INFO", "1"); +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + qputenv("QT_QUICK_CONTROLS_STYLE", "Basic"); +#else + qputenv("QT_QUICK_CONTROLS_STYLE", "Default"); +#endif +#if 0 + qputenv("QSG_RHI_BACKEND", "opengl"); + //qputenv("QSG_RHI_HDR", "scrgb"); + //qputenv("QT_QPA_DISABLE_REDIRECTION_SURFACE", "1"); + QGuiApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); +#endif + QGuiApplication application(argc, argv); + // Make sure alpha channel is requested, our special effects on Windows depends on it. + QQuickWindow::setDefaultAlphaBuffer(true); + QQmlApplicationEngine engine; + QWK::registerTypes(&engine); + engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); + return application.exec(); +} \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/main.qml b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/main.qml new file mode 100644 index 0000000000000000000000000000000000000000..8c59b9eb123635af6485c7ad7826647cd395da39 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/main.qml @@ -0,0 +1,220 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import Qt.labs.platform 1.1 +import QWindowKit 1.0 + +Window { + id: window + width: 800 + height: 600 + color: darkStyle.windowBackgroundColor + title: qsTr("Hello, world!") + Component.onCompleted: { + windowAgent.setup(window) + window.visible = true + } + + QtObject { + id: lightStyle + } + + QtObject { + id: darkStyle + readonly property color windowBackgroundColor: "#1E1E1E" + } + + Timer { + interval: 100 + running: true + repeat: true + onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss") + } + + WindowAgent { + id: windowAgent + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: contextMenu.open() + } + + Rectangle { + id: titleBar + anchors { + top: parent.top + topMargin: 1 + left: parent.left + right: parent.right + } + height: 32 + color: window.active ? "#3C3C3C" : "#505050" + Component.onCompleted: windowAgent.setTitleBar(titleBar) + + Image { + id: iconButton + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: 10 + } + width: 18 + height: 18 + mipmap: true + source: "qrc:///app/example.png" + } + + Text { + anchors { + verticalCenter: parent.verticalCenter + left: iconButton.right + leftMargin: 10 + } + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: window.title + font.pixelSize: 14 + color: "#ECECEC" + } + + Row { + anchors { + top: parent.top + right: parent.right + } + height: parent.height + + QWKButton { + id: minButton + height: parent.height + source: "qrc:///window-bar/minimize.svg" + onClicked: window.showMinimized() + Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Minimize, minButton) + } + + QWKButton { + id: maxButton + height: parent.height + source: window.visibility === Window.Maximized ? "qrc:///window-bar/restore.svg" : "qrc:///window-bar/maximize.svg" + onClicked: { + if (window.visibility === Window.Maximized) { + window.showNormal() + } else { + window.showMaximized() + } + } + Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Maximize, maxButton) + } + + QWKButton { + id: closeButton + height: parent.height + source: "qrc:///window-bar/close.svg" + background: Rectangle { + color: { + if (!closeButton.enabled) { + return "gray"; + } + if (closeButton.pressed) { + return "#e81123"; + } + if (closeButton.hovered) { + return "#e81123"; + } + return "transparent"; + } + } + onClicked: window.close() + Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Close, closeButton) + } + } + } + + Label { + id: timeLabel + anchors.centerIn: parent + font { + pointSize: 75 + bold: true + } + color: "#FEFEFE" + } + + Menu { + id: contextMenu + + Menu { + id: themeMenu + title: qsTr("Theme") + + MenuItemGroup { + id: themeMenuGroup + items: themeMenu.items + } + + MenuItem { + text: qsTr("Light") + checkable: true + onTriggered: windowAgent.setWindowAttribute("dark-mode", false) + } + + MenuItem { + text: qsTr("Dark") + checkable: true + onTriggered: windowAgent.setWindowAttribute("dark-mode", true) + } + } + + Menu { + id: specialEffectMenu + title: qsTr("Special effect") + + MenuItemGroup { + id: specialEffectMenuGroup + items: specialEffectMenu.items + } + + MenuItem { + enabled: Qt.platform.os === "windows" + text: qsTr("DWM blur") + checkable: true + onTriggered: { + window.color = checked ? "transparent" : darkStyle.windowBackgroundColor + windowAgent.setWindowAttribute("dwm-blur", checked) + } + } + + MenuItem { + enabled: Qt.platform.os === "windows" + text: qsTr("Acrylic material") + checkable: true + onTriggered: { + window.color = checked ? "transparent" : darkStyle.windowBackgroundColor + windowAgent.setWindowAttribute("acrylic-material", checked) + } + } + + MenuItem { + enabled: Qt.platform.os === "windows" + text: qsTr("Mica") + checkable: true + onTriggered: { + window.color = checked ? "transparent" : darkStyle.windowBackgroundColor + windowAgent.setWindowAttribute("mica", checked) + } + } + + MenuItem { + enabled: Qt.platform.os === "windows" + text: qsTr("Mica Alt") + checkable: true + onTriggered: { + window.color = checked ? "transparent" : darkStyle.windowBackgroundColor + windowAgent.setWindowAttribute("mica-alt", checked) + } + } + } + } +} \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/qml.qrc b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/qml.qrc new file mode 100644 index 0000000000000000000000000000000000000000..865977e4ba795d7d705ff22b5423f41f0a411aa5 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/qml/qml.qrc @@ -0,0 +1,6 @@ + + + main.qml + QWKButton.qml + + \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/CMakeLists.txt b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..187da26fd3fc32d0851fd3a64afcc2471b4f5cde --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(widgetframe) \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/app/example.icns b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/app/example.icns new file mode 100644 index 0000000000000000000000000000000000000000..bc4bb61e96bc1632c81f0b77bef24bbe42523599 Binary files /dev/null and b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/app/example.icns differ diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/app/example.ico b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/app/example.ico new file mode 100644 index 0000000000000000000000000000000000000000..316795df5babbc57db422a14414b66b91587416b Binary files /dev/null and b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/app/example.ico differ diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/app/example.png b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/app/example.png new file mode 100644 index 0000000000000000000000000000000000000000..61bf23c8b9f735b9179169babb32bd19eb4c4dfe Binary files /dev/null and b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/app/example.png differ diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/shared.qrc b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/shared.qrc new file mode 100644 index 0000000000000000000000000000000000000000..5aec715416bd699effa80aa3734850b6a1221f90 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/shared.qrc @@ -0,0 +1,11 @@ + + + window-bar/close.svg + window-bar/fullscreen.svg + window-bar/maximize.svg + window-bar/minimize.svg + window-bar/restore.svg + window-bar/more-line.svg + app/example.png + + diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/close.svg b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/close.svg new file mode 100644 index 0000000000000000000000000000000000000000..103d04ef4b3d621ee7b9ac6b8cc7028bd52a4298 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/close.svg @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/fullscreen.svg b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/fullscreen.svg new file mode 100644 index 0000000000000000000000000000000000000000..fff0898c53a3b70c10f4ef59d88cfe0b8816e338 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/fullscreen.svg @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/maximize.svg b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/maximize.svg new file mode 100644 index 0000000000000000000000000000000000000000..a50c90958a6d74083449c0253b7c9ae98ef45916 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/maximize.svg @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/minimize.svg b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/minimize.svg new file mode 100644 index 0000000000000000000000000000000000000000..e4e4bfdd99cb4a7b417719ce47ef4c67e15daeae --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/minimize.svg @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/more-line.svg b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/more-line.svg new file mode 100644 index 0000000000000000000000000000000000000000..246247fe815f9a7746fd5d5d994e7d176ee4e198 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/more-line.svg @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/restore.svg b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/restore.svg new file mode 100644 index 0000000000000000000000000000000000000000..bb6e245cbed5b9a904b96a3ec8a110025b98ce6c --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/resources/window-bar/restore.svg @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/CMakeLists.txt b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..05ac9491d1d24044624737ea2a94ee1299028baa --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/CMakeLists.txt @@ -0,0 +1,21 @@ +project(WidgetFrame) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +file(GLOB _src *.h *.cpp) + +add_library(${PROJECT_NAME} STATIC) + +qm_configure_target(${PROJECT_NAME} + SOURCES ${_src} + QT_LINKS Core Gui Widgets +) + +set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED TRUE +) + +target_include_directories(${PROJECT_NAME} PUBLIC . ..) \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbar.cpp b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbar.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8753bc27615a77dac14b434adb18fc04c7760398 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbar.cpp @@ -0,0 +1,298 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#include "windowbar.h" +#include "windowbar_p.h" + +#include +#include +#include + +namespace QWK { + + WindowBarPrivate::WindowBarPrivate() { + w = nullptr; + autoTitle = true; + autoIcon = false; + } + + WindowBarPrivate::~WindowBarPrivate() = default; + + void WindowBarPrivate::init() { + Q_Q(WindowBar); + layout = new QHBoxLayout(); + if (QLocale::system().textDirection() == Qt::RightToLeft) { + layout->setDirection(QBoxLayout::RightToLeft); + } + + layout->setContentsMargins(QMargins()); + layout->setSpacing(0); + for (int i = IconButton; i <= CloseButton; ++i) { + insertDefaultSpace(i); + } + q->setLayout(layout); + } + + void WindowBarPrivate::setWidgetAt(int index, QWidget *widget) { + auto item = layout->takeAt(index); + auto orgWidget = item->widget(); + if (orgWidget) { + orgWidget->deleteLater(); + } + delete item; + if (!widget) { + insertDefaultSpace(index); + } else { + layout->insertWidget(index, widget); + } + } + + QWidget *WindowBarPrivate::takeWidgetAt(int index) { + auto item = layout->itemAt(index); + auto orgWidget = item->widget(); + if (orgWidget) { + item = layout->takeAt(index); + delete item; + insertDefaultSpace(index); + } + return orgWidget; + } + + WindowBar::WindowBar(QWidget *parent) : WindowBar(*new WindowBarPrivate(), parent) { + } + + WindowBar::~WindowBar() = default; + + QMenuBar *WindowBar::menuBar() const { + Q_D(const WindowBar); + return static_cast(d->widgetAt(WindowBarPrivate::MenuWidget)); + } + + QLabel *WindowBar::titleLabel() const { + Q_D(const WindowBar); + return static_cast(d->widgetAt(WindowBarPrivate::TitleLabel)); + } + + QAbstractButton *WindowBar::iconButton() const { + Q_D(const WindowBar); + return static_cast(d->widgetAt(WindowBarPrivate::IconButton)); + } + + QAbstractButton *WindowBar::minButton() const { + Q_D(const WindowBar); + return static_cast(d->widgetAt(WindowBarPrivate::MinimumButton)); + } + + QAbstractButton *WindowBar::maxButton() const { + Q_D(const WindowBar); + return static_cast(d->widgetAt(WindowBarPrivate::MaximumButton)); + } + + QAbstractButton *WindowBar::closeButton() const { + Q_D(const WindowBar); + return static_cast(d->widgetAt(WindowBarPrivate::CloseButton)); + } + + void WindowBar::setMenuBar(QMenuBar *menuBar) { + Q_D(WindowBar); + auto org = takeMenuBar(); + if (org) + org->deleteLater(); + if (!menuBar) + return; + d->setWidgetAt(WindowBarPrivate::MenuWidget, menuBar); + menuBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum); + } + + void WindowBar::setTitleLabel(QLabel *label) { + Q_D(WindowBar); + auto org = takeTitleLabel(); + if (org) + org->deleteLater(); + if (!label) + return; + d->setWidgetAt(WindowBarPrivate::TitleLabel, label); + if (d->autoTitle && d->w) + label->setText(d->w->windowTitle()); + label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + } + + void WindowBar::setIconButton(QAbstractButton *btn) { + Q_D(WindowBar); + auto org = takeIconButton(); + if (org) + org->deleteLater(); + if (!btn) + return; + d->setWidgetAt(WindowBarPrivate::IconButton, btn); + if (d->autoIcon && d->w) + btn->setIcon(d->w->windowIcon()); + btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + } + + void WindowBar::setMinButton(QAbstractButton *btn) { + Q_D(WindowBar); + auto org = takeMinButton(); + if (org) + org->deleteLater(); + if (!btn) + return; + d->setWidgetAt(WindowBarPrivate::MinimumButton, btn); + connect(btn, &QAbstractButton::clicked, this, &WindowBar::minimizeRequested); + } + + void WindowBar::setMaxButton(QAbstractButton *btn) { + Q_D(WindowBar); + auto org = takeMaxButton(); + if (org) + org->deleteLater(); + if (!btn) + return; + d->setWidgetAt(WindowBarPrivate::MaximumButton, btn); + connect(btn, &QAbstractButton::clicked, this, &WindowBar::maximizeRequested); + } + + void WindowBar::setCloseButton(QAbstractButton *btn) { + Q_D(WindowBar); + auto org = takeCloseButton(); + if (org) + org->deleteLater(); + if (!btn) + return; + d->setWidgetAt(WindowBarPrivate::CloseButton, btn); + connect(btn, &QAbstractButton::clicked, this, &WindowBar::closeRequested); + } + + QMenuBar *WindowBar::takeMenuBar() { + Q_D(WindowBar); + return static_cast(d->takeWidgetAt(WindowBarPrivate::MenuWidget)); + } + + QLabel *WindowBar::takeTitleLabel() { + Q_D(WindowBar); + return static_cast(d->takeWidgetAt(WindowBarPrivate::TitleLabel)); + } + + QAbstractButton *WindowBar::takeIconButton() { + Q_D(WindowBar); + return static_cast(d->takeWidgetAt(WindowBarPrivate::IconButton)); + } + + QAbstractButton *WindowBar::takeMinButton() { + Q_D(WindowBar); + auto btn = static_cast(d->takeWidgetAt(WindowBarPrivate::MinimumButton)); + if (!btn) { + return nullptr; + } + disconnect(btn, &QAbstractButton::clicked, this, &WindowBar::minimizeRequested); + return btn; + } + + QAbstractButton *WindowBar::takeMaxButton() { + Q_D(WindowBar); + auto btn = static_cast(d->takeWidgetAt(WindowBarPrivate::MaximumButton)); + if (!btn) { + return nullptr; + } + disconnect(btn, &QAbstractButton::clicked, this, &WindowBar::maximizeRequested); + return btn; + } + + QAbstractButton *WindowBar::takeCloseButton() { + Q_D(WindowBar); + auto btn = static_cast(d->takeWidgetAt(WindowBarPrivate::CloseButton)); + if (!btn) { + return nullptr; + } + disconnect(btn, &QAbstractButton::clicked, this, &WindowBar::closeRequested); + return btn; + } + + QWidget *WindowBar::hostWidget() const { + Q_D(const WindowBar); + return d->w; + } + + void WindowBar::setHostWidget(QWidget *w) { + Q_D(WindowBar); + + QWidget *org = d->w; + if (org) { + org->removeEventFilter(this); + } + d_ptr->w = w; + if (w) { + w->installEventFilter(this); + } + } + + bool WindowBar::titleFollowWindow() const { + Q_D(const WindowBar); + return d->autoTitle; + } + + void WindowBar::setTitleFollowWindow(bool value) { + Q_D(WindowBar); + d->autoTitle = value; + } + + bool WindowBar::iconFollowWindow() const { + Q_D(const WindowBar); + return d->autoIcon; + } + + void WindowBar::setIconFollowWindow(bool value) { + Q_D(WindowBar); + d->autoIcon = value; + } + + bool WindowBar::eventFilter(QObject *obj, QEvent *event) { + Q_D(WindowBar); + auto w = d->w; + if (obj == w) { + QAbstractButton *iconBtn = iconButton(); + QLabel *label = titleLabel(); + QAbstractButton *maxBtn = maxButton(); + switch (event->type()) { + case QEvent::WindowIconChange: { + if (d_ptr->autoIcon && iconBtn) { + iconBtn->setIcon(w->windowIcon()); + iconChanged(w->windowIcon()); + } + break; + } + case QEvent::WindowTitleChange: { + if (d_ptr->autoTitle && label) { + label->setText(w->windowTitle()); + titleChanged(w->windowTitle()); + } + break; + } + case QEvent::WindowStateChange: { + if (maxBtn) { + maxBtn->setChecked(w->isMaximized()); + } + break; + } + default: + break; + } + } + return QWidget::eventFilter(obj, event); + } + + void WindowBar::titleChanged(const QString &text) { + Q_UNUSED(text) + } + + void WindowBar::iconChanged(const QIcon &icon){Q_UNUSED(icon)} + + WindowBar::WindowBar(WindowBarPrivate &d, QWidget *parent) + : QFrame(parent), d_ptr(&d) { + d.q_ptr = this; + + d.init(); + } + +} diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbar.h b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbar.h new file mode 100644 index 0000000000000000000000000000000000000000..2e802f2b2a79fda2bdfb2e54aeb64cbd26ea6612 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbar.h @@ -0,0 +1,74 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWBAR_H +#define WINDOWBAR_H + +#include +#include +#include +#include + +namespace QWK { + + class WindowBarPrivate; + + class WindowBar : public QFrame { + Q_OBJECT + Q_DECLARE_PRIVATE(WindowBar) + public: + explicit WindowBar(QWidget *parent = nullptr); + ~WindowBar(); + + public: + QMenuBar *menuBar() const; + QLabel *titleLabel() const; + QAbstractButton *iconButton() const; + QAbstractButton *minButton() const; + QAbstractButton *maxButton() const; + QAbstractButton *closeButton() const; + + void setMenuBar(QMenuBar *menuBar); + void setTitleLabel(QLabel *label); + void setIconButton(QAbstractButton *btn); + void setMinButton(QAbstractButton *btn); + void setMaxButton(QAbstractButton *btn); + void setCloseButton(QAbstractButton *btn); + + QMenuBar *takeMenuBar(); + QLabel *takeTitleLabel(); + QAbstractButton *takeIconButton(); + QAbstractButton *takeMinButton(); + QAbstractButton *takeMaxButton(); + QAbstractButton *takeCloseButton(); + + QWidget *hostWidget() const; + void setHostWidget(QWidget *w); + + bool titleFollowWindow() const; + void setTitleFollowWindow(bool value); + + bool iconFollowWindow() const; + void setIconFollowWindow(bool value); + + Q_SIGNALS: + void minimizeRequested(); + void maximizeRequested(bool max = false); + void closeRequested(); + + protected: + bool eventFilter(QObject *obj, QEvent *event) override; + + virtual void titleChanged(const QString &text); + virtual void iconChanged(const QIcon &icon); + + protected: + WindowBar(WindowBarPrivate &d, QWidget *parent = nullptr); + + QScopedPointer d_ptr; + }; + +} + +#endif // WINDOWBAR_H \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbar_p.h b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbar_p.h new file mode 100644 index 0000000000000000000000000000000000000000..36ceb17cba52e1c241bdf61afd12c526ed2ebd9e --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbar_p.h @@ -0,0 +1,57 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWBARPRIVATE_H +#define WINDOWBARPRIVATE_H + +#include + +#include "windowbar.h" + +namespace QWK { + + class WindowBarPrivate { + Q_DECLARE_PUBLIC(WindowBar) + public: + WindowBarPrivate(); + virtual ~WindowBarPrivate(); + + void init(); + + WindowBar *q_ptr; + + QWidget *w; + bool autoTitle; + bool autoIcon; + + enum WindowBarItem { + IconButton, + MenuWidget, + TitleLabel, + MinimumButton, + MaximumButton, + CloseButton, + }; + + QHBoxLayout *layout; + + inline QWidget *widgetAt(int index) const { + return layout->itemAt(index)->widget(); + } + + void setWidgetAt(int index, QWidget *widget); + + QWidget *takeWidgetAt(int index); + + inline void insertDefaultSpace(int index) { + layout->insertSpacerItem(index, new QSpacerItem(0, 0)); + } + + private: + Q_DISABLE_COPY(WindowBarPrivate) + }; + +} + +#endif // WINDOWBARPRIVATE_H \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbutton.cpp b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbutton.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f0b2c70ffb42c61dd87b5fb447025c77f5aa83be --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbutton.cpp @@ -0,0 +1,94 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#include "windowbutton.h" +#include "windowbutton_p.h" + +#include +#include + +namespace QWK { + + WindowButtonPrivate::WindowButtonPrivate() = default; + + WindowButtonPrivate::~WindowButtonPrivate() = default; + + void WindowButtonPrivate::init() { + } + + void WindowButtonPrivate::reloadIcon() { + Q_Q(WindowButton); + + if (!q->isEnabled() && !iconDisabled.isNull()) { + q->setIcon(iconDisabled); + return; + } + + if (q->isChecked() && !iconChecked.isNull()) { + q->setIcon(iconChecked); + return; + } + + if (!iconNormal.isNull()) { + q->setIcon(iconNormal); + } + } + + WindowButton::WindowButton(QWidget *parent) : WindowButton(*new WindowButtonPrivate(), parent) { + } + + WindowButton::~WindowButton() = default; + + QIcon WindowButton::iconNormal() const { + Q_D(const WindowButton); + return d->iconNormal; + } + + void WindowButton::setIconNormal(const QIcon &icon) { + Q_D(WindowButton); + d->iconNormal = icon; + d->reloadIcon(); + } + + QIcon WindowButton::iconChecked() const { + Q_D(const WindowButton); + return d->iconChecked; + } + + void WindowButton::setIconChecked(const QIcon &icon) { + Q_D(WindowButton); + d->iconChecked = icon; + d->reloadIcon(); + } + + QIcon WindowButton::iconDisabled() const { + Q_D(const WindowButton); + return d->iconDisabled; + } + + void WindowButton::setIconDisabled(const QIcon &icon) { + Q_D(WindowButton); + d->iconDisabled = icon; + d->reloadIcon(); + } + + void WindowButton::checkStateSet() { + Q_D(WindowButton); + d->reloadIcon(); + } + + void WindowButton::mouseDoubleClickEvent(QMouseEvent *event) { + if (event->button() == Qt::LeftButton) { + Q_EMIT doubleClicked(); + } + } + + WindowButton::WindowButton(WindowButtonPrivate &d, QWidget *parent) + : QPushButton(parent), d_ptr(&d) { + d.q_ptr = this; + + d.init(); + } + +} diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbutton.h b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbutton.h new file mode 100644 index 0000000000000000000000000000000000000000..402b50fd335392180cd7509dd8710debc71513aa --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbutton.h @@ -0,0 +1,50 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWBUTTON_H +#define WINDOWBUTTON_H + +#include + +namespace QWK { + + class WindowButtonPrivate; + + class WindowButton : public QPushButton { + Q_OBJECT + Q_DECLARE_PRIVATE(WindowButton) + Q_PROPERTY(QIcon iconNormal READ iconNormal WRITE setIconNormal FINAL) + Q_PROPERTY(QIcon iconChecked READ iconChecked WRITE setIconChecked FINAL) + Q_PROPERTY(QIcon iconDisabled READ iconDisabled WRITE setIconDisabled FINAL) + public: + explicit WindowButton(QWidget *parent = nullptr); + ~WindowButton(); + + public: + QIcon iconNormal() const; + void setIconNormal(const QIcon &icon); + + QIcon iconChecked() const; + void setIconChecked(const QIcon &icon); + + QIcon iconDisabled() const; + void setIconDisabled(const QIcon &icon); + + Q_SIGNALS: + void doubleClicked(); + + protected: + void checkStateSet() override; + + void mouseDoubleClickEvent(QMouseEvent *event) override; + + protected: + WindowButton(WindowButtonPrivate &d, QWidget *parent = nullptr); + + QScopedPointer d_ptr; + }; + +} + +#endif // WINDOWBUTTON_H \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbutton_p.h b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbutton_p.h new file mode 100644 index 0000000000000000000000000000000000000000..34aaaaf296bf71faddfbd6fb631f4783c811b609 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/examples/shared/widgetframe/windowbutton_p.h @@ -0,0 +1,31 @@ +// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) +// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) +// SPDX-License-Identifier: Apache-2.0 + +#ifndef WINDOWBUTTONPRIVATE_H +#define WINDOWBUTTONPRIVATE_H + +#include "windowbutton.h" + +namespace QWK { + + class WindowButtonPrivate { + Q_DECLARE_PUBLIC(WindowButton) + public: + WindowButtonPrivate(); + virtual ~WindowButtonPrivate(); + + void init(); + + WindowButton *q_ptr; + + QIcon iconNormal; + QIcon iconChecked; + QIcon iconDisabled; + + void reloadIcon(); + }; + +} + +#endif // WINDOWBUTTONPRIVATE_H \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/.clang-format b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..e2f17207031d3791e91f1791e4f5c280bcf8f200 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/.clang-format @@ -0,0 +1,130 @@ +# References: +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# https://code.qt.io/cgit/qt/qt5.git/tree/_clang-format + +BasedOnStyle: LLVM + +Standard: c++17 + +# 指针和引用的对齐方式。 +# 可能的值有: +# PAS_Left (在配置中: Left) 指针左对齐。 +# PAS_Right (在配置中: Right) 指针右对齐。 +# PAS_Middle (在配置中: Middle) 指针中间对齐。 +PointerAlignment: Right + +# public/protected/private 等访问修饰符偏移量 +AccessModifierOffset: -4 + +# 缩进长度 +IndentWidth: 4 + +# 连续空行的最大数 +MaxEmptyLinesToKeep: 999 + +# 在OC中的@property后面添加一个空格。例如:使用“@property (readonly)”而不是“@property(readonly)” +ObjCSpaceAfterProperty: true + +# OC块中所拍的字符数 +ObjCBlockIndentWidth: 4 + +# 取决于值, 语句“int f() { return 0; }”可以被放到一个单行。 +# 可能的值有: +# SFS_None (在配置中: None) 从不合并方法或函数到单独的一行。 +# SFS_Empty (在配置中: Empty) 仅合并空的函数。 +# SFS_Inline (在配置中: Inline) 仅合并类中定义的方法或函数. 意味着 “empty”. +# SFS_All (在配置中: All) 合并所有的方法适应单行. +AllowShortFunctionsOnASingleLine: None + +# 如果为真(true), 语句“if (a) return;” 能被放到单行。 +AllowShortIfStatementsOnASingleLine: false + +# 如果为真(true), 对齐注释。 +AlignTrailingComments: true + +# 如果为真,对齐连续的宏定义 +AlignConsecutiveMacros: true + +# 如果为真(true),将会在“[”之后和“]”之前插入空格。 +SpacesInSquareBrackets: false + +# 如果为真(true), 将会在“(”之后和“)”之前插入空格。 +SpacesInParentheses : false + +# 如果为真(true), 校准连续的声明。 +# 这将会校准连续多行的声明的名字。这将会导致像下面这样的格式: +# int aaaa = 12; +# float b = 23; +# std::string ccc = 23; +AlignConsecutiveDeclarations: false + +# 如果为真(true),连续调整多行 +# 这将会调整连续行中的分配操作符。这将会导致像下面这样的格式: +# int aaaa = 12; +# int b = 23; +# int ccc = 23; +AlignConsecutiveAssignments: false + +# 如果为假(false),移除分配操作符(=)前空格。 +SpaceBeforeAssignmentOperators: true + +# 如果为真(true), 将会在字面量容器中插入空格(例如 OC和Javascript的数组和字典字面量)。 +SpacesInContainerLiterals: false + +# 缩进case标签 +IndentCaseLabels: true + +# 如果表达式中包含函数调用,并且函数调用因为表达式太长被放到了下一行,是否缩进 +IndentWrappedFunctionNames: true + +# 如果为真(true), 保持块的起始空行。 +# true: false: +# if (foo) { vs. if (foo) { +# bar(); +# bar(); } +# } +KeepEmptyLinesAtTheStartOfBlocks: true + +# 允许所有参数都被放在下一行 +AllowAllParametersOfDeclarationOnNextLine: false + +# 使用C风格强制类型转换后,是否在中间添加一个空格 +SpaceAfterCStyleCast: true + +# 在模板定义后换行 +AlwaysBreakTemplateDeclarations: Yes + +# Tab长度 +TabWidth: 4 + +# 是否使用Tab +UseTab: Never + +# 在括号后对齐参数 +# someLongFunction(argument1, +# argument2); +AlignAfterOpenBracket: Align + +# 名字空间内部缩进 +NamespaceIndentation: All + +# 一行最长列数 +ColumnLimit: 100 + +# 按层次缩进宏定义 +IndentPPDirectives: AfterHash + +# 预处理语句缩进为 2 +PPIndentWidth: 2 + +# 数组元素对齐 +AlignArrayOfStructures: Left + +# 不对头文件排序 +SortIncludes: Never + +FixNamespaceComments: false + +StatementMacros: ['__qas_attr__', '__qas_exclude__', '__qas_include__'] + +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ] \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/.gitignore b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..04490f0e36d233401bd68041327647e290b7156e --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/.gitignore @@ -0,0 +1,98 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.log +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +# *.res +# *.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +__pycache__ +*.pyc + +# Binaries +# -------- +*.dll +*.exe + +.DS_Store +*/.DS_Store + +build-src* +build/ +cmake-build* +*.user +*.lnk +_workingDir* +.vscode +.idea +.cache +cache +.vs +out/ +CMakeSettings.json +# /vcpkg +/data +/*.natvis + +*.sublime-* +setup-vcpkg.json +setup-vcpkg-temp* \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/.gitmodules b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..e44cf9366b8e9d92f3b1fdf12c006c96793db1ff --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/.gitmodules @@ -0,0 +1,4 @@ +[submodule "syscmdline"] + path = src/syscmdline + url = ../../SineStriker/syscmdline.git + branch = main \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/CMakeLists.txt b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0387b18c6b1427c08b454d845fe8acaef5d125a2 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.19) + +project(qmsetup VERSION 0.0.1.5) + +# ---------------------------------- +# Configure Options +# ---------------------------------- +option(QMSETUP_VCPKG_TOOLS_HINT "Install executables to tools directory" OFF) +option(QMSETUP_STATIC_RUNTIME "Static link runtime libraries on Windows" ON) + +# ---------------------------------- +# CMake Settings +# ---------------------------------- +if(NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +endif() + +if(NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +endif() + +if(NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +endif() + +if(MSVC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /manifest:no") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") + + if(NOT DEFINED CMAKE_DEBUG_POSTFIX) + set(CMAKE_DEBUG_POSTFIX "d") + endif() +endif() + +# ---------------------------------- +# Project Variables +# ---------------------------------- +set(QMSETUP_VERSION ${PROJECT_VERSION}) +set(QMSETUP_INSTALL_NAME ${PROJECT_NAME}) +set(QMSETUP_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +include(cmake/QMSetupAPI.cmake) + +add_subdirectory(src) \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/LICENSE b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..e4412f67cd7ecb00beaaad61b190c1ab50ab0c01 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Stdware Collections + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/README.md b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0777d9a4ca720735be7ef34fa1b7fa02bb181427 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/README.md @@ -0,0 +1,202 @@ +# QMSetup + +**QMSetup** is a set of CMake Modules and Basic Libraries for C/C++ projects. + +**This project is independent from Qt and other 3rdparty libraries.** Due to the fact that it encompasses some tools that need to be compiled, it's not suggested to be included as a subproject. + +## Features + ++ Helpful CMake utilities ++ Generate configuration header files ++ Re-organize header files ++ Deploy project dependencies and fix rpaths ++ Support calling **Doxygen** via CMake conveniently ++ Support calling **Qt Linguist Tools** via CMake conveniently + +## Support Platforms + ++ Microsoft Windows ++ Apple Macintosh ++ GNU/Linux + +## Dependencies + +### Required Packages + +Windows deploy command acquires the shared library paths by reading the PE files and searching the specified paths so that it doesn't depend on `dumpbin` tool. + +Unix deploy command acquires the shared library paths by running `ldd`/`otool` command and fixes the *rpath*s by runing the `patchelf`/`install_name_tool` command, make sure you have installed them. + +```sh +sudo apt install patchelf +``` + +### Build System + ++ C++ 17 ++ CMake 3.19 + +### Open-Source Libraries ++ https://github.com/SineStriker/syscmdline ++ https://github.com/jothepro/doxygen-awesome-css + +## Integrate + +### Clone + +Via Https +```sh +git clone --recursive https://github.com/stdware/qmsetup.git +``` +Via SSH +```sh +git clone --recursive git@github.com:stdware/qmsetup.git +``` + +### Preinstall (Suggested) + +#### Build & Install +```sh +cmake -B build -DCMAKE_INSTALL_PREFIX=/path/to +cmake -B build --target all +cmake -B build --target install +``` + +#### Import +```sh +cmake -Dqmsetup_DIR=/path/to/lib/cmake/qmsetup ... +``` +```cmake +find_package(qmsetup REQUIRED) +``` + +### Sub-project + +It still needs to be installed, but the installation occurs during the CMake Configure phase and is executed only once. + +```cmake +find_package(qmsetup QUIET) + +if (NOT TARGET qmsetup::library) + # Modify this variable according to your project structure + set(_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/qmsetup) + + # Import install function + include("${_source_dir}/cmake/modules/InstallPackage.cmake") + + # Install package in place + set(_package_path) + qm_install_package(qmsetup + SOURCE_DIR ${_source_dir} + BUILD_TYPE Release + RESULT_PATH _package_path + ) + + # Find package again + find_package(qmsetup REQUIRED PATHS ${_package_path}) + + # Update import path + set(qmsetup_DIR ${_package_path} CACHE PATH "" FORCE) +endif() +``` + +## Quick Start + +### Examples + +Here are some common use cases of CMake project, you can simplify many operations when using this library. + +#### Generate Configuration Header +```cmake +qm_import(Preprocess) + +qm_add_definition(FOO false) +qm_add_definition(BAR 114514) +qm_add_definition(BAZ "ABC" STRING_LITERAL) + +qm_generate_config(${CMAKE_BINARY_DIR}/conf.h) +``` + +#### Sync Resource Files After Build +```cmake +qm_import(Filesystem) + +qm_add_copy_command(${PROJECT_NAME} + SOURCES + file.txt + dir_to_copy + dir_contents_to_copy/ + DESTINATION . +) +``` + +#### Deploy Project And All Dependencies +```cmake +qm_import(Deploy) + +qm_deploy_directory("${CMAKE_INSTALL_PREFIX}" + COMMENT "Deploy project spectacularly" + PLUGINS "iconengines/qsvgicon" "bearer/qgenericbearer" + QML Qt QtQml + PLUGIN_DIR share/plugins + QML_DIR share/qml +) +``` + +#### Add Qt Translations +```cmake +qm_import(Translate) + +qm_find_qt(LinguistTools) +qm_add_translation(${PROJECT_NAME}_translations + LOCALES ja_JP zh_CN zh_TW + PREFIX ${PROJECT_NAME} + TARGETS ${PROJECT_NAME} + TS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/translations + QM_DIR ${CMAKE_CURRENT_BINARY_DIR}/translations +) +``` + +#### Generate Doxygen Document +```cmake +qm_import(Doxygen) + +find_package(Doxygen REQUIRED) +qm_setup_doxygen(${PROJECT_NAME}_RunDoxygen + NAME ${PROJECT_NAME} + DESCRIPTION "my project" + MDFILE "${CMAKE_SOURCE_DIR}/README.md" + OUTPUT_DIR "${CMAK_BINARY_DIR}/doc" + INPUT src + TARGETS ${PROJECT_NAME} + DEPENDS ${PROJECT_NAME} + NO_EXPAND_MACROS + Q_OBJECT + Q_GADGET + Q_DECLARE_TR_FUNCTIONS + COMPILE_DEFINITIONS + Q_SIGNALS=Q_SIGNALS + Q_SLOTS=Q_SLOTS + GENERATE_TAGFILE "${PROJECT_NAME}_tagfile.xml" + INSTALL_DIR "doc" +) +``` + +### Detailed Documents + ++ [Core Command](docs/core-command.md) + +The CMake Modules documentations is provided in the form of comment. + +See `examples` to get detailed use cases. + +## Contributors + ++ [SineStriker](https://github.com/SineStriker) ++ [wangwenx190](https://github.com/wangwenx190) ++ [RigoLigoRLC](https://github.com/RigoLigoRLC) ++ [CrSjimo](https://github.com/CrSjimo) + +## License + +QMSetup is licensed under the MIT License. \ No newline at end of file diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/cmake/QMSetupAPI.cmake b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/cmake/QMSetupAPI.cmake new file mode 100644 index 0000000000000000000000000000000000000000..aea855b49f80d575a2d73ad43640c8552e132891 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/cmake/QMSetupAPI.cmake @@ -0,0 +1,760 @@ +cmake_minimum_required(VERSION 3.19) + +#[[ + NOTICE + -------- + Since Qt official CMake modules sets private header directory variables when you call `find_package(Qt)` + only if the Qt targets hasn't been defined, if we place `find_package(Qt)` in a function, the variable + will be cleared while the target remains after the function returns, as a result, we can never get the + private header directory variables again. + + Therefore, never wrap `find_package(Qt)` in a function, use macro instead, any macros that wraps it also + shouldn't be wrapped in any function. +]]# + +set(QMSETUP_MODULES_DIR ${CMAKE_CURRENT_LIST_DIR}) + +if(WIN32) + set(QMSETUP_SHARED_LIBRARY_CATEGORY bin) + set(QMSETUP_NULL_FILE "NUL") + set(QMSETUP_REGEX_ABSOLUTE_PATH "^[a-zA-Z]:|\\\\") +else() + set(QMSETUP_SHARED_LIBRARY_CATEGORY lib) + set(QMSETUP_NULL_FILE "/dev/null") + set(QMSETUP_REGEX_ABSOLUTE_PATH "^/") +endif() + +set(QMSETUP_IGNORE_STDOUT > ${QMSETUP_NULL_FILE}) +set(QMSETUP_IGNORE_STDERR 2> ${QMSETUP_NULL_FILE}) +set(QMSETUP_IGNORE_STDOUT_STDERR > ${QMSETUP_NULL_FILE} 2>&1) + +if(TARGET qmsetup::corecmd) + get_target_property(QMSETUP_CORECMD_EXECUTABLE qmsetup::corecmd LOCATION) +else() + set(QMSETUP_CORECMD_EXECUTABLE) +endif() + +if(NOT DEFINED QMSETUP_FIND_QT_ORDER) + set(QMSETUP_FIND_QT_ORDER Qt6 Qt5) +endif() + +include_guard(DIRECTORY) + +#[[ + Include modules of this library. + + qm_import() +]] # +macro(qm_import) + foreach(_module ${ARGN}) + if(NOT _module MATCHES ".+\\.cmake") + set(_module "${_module}.cmake") + endif() + + set(_module_path "${QMSETUP_MODULES_DIR}/modules/${_module}") + + if(NOT EXISTS "${_module_path}") + message(FATAL_ERROR "qm_import: module \"${_module}\" not found.") + endif() + + include("${_module_path}") + endforeach() +endmacro() + +#[[ + Include all modules of this library. + + qm_import_all() +]] # +macro(qm_import_all) + file(GLOB _tmp_modules "${QMSETUP_MODULES_DIR}/modules/*") + + foreach(_module IN LISTS _tmp_modules) + include("${_module}") + endforeach() + + unset(_tmp_modules) +endmacro() + +#[[ + Skip CMAKE_AUTOMOC for sources files or ones in directories. + + qm_skip_automoc() +]] # +function(qm_skip_automoc) + foreach(_item ${ARGN}) + get_filename_component(_item ${_item} ABSOLUTE) + + if(IS_DIRECTORY ${_item}) + file(GLOB _src ${_item}/*.h ${_item}/*.hh ${_item}/*.hpp ${_item}/*.hxx ${_item}/*.c ${_item}/*.cc ${_item}/*.cpp ${_item}/*.cxx ${_item}/*.m ${_item}/*.mm) + set_source_files_properties( + ${_src} PROPERTIES SKIP_AUTOMOC ON + ) + elseif(EXISTS ${_item}) + set_source_files_properties( + ${_item} PROPERTIES SKIP_AUTOMOC ON + ) + endif() + endforeach() +endfunction() + +#[[ + Find Qt libraries. Don't wrap it in any functions. + + qm_find_qt() +#]] +macro(qm_find_qt) + foreach(_module ${ARGN}) + if(NOT QT_VERSION_MAJOR) + find_package(QT NAMES ${QMSETUP_FIND_QT_ORDER} COMPONENTS ${_module} REQUIRED) + endif() + + if(NOT TARGET Qt${QT_VERSION_MAJOR}::${_module}) + find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${_module} REQUIRED) + endif() + endforeach() +endmacro() + +#[[ + Link Qt libraries. Don't wrap it in any functions. + + qm_link_qt( ) +#]] +macro(qm_link_qt _target _scope) + foreach(_module ${ARGN}) + qm_find_qt(${_module}) + target_link_libraries(${_target} ${_scope} Qt${QT_VERSION_MAJOR}::${_module}) + endforeach() +endmacro() + +#[[ + Include Qt private header directories. Don't wrap it in any functions. + + qm_include_qt_private( ) +#]] +macro(qm_include_qt_private _target _scope) + foreach(_module ${ARGN}) + qm_find_qt(${_module}) + target_include_directories(${_target} ${_scope} ${Qt${QT_VERSION_MAJOR}${_module}_PRIVATE_INCLUDE_DIRS}) + endforeach() +endmacro() + +#[[ + Helper to link libraries and include directories of a target. Don't wrap it in any functions. + + qm_configure_target( + [SOURCES ] + [LINKS ] + [LINKS_PRIVATE ] + [INCLUDE_PRIVATE ] + + [DEFINES ] + [DEFINES_PRIVATE ] + + [CCFLAGS ] + [CCFLAGS_PRIVATE ] + + [QT_LINKS ] + [QT_LINKS_PRIVATE ] + [QT_INCLUDE_PRIVATE ] + + [SKIP_AUTOMOC ] + ) +]] # +macro(qm_configure_target _target) + set(options) + set(oneValueArgs) + set(multiValueArgs + SOURCES LINKS LINKS_PRIVATE + QT_LINKS QT_LINKS_PRIVATE QT_INCLUDE_PRIVATE + INCLUDE_PRIVATE + DEFINES DEFINES_PRIVATE + CCFLAGS CCFLAGS_PUBLIC + SKIP_AUTOMOC + ) + cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + target_sources(${_target} PRIVATE ${FUNC_SOURCES}) + target_link_libraries(${_target} PUBLIC ${FUNC_LINKS}) + target_link_libraries(${_target} PRIVATE ${FUNC_LINKS_PRIVATE}) + target_compile_definitions(${_target} PUBLIC ${FUNC_DEFINES}) + target_compile_definitions(${_target} PRIVATE ${FUNC_DEFINES_PRIVATE}) + target_compile_options(${_target} PUBLIC ${FUNC_CCFLAGS_PUBLIC}) + target_compile_options(${_target} PRIVATE ${FUNC_CCFLAGS}) + qm_link_qt(${_target} PUBLIC ${FUNC_QT_LINKS}) + qm_link_qt(${_target} PRIVATE ${FUNC_QT_LINKS_PRIVATE}) + target_include_directories(${_target} PRIVATE ${FUNC_INCLUDE_PRIVATE}) + qm_include_qt_private(${_target} PRIVATE ${FUNC_QT_INCLUDE_PRIVATE}) + qm_skip_automoc(${FUNC_SKIP_AUTOMOC}) +endmacro() + +#[[ + Helper to define export macros. + + qm_export_defines( + [PREFIX ] + [STATIC ] + [LIBRARY ] + ) +]] # +function(qm_export_defines _target) + set(options) + set(oneValueArgs PREFIX STATIC LIBRARY) + set(multiValueArgs) + cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT FUNC_PREFIX) + string(TOUPPER ${_target} _prefix) + else() + set(_prefix ${FUNC_PREFIX}) + endif() + + qm_set_value(_static_macro FUNC_STATIC ${_prefix}_STATIC) + qm_set_value(_library_macro FUNC_LIBRARY ${_prefix}_LIBRARY) + + get_target_property(_type ${_target} TYPE) + + if("${_type}" STREQUAL "STATIC_LIBRARY") + target_compile_definitions(${_target} PUBLIC ${_static_macro}) + endif() + + target_compile_definitions(${_target} PRIVATE ${_library_macro}) +endfunction() + +#[[ + Attach windows RC file to a target. + + qm_add_win_rc( + [NAME name] + [VERSION version] + [DESCRIPTION desc] + [COPYRIGHT copyright] + [ICON ico] + [OUTPUT output] + ) +]] # +function(qm_add_win_rc _target) + if(NOT WIN32) + return() + endif() + + _qm_check_target_type_helper(${_target} _ "EXECUTABLE" "SHARED_LIBRARY") + + set(options) + set(oneValueArgs NAME VERSION DESCRIPTION COPYRIGHT ICON OUTPUT) + set(multiValueArgs) + cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + qm_set_value(_name FUNC_NAME ${_target}) + qm_set_value(_version FUNC_VERSION PROJECT_VERSION "0.0.0.0") + qm_set_value(_desc FUNC_DESCRIPTION ${_name}) + qm_set_value(_copyright FUNC_COPYRIGHT ${_name}) + + qm_parse_version(_ver ${_version}) + set(RC_VERSION ${_ver_1},${_ver_2},${_ver_3},${_ver_4}) + + set(RC_APPLICATION_NAME ${_name}) + set(RC_VERSION_STRING ${_version}) + set(RC_DESCRIPTION ${_desc}) + set(RC_COPYRIGHT ${_copyright}) + + if(NOT FUNC_ICON) + set(RC_ICON_COMMENT "//") + set(RC_ICON_PATH) + else() + get_filename_component(RC_ICON_PATH ${FUNC_ICON} ABSOLUTE) + endif() + + qm_set_value(_out_path FUNC_OUTOUT "${CMAKE_CURRENT_BINARY_DIR}/${_name}_res.rc") + configure_file("${QMSETUP_MODULES_DIR}/windows/WinResource.rc.in" ${_out_path} @ONLY) + target_sources(${_target} PRIVATE ${_out_path}) +endfunction() + +#[[ + Attach windows RC file to a target, enhanced edition. + + qm_add_win_rc_enhanced( + [NAME name] + [VERSION version] + [DESCRIPTION description] + [COPYRIGHT copyright] + [COMMENTS comments] + [COMPANY company] + [INTERNAL_NAME internal name] + [TRADEMARK trademark] + [ORIGINAL_FILENAME original filename] + [ICONS icon file paths] + [OUTPUT output] + ) +]] # +function(qm_add_win_rc_enhanced _target) + if(NOT WIN32) + return() + endif() + + _qm_check_target_type_helper(${_target} _type "EXECUTABLE" "SHARED_LIBRARY") + + set(options) + set(oneValueArgs + NAME VERSION DESCRIPTION COPYRIGHT COMMENTS COMPANY + INTERNAL_NAME TRADEMARK ORIGINAL_FILENAME OUTPUT + ) + set(multiValueArgs ICONS) + cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + qm_set_value(_name FUNC_NAME ${_target}) + qm_set_value(_version FUNC_VERSION PROJECT_VERSION "0.0.0.0") + qm_set_value(_desc FUNC_DESCRIPTION ${_name}) + qm_set_value(_copyright FUNC_COPYRIGHT ${_name}) + qm_set_value(_comments FUNC_COMMENTS "") + qm_set_value(_company FUNC_COMPANY "") + qm_set_value(_internal_name FUNC_INTERNAL_NAME "") + qm_set_value(_trademark FUNC_TRADEMARK "") + qm_set_value(_original_filename FUNC_ORIGINAL_FILENAME "") + + qm_parse_version(_ver ${_version}) + set(RC_VERSION ${_ver_1},${_ver_2},${_ver_3},${_ver_4}) + + set(RC_APPLICATION_NAME ${_name}) + set(RC_VERSION_STRING ${_version}) + set(RC_DESCRIPTION ${_desc}) + set(RC_COPYRIGHT ${_copyright}) + set(RC_COMMENTS ${_comments}) + set(RC_COMPANY ${_company}) + set(RC_INTERNAL_NAME ${_internal_name}) + set(RC_TRADEMARK ${_trademark}) + set(RC_ORIGINAL_FILENAME ${_original_filename}) + + set(_file_type) + + if("${_type}" STREQUAL "EXECUTABLE") + set(_file_type "VFT_APP") + else() + set(_file_type "VFT_DLL") + endif() + + set(RC_FILE_TYPE ${_file_type}) + + set(_icons) + + if(FUNC_ICONS) + set(_index 1) + + foreach(_icon IN LISTS FUNC_ICONS) + get_filename_component(_icon_path ${_icon} ABSOLUTE) + string(APPEND _icons "IDI_ICON${_index} ICON \"${_icon_path}\"\n") + math(EXPR _index "${_index} +1") + endforeach() + endif() + + set(RC_ICONS ${_icons}) + + qm_set_value(_out_path FUNC_OUTOUT "${CMAKE_CURRENT_BINARY_DIR}/${_name}_res.rc") + configure_file("${QMSETUP_MODULES_DIR}/windows/WinResource2.rc.in" ${_out_path} @ONLY) + target_sources(${_target} PRIVATE ${_out_path}) +endfunction() + +#[[ + Attach windows manifest file to a target. + + qm_add_win_manifest( + [UTF8] + [NAME name] + [VERSION version] + [DESCRIPTION desc] + [OUTPUT output] + ) +]] # +function(qm_add_win_manifest _target) + if(NOT WIN32) + return() + endif() + + _qm_check_target_type_helper(${_target} _ "EXECUTABLE") + + set(options UTF8) + set(oneValueArgs NAME VERSION DESCRIPTION OUTPUT) + set(multiValueArgs) + cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + qm_set_value(_name FUNC_NAME ${_target}) + qm_set_value(_version FUNC_VERSION PROJECT_VERSION "0.0.0.0") + qm_set_value(_desc FUNC_DESCRIPTION ${_name}) + + qm_crop_version(_version ${_version} 4) + + set(MANIFEST_IDENTIFIER ${_name}) + set(MANIFEST_VERSION ${_version}) + set(MANIFEST_DESCRIPTION ${_desc}) + + set(MANIFEST_UTF8) + + if(FUNC_UTF8) + set(MANIFEST_UTF8 "UTF-8") + endif() + + qm_set_value(_out_path FUNC_OUTOUT "${CMAKE_CURRENT_BINARY_DIR}/${_name}_manifest.manifest") + configure_file("${QMSETUP_MODULES_DIR}/windows/WinManifest.manifest.in" ${_out_path} @ONLY) + target_sources(${_target} PRIVATE ${_out_path}) +endfunction() + +#[[ + Add Mac bundle info. + + qm_add_mac_bundle( + [NAME ] + [VERSION ] + [DESCRIPTION ] + [COPYRIGHT ] + [ICON ] + ) +]] # +function(qm_add_mac_bundle _target) + if(NOT APPLE) + return() + endif() + + _qm_check_target_type_helper(${_target} _ "EXECUTABLE") + + set(options) + set(oneValueArgs NAME VERSION DESCRIPTION COPYRIGHT ICON) + set(multiValueArgs) + cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + qm_set_value(_app_name FUNC_NAME ${_target}) + qm_set_value(_app_version FUNC_VERSION PROJECT_VERSION "0.0.0.0") + qm_set_value(_app_desc FUNC_DESCRIPTION ${_app_name}) + qm_set_value(_app_copyright FUNC_COPYRIGHT ${_app_name}) + + qm_parse_version(_app_version ${_app_version}) + + # configure mac plist + set_target_properties(${_target} PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_BUNDLE_NAME ${_app_name} + MACOSX_BUNDLE_EXECUTABLE_NAME ${_app_name} + MACOSX_BUNDLE_INFO_STRING ${_app_desc} + MACOSX_BUNDLE_GUI_IDENTIFIER ${_app_name} + MACOSX_BUNDLE_BUNDLE_VERSION ${_app_version} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${_app_version_1}.${_app_version_2} + MACOSX_BUNDLE_COPYRIGHT ${_app_copyright} + ) + + if(FUNC_ICON) + # And this part tells CMake where to find and install the file itself + set_source_files_properties(${FUNC_ICON} PROPERTIES + MACOSX_PACKAGE_LOCATION "Resources" + ) + + # NOTE: Don't include the path in MACOSX_BUNDLE_ICON_FILE -- this is + # the property added to Info.plist + get_filename_component(_icns_name ${FUNC_ICON} NAME) + + # configure mac plist + set_target_properties(${_target} PROPERTIES + MACOSX_BUNDLE_ICON_FILE ${_icns_name} + ) + + # ICNS icon MUST be added to executable's sources list, for some reason + # Only apple can do + target_sources(${_target} PRIVATE ${FUNC_ICON}) + endif() +endfunction() + +#[[ + Generate Windows shortcut after building target. + + qm_create_win_shortcut( + [OUTPUT_NAME ) + + set(_vbs_name ${CMAKE_CURRENT_BINARY_DIR}/${_target}_shortcut_$.vbs) + set(_vbs_temp ${_vbs_name}.in) + + set(_lnk_path "${_dir}/${_output_name}.lnk") + + set(SHORTCUT_PATH ${_lnk_path}) + set(SHORTCUT_TARGET_PATH $) + set(SHORTCUT_WORKING_DIRECOTRY $) + set(SHORTCUT_DESCRIPTION $) + set(SHORTCUT_ICON_LOCATION $) + + configure_file( + "${QMSETUP_MODULES_DIR}/windows/WinCreateShortcut.vbs.in" + ${_vbs_temp} + @ONLY + ) + file(GENERATE OUTPUT ${_vbs_name} INPUT ${_vbs_temp}) + + add_custom_command( + TARGET ${_target} POST_BUILD + COMMAND cscript ${_vbs_name} ${QMSETUP_IGNORE_STDOUT} + BYPRODUCTS ${_lnk_path} + VERBATIM + ) +endfunction() + +#[[ + Parse version and create seq vars with specified prefix. + + qm_parse_version( ) +]] # +function(qm_parse_version _prefix _version) + string(REPLACE "." ";" _version_list ${_version}) + list(LENGTH _version_list _version_count) + list(PREPEND _version_list 0) # Add placeholder + + foreach(_i RANGE 1 4) + if(_i LESS_EQUAL _version_count) + list(GET _version_list ${_i} _item) + else() + set(_item 0) + endif() + + set(${_prefix}_${_i} ${_item} PARENT_SCOPE) + endforeach() +endfunction() + +#[[ + Get shorter version number. + + qm_crop_version( ) +]] # +function(qm_crop_version _var _version _count) + qm_parse_version(FUNC ${_version}) + + set(_list) + + foreach(_i RANGE 1 ${_count}) + list(APPEND _list ${FUNC_${_i}}) + endforeach() + + string(JOIN "." _short_version ${_list}) + set(${_var} ${_short_version} PARENT_SCOPE) +endfunction() + +#[[ + Tell if there are any generator expressions in the string. + + qm_has_genex( ) +]] # +function(qm_has_genex _out _str) + string(GENEX_STRIP "${_str}" _no_genex) + + if("${_str}" STREQUAL "${_no_genex}") + set(_res off) + else() + set(_res on) + endif() + + set(${_out} ${_res} PARENT_SCOPE) +endfunction() + +#[[ + Tell if the given paths are same in canonical form. + + qm_paths_equal( ) +]] # +function(qm_paths_equal _out _path1 _path2) + # cmake_path(NORMAL_PATH) is introduced in CMake 3.20, we don't use it + # We call `get_filename_component` twice to normalize the paths + get_filename_component(_path1 ${_path1} ABSOLUTE) + get_filename_component(_path1 ${_path1} REALPATH) + + get_filename_component(_path2 ${_path2} ABSOLUTE) + get_filename_component(_path2 ${_path2} REALPATH) + + if(_path2 STREQUAL _path2) + set(${_out} on PARENT_SCOPE) + else() + set(${_out} off PARENT_SCOPE) + endif() +endfunction() + +#[[ + Set value if valid, otherwise use default. + + qm_set_value( ) +]] # +function(qm_set_value _key) + set(_args "${ARGN}") + list(POP_BACK _args _default) + + foreach(_item ${_args}) + if(${_item}) + set(${_key} ${${_item}} PARENT_SCOPE) + return() + endif() + endforeach() + + set(${_key} ${_default} PARENT_SCOPE) +endfunction() + +#[[ + Collect targets of given types recursively in a directory. + + qm_collect_targets( [DIRECTORY directory] + [EXECUTABLE] [SHARED] [STATIC] [UTILITY]) + + If one or more types are specified, return targets matching the types. + If no type is specified, return all targets. +]] # +function(qm_collect_targets _var) + set(options EXECUTABLE SHARED STATIC UTILITY) + set(oneValueArgs DIR) + set(multiValueArgs) + cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(FUNC_DIRECTORY) + set(_dir ${FUNC_DIRECTORY}) + else() + set(_dir ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + set(_tmp_targets) + + macro(get_targets_recursive _targets _dir) + get_property(_subdirs DIRECTORY ${_dir} PROPERTY SUBDIRECTORIES) + + foreach(_subdir ${_subdirs}) + get_targets_recursive(${_targets} ${_subdir}) + endforeach() + + get_property(_current_targets DIRECTORY ${_dir} PROPERTY BUILDSYSTEM_TARGETS) + list(APPEND ${_targets} ${_current_targets}) + endmacro() + + # Get targets + get_targets_recursive(_tmp_targets ${_dir}) + set(_targets) + + if(NOT FUNC_EXECUTABLE AND NOT FUNC_SHARED AND NOT FUNC_STATIC AND NOT FUNC_UTILITY) + set(_targets ${_tmp_targets}) + else() + # Filter targets + foreach(_item ${_tmp_targets}) + get_target_property(_type ${_item} TYPE) + + if("${_type}" STREQUAL "EXECUTABLE") + if(FUNC_EXECUTABLE) + list(APPEND _targets ${_item}) + endif() + elseif("${_type}" STREQUAL "SHARED_LIBRARY") + if(FUNC_SHARED) + list(APPEND _targets ${_item}) + endif() + elseif("${_type}" STREQUAL "STATIC_LIBRARY") + if(FUNC_STATIC) + list(APPEND _targets ${_item}) + endif() + elseif("${_type}" STREQUAL "UTILITY") + if(FUNC_UTILITY) + list(APPEND _targets ${_item}) + endif() + endif() + endforeach() + endif() + + set(${_var} ${_targets} PARENT_SCOPE) +endfunction() + +#[[ + Get subdirectories' names or paths. + + qm_get_subdirs( + [DIRECTORY dir] + [EXCLUDE names...] + [REGEX_INCLUDE exps...] + [REGEX_EXLCUDE exps...] + [RELATIVE path] + [ABSOLUTE] + ) + + If `DIRECTORY` is not specified, consider `CMAKE_CURRENT_SOURCE_DIR`. + If `RELATIVE` is specified, return paths evaluated as a relative path to it. + If `ABSOLUTE` is specified, return absolute paths. + If neither of them is specified, return names. +]] # +function(qm_get_subdirs _var) + set(options ABSOLUTE) + set(oneValueArgs DIRECTORY RELATIVE) + set(multiValueArgs EXCLUDE REGEX_EXLCUDE) + cmake_parse_arguments(FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(FUNC_DIRECTORY) + get_filename_component(_dir ${FUNC_DIRECTORY} ABSOLUTE) + else() + set(_dir ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + file(GLOB _subdirs LIST_DIRECTORIES true RELATIVE ${_dir} "${_dir}/*") + + if(FUNC_EXCLUDE) + foreach(_exclude_dir ${FUNC_EXCLUDE}) + list(REMOVE_ITEM _subdirs ${_exclude_dir}) + endforeach() + endif() + + if(FUNC_REGEX_INCLUDE) + foreach(_exp ${FUNC_REGEX_INCLUDE}) + list(FILTER _subdirs INCLUDE REGEX ${_exp}) + endforeach() + endif() + + if(FUNC_REGEX_EXCLUDE) + foreach(_exp ${FUNC_REGEX_EXCLUDE}) + list(FILTER _subdirs EXCLUDE REGEX ${_exp}) + endforeach() + endif() + + set(_res) + + if(FUNC_RELATIVE) + get_filename_component(_relative ${FUNC_RELATIVE} ABSOLUTE) + else() + set(_relative) + endif() + + foreach(_sub ${_subdirs}) + if(IS_DIRECTORY ${_dir}/${_sub}) + if(FUNC_ABSOLUTE) + list(APPEND _res ${_dir}/${_sub}) + elseif(_relative) + file(RELATIVE_PATH _rel_path ${_relative} ${_dir}/${_sub}) + list(APPEND _res ${_rel_path}) + else() + list(APPEND _res ${_sub}) + endif() + endif() + endforeach() + + set(${_var} ${_res} PARENT_SCOPE) +endfunction() + +# ---------------------------------- +# Private functions +# ---------------------------------- +macro(_qm_check_target_type_helper _target _type) + set(_tmp_target_type_list ${ARGN}) + get_target_property(_tmp_target_type ${_target} TYPE) + + if(NOT "${_tmp_target_type}" IN_LIST _tmp_target_type_list) + return() + endif() + + set(${_type} ${_tmp_target_type}) + unset(_tmp_target_type) + unset(_tmp_target_type_list) +endmacro() diff --git a/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/cmake/doxygen/Doxyfile b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/cmake/doxygen/Doxyfile new file mode 100644 index 0000000000000000000000000000000000000000..d812cface5b2602256fb51805dffe0bc35375975 --- /dev/null +++ b/third_party/SARibbon/SARibbonBar_amalgamate/SARibbonBar/3rdparty/qwindowkit/qmsetup/cmake/doxygen/Doxyfile @@ -0,0 +1,2867 @@ +# Doxyfile 1.9.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +@INCLUDE = "$(DOXY_INCLUDE_FILE)" + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "$(DOXY_PROJECT_NAME)" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = "$(DOXY_PROJECT_VERSION)" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "$(DOXY_PROJECT_BRIEF)" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = "$(DOXY_PROJECT_LOGO)" + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = "$(DOXY_OUTPUT_DIR)" + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = NO + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = YES + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + +CASE_SENSE_NAMES = SYSTEM + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = YES + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +# INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, +# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php, +# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be +# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cxxm \ + *.cpp \ + *.cppm \ + *.c++ \ + *.c++m \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.ixx \ + *.l \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = "$(DOXY_MAINPAGE_MD_FILE)" + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = "$(DOXY_FILE_DIR)/header.html" + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = \ + "$(DOXY_FILE_DIR)/doxygen-awesome.css" \ + "$(DOXY_FILE_DIR)/doxygen-awesome-sidebar-only-darkmode-toggle.css" \ + "$(DOXY_FILE_DIR)/extra-stylesheet.css" + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = "$(DOXY_FILE_DIR)/doxygen-awesome-darkmode-toggle.js" + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = YES + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /