Compare commits

...

24 Commits

Author SHA1 Message Date
github-actions[bot] b03849d6de chore: New release 5.7.25
Log:
2025-10-30 13:59:43 +00:00
zhangkun 187be9a947 feat: improve icon theme generation with scalable support
1. Add SCALABLE_SIZE constant for scalable icon directories
2. Change foundSize return type from uint to int to support scalable
detection
3. Modify writeScaledImage to accept baseSize parameter instead of
parsing from directory
4. Move directory parsing logic to writeImage function for better
organization
5. Add scalable directory detection in size parsing logic
6. Use QImageReader's scaledSize for better image scaling quality
7. Fix error handling to continue processing instead of exiting on first
error

Log: Improved icon theme generation with better scalable icon support

Influence:
1. Test with icon directories containing scalable icons
2. Verify different scale factors work correctly
3. Test error handling when encountering invalid image files
4. Verify image quality at various scales
5. Test with both fixed-size and scalable icon directories

feat: 改进图标主题生成,支持可缩放图标

1. 添加 SCALABLE_SIZE 常量用于可缩放图标目录
2. 将 foundSize 返回类型从 uint 改为 int 以支持可缩放检测
3. 修改 writeScaledImage 函数,接受 baseSize 参数而非从目录解析
4. 将目录解析逻辑移至 writeImage 函数以获得更好的代码组织
5. 在尺寸解析逻辑中添加可缩放目录检测
6. 使用 QImageReader 的 scaledSize 获得更好的图像缩放质量
7. 修复错误处理,在遇到错误时继续处理而非立即退出

Log: 改进图标主题生成,提供更好的可缩放图标支持

Influence:
1. 测试包含可缩放图标的目录
2. 验证不同缩放因子是否正确工作
3. 测试遇到无效图像文件时的错误处理
4. 验证各种缩放比例下的图像质量
5. 测试固定尺寸和可缩放图标目录
2025-10-23 08:41:20 +00:00
github-actions[bot] 16145642e1 chore: New release 5.7.24
Log:
2025-10-16 20:13:34 +08:00
zhangkun 39a50cca37 feat: improve icon size detection and duplicate handling
1. Added foundSize function to parse icon size from directory names
using multiple strategies
2. Implemented duplicate entry detection to skip existing icons in DCI
files
3. Added QFileInfo and QLogging includes for new functionality
4. Removed debug-specific code that was adding unnecessary prefixes
5. Improved size detection logic to handle both numeric and "NxN" format
directory names
6. Added fallback to parent directory when current directory name
doesn't contain size information

Log: Improved icon theme generation with better size detection and
duplicate prevention

Influence:
1. Test icon theme generation with various directory naming conventions
2. Verify that duplicate icons are properly skipped during generation
3. Test with directories containing numeric names vs "widthxheight"
format
4. Verify fallback to parent directory size detection works correctly
5. Check that existing icons in DCI files are not overwritten
6. Test with different directory structures and naming patterns

feat: 改进图标尺寸检测和重复项处理

1. 添加 foundSize 函数,使用多种策略从目录名解析图标尺寸
2. 实现重复条目检测,跳过 DCI 文件中已存在的图标
3. 添加 QFileInfo 和 QLogging 包含以支持新功能
4. 移除添加不必要前缀的调试专用代码
5. 改进尺寸检测逻辑,支持数字和"NxN"格式的目录名
6. 在当前目录名不包含尺寸信息时,添加回退到父目录的检测

Log: 改进图标主题生成,提供更好的尺寸检测和重复项预防

Influence:
1. 测试使用不同目录命名约定的图标主题生成
2. 验证在生成过程中重复图标是否被正确跳过
3. 测试包含数字名称与"宽x高"格式的目录
4. 验证回退到父目录尺寸检测是否正常工作
5. 检查 DCI 文件中现有图标是否不会被覆盖
6. 测试不同的目录结构和命名模式
2025-10-16 11:05:03 +00:00
yeshanshan 2767618f84 fix: fix Qt6 compilation error with libxdg
1. Restructured the conditional logic for libxdg usage to avoid Qt6
compilation errors
2. Moved the DTK_DISABLE_LIBXDG check to outer scope to properly handle
both Qt5 and Qt6
3. Added explicit Qt6 handling by disabling libxdg when not using Qt5
4. This resolves build failures in Qt6 environments where
Qt5XdgIconLoader is not available

The previous implementation only checked for Qt5XdgIconLoader when
using Qt5, but didn't properly handle Qt6 scenarios, causing compilation
errors when building with Qt6.

fix: 修复 Qt6 下 libxdg 编译错误

1. 重构了 libxdg 使用的条件逻辑以避免 Qt6 编译错误
2. 将 DTK_DISABLE_LIBXDG 检查移到外层作用域以正确处理 Qt5 和 Qt6
3. 添加了明确的 Qt6 处理逻辑,在非 Qt5 环境下禁用 libxdg
4. 解决了在 Qt6 环境中因 Qt5XdgIconLoader 不可用而导致的构建失败问题

之前的实现只在 Qt5 环境下检查 Qt5XdgIconLoader,但没有正确处理 Qt6 场
景,导致在使用 Qt6 构建时出现编译错误。
2025-10-16 17:39:56 +08:00
ComixHe 36c06be095 fix: correct lockfile path construction
Move process ID namespace handling from lockfile to socket_key variable to ensure consistent lock file paths.
Reorganize code structure for proper execution order.

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-10-16 13:14:00 +08:00
ComixHe 80db4dd238 feat: support Qt 6.10
Also adjusting the logic for finding QtXDGIconLoader.
This library only be used in Qt 5.

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-10-13 13:03:30 +08:00
github-actions[bot] 3d0713f7a2 chore: New release 5.7.23
Log:
2025-09-25 17:44:27 +08:00
Mingcong Bai af1259edbb fix(tests): fix build for Qt 6.9
Qt 6.9 renamed <private/qdesktopunixservices_p.h> to
<private/qgenericunixservices_p.h>, as well as some of the classes along
the same format (QGenericUnixServices => QDesktopUnixServices).

Add a condition test with QT_VERSION to fix build with Qt >= 6.9.

Signed-off-by: Mingcong Bai <jeffbai@aosc.io>
2025-09-24 16:17:19 +08:00
Robertkill 3b5ba9bbda fix: 解决控制中心闪烁问题
初始化时立即读取并设置主题色,避免异步出现主题色闪烁问题

Log: 修复配置管理以使用标准 DConfig 接口

Influence:
1. 测试主题切换功能以确保正确的主题类型检测
2. 验证动画设置使用新的配置方法正常工作
3. 检查主题偏好是否正确保存和恢复
4. 测试配置值缺失或无效时的应用程序行为

PMS: BUG-334439
2025-09-18 13:26:13 +08:00
github-actions[bot] c8f2955bd0 chore: New release 5.7.22
Log:
2025-09-04 20:06:26 +08:00
zhangkun 96d0b45dd0 feat: add T11 font size and adjust T5 default
Added T11 font size with 8px default value to extend the font size
options
Modified T5 default size from 17px to 16px for better size progression
Updated font pixel size array to include the new T11 size
Added t11() accessor method and corresponding test case
cherry-pick from: 2c3b351b43

feat: 添加 T11 字体大小并调整 T5 默认值

添加了默认值为 8px 的 T11 字体大小以扩展字体大小选项
将 T5 默认大小从 17px 修改为 16px 以获得更好的大小递进
更新了字体像素大小数组以包含新的 T11 大小
添加了 t11() 访问器方法和相应的测试用例
cherry-pick from: 2c3b351b43

pms: BUG-310879
2025-09-01 11:27:52 +08:00
ComixHe 00109d07e3 fix: improve PID namespace detection in setSingleInstance
Replace stat-based PID namespace detection with QFileInfo approach
for better compatibility across different kernel versions and
containerized environments. Add robust parsing and fallback handling
for namespace format variations.

PMS: BUG-329663
Signed-off-by: ComixHe <heyuming@deepin.org>
2025-08-19 10:14:09 +08:00
github-actions[bot] d2c2efc49a chore: New release 5.7.21
Log:
2025-08-14 20:28:21 +08:00
yeshanshan 9842a0644e fix: resolve compilation warnings and deprecation issues
1. Fixed deprecated enum usage by adding explicit deprecation comment
for IconAttibute
2. Added QT version checks for QMouseEvent::globalPos() vs
globalPosition() API changes
3. Marked unused parameters with Q_UNUSED to suppress warnings
4. Added version checks for DTK 6.0 API changes
5. Fixed test class visibility warnings by adding GTEST_API_ prefix
6. Improved warning messages to use modern qWarning() stream syntax
7. Added [[maybe_unused]] attribute for DEFINE_CONST_CHAR macro
8. Added version checks for QVariant type handling differences between
Qt5/Qt6
9. Fixed potential memory leak in DIconTheme::isXdgIcon() logic
10. Added proper type checks for QVariant conversions

Log: Fixed various compilation warnings and deprecated API usage

Influence:
1. Verify icon rendering still works correctly after enum changes
2. Test window movement functionality with both Qt5 and Qt6
3. Check palette handling in DGuiApplicationHelper
4. Verify test cases still pass with updated class visibility
5. Test QVariant conversions in DPlatformHandle properties

fix: 解决编译警告和废弃API问题

1. 为废弃的IconAttibute枚举添加明确的弃用注释
2. 为QMouseEvent::globalPos()和globalPosition()API变更添加QT版本检查
3. 使用Q_UNUSED标记未使用参数以消除警告
4. 添加DTK 6.0 API变更的版本检查
5. 通过添加GTEST_API_前缀修复测试类可见性警告
6. 改进警告消息使用现代qWarning()流式语法
7. 为DEFINE_CONST_CHAR宏添加[[maybe_unused]]属性
8. 添加Qt5/Qt6之间QVariant类型处理的版本检查
9. 修复DIconTheme::isXdgIcon()中潜在的内存泄漏问题
10. 为QVariant转换添加适当的类型检查

Log: 修复各种编译警告和废弃API使用问题

Influence:
1. 验证图标渲染在枚举变更后仍能正常工作
2. 测试Qt5和Qt6下的窗口移动功能
3. 检查DGuiApplicationHelper中的调色板处理
4. 验证更新类可见性后测试用例是否仍能通过
5. 测试DPlatformHandle属性中的QVariant转换
2025-08-14 11:39:10 +00:00
yeshanshan 9d3d659afe feat: add flags parameter to DDciIconPlayer play method
1. Added new overloaded play method that accepts
DDciIconImagePlayer::Flags parameter
2. Modified existing play method to call new method with default
IgnoreLastImageLoop flag
3. Updated documentation to reflect new method signature and behavior
4. Allows more control over icon playback behavior through flags
parameter

feat: 为 DDciIconPlayer 的 play 方法添加 flags 参数

1. 添加了新的重载 play 方法,接受 DDciIconImagePlayer::Flags 参数
2. 修改现有 play 方法以使用默认的 IgnoreLastImageLoop 标志调用新方法
3. 更新文档以反映新的方法签名和行为
4. 通过 flags 参数提供对图标播放行为的更多控制
2025-08-07 14:38:07 +08:00
github-actions[bot] eaf96e824d chore: New release 5.7.20
Log:
2025-07-31 20:29:47 +08:00
zhangkun 7c05bace4a feat: switch to QGuiApplication for SVG rendering
1. Changed from QCoreApplication to QGuiApplication to support SVG
rendering
2. Updated CMakeLists to link against QtGui instead of QtCore
3. This change is necessary because SVG rendering requires GUI
components
4. Note: CLI environment users must set QT_QPA_PLATFORM=offscreen

feat: 切换到 QGuiApplication 以支持 SVG 渲染

1. 从 QCoreApplication 切换到 QGuiApplication 以支持 SVG 渲染
2. 更新 CMakeLists 链接 QtGui 替代 QtCore
3. 此变更是必要的因为 SVG 渲染需要 GUI 组件
4. 注意:命令行环境用户需要设置 QT_QPA_PLATFORM=offscreen
2025-07-31 13:42:53 +08:00
yeshanshan a892ddc4e2 feat: add concurrent processing for DCI icon conversion
1. Added QtConcurrent support for parallel processing of icon files
2. Implemented error handling with custom DciProcessingError exception
3. Grouped icon files by name to avoid concurrent access to same DCI
file
4. Improved performance by processing icon groups in parallel
5. Added proper error propagation and atomic flag for early termination
6. Maintained existing functionality while adding concurrency
7. Each icon group is processed as a unit to ensure thread safety with
DCI files

feat: 为DCI图标转换添加并发处理

1. 添加QtConcurrent支持以实现图标文件的并行处理
2. 使用自定义DciProcessingError异常实现错误处理
3. 按名称分组图标文件以避免对同一DCI文件的并发访问
4. 通过并行处理图标组提高性能
5. 添加适当的错误传播和原子标志用于提前终止
6. 在添加并发性的同时保持现有功能
7. 每个图标组作为一个单元处理以确保DCI文件的线程安全
2025-07-24 20:49:35 +08:00
yeshanshan 65cf7b4161 refactor: move icon finder functionality to separate tool
1. Removed icon finding functionality from dci-icon-theme to make it a
pure TUI tool
2. Created new dci-iconfinder tool specifically for icon lookup
operations
3. Changed dci-icon-theme to use QCoreApplication instead of
QGuiApplication
4. Updated version handling to use DTK version macros
5. Removed GUI dependencies from dci-icon-theme
6. Added proper documentation for both tools

This change was necessary to separate concerns and allow dci-icon-theme
to be used in environments without GUI dependencies while maintaining
icon lookup capabilities in a dedicated tool.

refactor: 将图标查找功能移至独立工具

1. 从dci-icon-theme中移除图标查找功能,使其成为纯命令行工具
2. 创建新的dci-iconfinder工具专门处理图标查找操作
3. 将dci-icon-theme改为使用QCoreApplication而非QGuiApplication
4. 更新版本处理以使用DTK版本宏
5. 移除dci-icon-theme对GUI的依赖
6. 为两个工具添加了适当的文档

此变更是为了分离关注点,使dci-icon-theme可以在没有GUI依赖的环境中使用,
同时将图标查找功能保留在专用工具中。
2025-07-23 04:16:59 +00:00
ComixHe 6b7e941839 feat: add pid namespace isolation to setSingleInstance
Enhance setSingleInstance to support multiple PID namespaces for
containerized environments. This prevents applications in different
containers from interfering with each other's single instance detection.

The lockfile format becomes: {original_name}_{namespace_inode}.lock,
ensuring unique identification across different PID namespaces.

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-07-22 14:19:01 +08:00
yeshanshan dc2eaeb61c docs: update REUSE license file for CHANGELOG
Added CHANGELOG.md to the list of files under CC-BY-4.0 license
in .reuse/dep5
This change ensures proper licensing attribution for the changelog file
The modification follows the project's REUSE compliance standards

docs: 更新 REUSE 许可文件以包含 CHANGELOG

在 .reuse/dep5 文件中将 CHANGELOG.md 添加到 CC-BY-4.0 许可证下的文件列表
此变更确保了对变更日志文件的正确许可归属
修改遵循项目的 REUSE 合规标准
2025-07-21 09:55:22 +08:00
github-actions[bot] 4dd22b538d chore: New release 5.7.19
Log:
2025-07-03 13:23:09 +00:00
yeshanshan 2ea4e762fe fix: enhance build security hardening
1. Added security hardening compiler flags in debian/rules including
stack protection and RELRO
2. Removed redundant linker flag from dtkgui.cmake that was already set
in debian/rules
3. Consolidated security-related build flags in debian/rules for better
maintainability
4. The changes improve binary security against common exploits while
maintaining compatibility

fix: 增强构建安全加固

1. 在debian/rules中添加了安全加固编译标志,包括栈保护和RELRO
2. 从dtkgui.cmake中移除了已在debian/rules中设置的冗余链接器标志
3. 将安全相关的构建标志整合到debian/rules中以提高可维护性
4. 这些改动提高了二进制文件对常见漏洞攻击的防护能力,同时保持兼容性
2025-07-03 13:03:14 +00:00
52 changed files with 625 additions and 164 deletions

View File

@ -34,7 +34,7 @@ Copyright: None
License: LGPL-3.0-or-later
# README
Files: *README.md *README.zh_CN.md
Files: *README.md *README.zh_CN.md CHANGELOG.md
Copyright: None
License: CC-BY-4.0

View File

@ -5,6 +5,73 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [5.7.25] - 2025-10-30
### Changed
- Improve icon theme generation with scalable support
## [5.7.24] - 2025-10-16
### Added
- Support Qt 6.10
### Changed
- Improve icon size detection and duplicate handling
### Fixed
- Correct lockfile path construction
- Fix Qt6 compilation error with libxdg
## [5.7.23] - 2025-09-25
### Fixed
- 解决控制中心闪烁问题
- Fix build for Qt 6.9
## [5.7.22] - 2025-09-04
### Added
- Add T11 font size and adjust T5 default
### Fixed
- Improve PID namespace detection in setSingleInstance
## [5.7.21] - 2025-08-14
### Added
- Add flags parameter to DDciIconPlayer play method
### Fixed
- Resolve compilation warnings and deprecation issues
## [5.7.20] - 2025-07-31
### Added
- Add pid namespace isolation to setSingleInstance
- Add concurrent processing for DCI icon conversion
### Changed
- Update REUSE license file for CHANGELOG
- Move icon finder functionality to separate tool
- Switch to QGuiApplication for SVG rendering
## [5.7.19] - 2025-07-03
### Fixed
- Enhance build security hardening
## [5.7.18] - 2025-06-27
### Fixed

View File

@ -1 +1 @@
5.7.18
5.7.25

View File

@ -1,6 +1,6 @@
# Maintainer: justforlxz <justforlxz@gmail.com>
pkgname=dtkgui-git
pkgver=5.7.18
pkgver=5.7.25
pkgrel=1
sourcename=dtkgui
sourcetars=("$sourcename"_"$pkgver".tar.xz)

42
debian/changelog vendored
View File

@ -1,3 +1,45 @@
dtkgui (5.7.25) unstable; urgency=medium
* Release 5.7.25
-- yeshanshan <yeshanshan@uniontech.com> Thu, 30 Oct 2025 21:47:33 +0800
dtkgui (5.7.24) unstable; urgency=medium
* Release 5.7.24
-- yeshanshan <yeshanshan@uniontech.com> Thu, 16 Oct 2025 19:45:30 +0800
dtkgui (5.7.23) unstable; urgency=medium
* Release 5.7.23
-- yeshanshan <packages@deepin.org> Thu, 25 Sep 2025 16:49:18 +0800
dtkgui (5.7.22) unstable; urgency=medium
* Release 5.7.22
-- yeshanshan <yeshanshan@uniontech.com> Thu, 04 Sep 2025 19:27:38 +0800
dtkgui (5.7.21) unstable; urgency=medium
* Release 5.7.21
-- yeshanshan <packages@deepin.org> Thu, 14 Aug 2025 19:47:40 +0800
dtkgui (5.7.20) unstable; urgency=medium
* Release 5.7.20
-- yeshanshan <yeshanshan@uniontech.com> Thu, 31 Jul 2025 19:59:35 +0800
dtkgui (5.7.19) unstable; urgency=medium
* Release 5.7.19
-- yeshanshan <yeshanshan@uniontech.com> Thu, 03 Jul 2025 21:12:27 +0800
dtkgui (5.7.18) unstable; urgency=medium
* Release 5.7.18

6
debian/rules vendored
View File

@ -3,6 +3,12 @@ DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/default.mk
export QT_SELECT = qt5
# 安全编译参数
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export DEB_CFLAGS_MAINT_APPEND = -Wall
export DEB_CXXFLAGS_MAINT_APPEND = -Wall
export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack -Wl,-E
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
VERSION = $(DEB_VERSION_UPSTREAM)

View File

@ -171,6 +171,11 @@
@details 为指定的图标模式计算要显示的内容,这可能会导致 currentImage 发生变化。这是个一次性行为,不会影响下一次调用 setMode 后的结果。
@param[in] mode 指定要播放的图标模式,如果此模式支持动画,会触发此模式自身的动画播放(与 setMode 导致的动画不同的是,此动画不涉及两种模式变化时的过渡规则),否则将修改 currentImage 为此模式对应的静态图片资源。
@fn Dtk::Gui::DDciIconPlayer::play(DDciIcon::Mode mode, DDciIconImagePlayer::Flags flags)
@details 为指定的图标模式和播放参数计算要显示的内容。
@param[in] mode 指定要播放的图标模式。
@param[in] flags 播放参数
@fn Dtk::Gui::DDciIconPlayer::stop()
@details 如果当前正在播放动画,则停止当前动画的播放,但不会影响队列中下一个动画的播放。可能会导致 DDciIconPlayer::state 发生变化。
@sa DDciIconPlayer::abort

View File

@ -18,11 +18,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Set build option
option(DTK_DISABLE_LIBXDG "Disable libxdg" OFF)
find_package(Qt${QT_VERSION_MAJOR}XdgIconLoader)
if (NOT Qt${QT_VERSION_MAJOR}XdgIconLoader_FOUND)
message(WARNING " XdgIconLoader Not Found, DISABLE LIBXDG !")
set (DTK_DISABLE_LIBXDG ON)
endif()
option(DTK_DISABLE_LIBRSVG "Disable librsvg" OFF)
option(DTK_DISABLE_EX_IMAGE_FORMAT "Disable libraw and freeimage" OFF)
@ -43,7 +38,6 @@ set(CONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/Dtk${DTK_VERSION_MAJOR}Gui
set(PKGCONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Wextra -fopenmp")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--as-needed")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie")
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(BUILD_TESTING ON)
@ -121,6 +115,19 @@ find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED Core)
find_package(DtkBuildHelper REQUIRED)
pkg_check_modules(librsvg REQUIRED IMPORTED_TARGET librsvg-2.0)
if(NOT DTK_DISABLE_LIBXDG)
# Only use libxdg under Qt5
if (${QT_VERSION_MAJOR} STREQUAL "5")
find_package(Qt5XdgIconLoader REQUIRED)
if (NOT Qt5XdgIconLoader_FOUND)
message(WARNING " XdgIconLoader Not Found, DISABLE LIBXDG !")
set (DTK_DISABLE_LIBXDG ON)
endif()
else()
set (DTK_DISABLE_LIBXDG ON)
endif()
endif()
# Check optional image handler dependencies.
find_package(FreeImage)
pkg_check_modules(libraw IMPORTED_TARGET libraw)

View File

@ -77,7 +77,8 @@ public:
Light = 0,
Dark = 1
};
D_DECL_DEPRECATED enum IconAttibute {
// IconAttibute is deprecated, please use IconAttribute instead.
enum IconAttibute {
HasPalette = 0x001
};
using IconAttribute = DDciIcon::IconAttibute;

View File

@ -106,6 +106,7 @@ public:
QImage currentImage() const;
void play(DDciIcon::Mode mode);
void play(DDciIcon::Mode mode, DDciIconImagePlayer::Flags flags);
void stop();
void abort();

View File

@ -44,6 +44,7 @@ public:
T8,
T9,
T10,
T11,
NSizeTypes
};
Q_ENUM(SizeType)
@ -110,6 +111,10 @@ public:
{
return get(T10);
}
inline const QFont t11() const
{
return get(T11);
}
Q_SIGNALS:
void fontChanged();

View File

@ -1,5 +1,5 @@
Name: dtkgui
Version: 5.7.18
Version: 5.7.25
Release: 1%{?dist}
Summary: Deepin dtkgui
License: LGPLv3+

View File

@ -1,5 +1,10 @@
if("${QT_VERSION_MAJOR}" STREQUAL "6")
find_package(Qt6 REQUIRED COMPONENTS Core Widgets WaylandClient)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets WaylandClient)
if (${Qt6Core_VERSION} VERSION_GREATER_EQUAL "6.10.0")
set(QT_NO_PRIVATE_MODULE_WARNING ON)
find_package(Qt6 REQUIRED COMPONENTS CorePrivate GuiPrivate WaylandClientPrivate)
endif()
else()
find_package(Qt5 REQUIRED COMPONENTS WaylandClient XkbCommonSupport)
endif()

View File

@ -60,6 +60,7 @@
#ifdef Q_OS_LINUX
#include <pwd.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
@ -321,7 +322,7 @@ DPlatformTheme *DGuiApplicationHelperPrivate::initWindow(QWindow *window) const
window->setProperty(WINDOW_THEME_KEY, QVariant::fromValue(theme));
theme->setParent(window); // 跟随窗口销毁
auto onWindowThemeChanged = [window, theme, this] {
auto onWindowThemeChanged = [window, this] {
// 如果程序自定义了调色板, 则没有必要再关心窗口自身平台主题的变化
// 需要注意的是, 这里的信号和事件可能会与 notifyAppThemeChanged 中的重复
// 但是不能因此而移除这里的通知, 当窗口自身所对应的平台主题发生变化时, 这里
@ -391,7 +392,12 @@ void DGuiApplicationHelperPrivate::notifyAppThemeChanged()
void DGuiApplicationHelperPrivate::notifyAppThemeChangedByEvent()
{
// https://github.com/qt/qtbase/commit/68a9c5fe513e147e4cffd29b50a4714813df411e
#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
QWindowSystemInterfacePrivate::ThemeChangeEvent event;
#else
QWindowSystemInterfacePrivate::ThemeChangeEvent event(nullptr);
#endif
// 此事件会促使QGuiApplication重新从QPlatformTheme中获取系统级别的QPalette.
// 而在deepin平台下, 系统级别的QPalette来源自 \a applicationPalette()
QGuiApplicationPrivate::processThemeChanged(&event);
@ -455,7 +461,10 @@ void DGuiApplicationHelperPrivate::initPaletteType() const
const_cast<DGuiApplicationHelperPrivate *>(this)->setPaletteType(DGuiApplicationHelper::ColorType(ct), emitSignal);
};
applyThemeType(false);
// 读取配置文件中的主题类型并立即应用
DTK_CORE_NAMESPACE::DConfig dconfig("org.deepin.dtk.preference");
int ct = dconfig.value("themeType", DGuiApplicationHelper::UnknownType).toInt();
const_cast<DGuiApplicationHelperPrivate *>(this)->setPaletteType(DGuiApplicationHelper::ColorType(ct), false);
QObject::connect(_d_dconfig.operator ()(), &OrgDeepinDTKPreference::themeTypeChanged, _d_dconfig, [applyThemeType] {
applyThemeType(true);
@ -505,13 +514,13 @@ DGuiApplicationHelper::SizeMode DGuiApplicationHelperPrivate::fetchSizeMode(bool
/*!
\enum DGuiApplicationHelper::ColorType
DGuiApplicationHelper::ColorType .
\var DGuiApplicationHelper::ColorType DGuiApplicationHelper::UnknownType
()
\var DGuiApplicationHelper::ColorType DGuiApplicationHelper::LightType
\var DGuiApplicationHelper::ColorType DGuiApplicationHelper::DarkType
*/
@ -519,28 +528,28 @@ DGuiApplicationHelper::SizeMode DGuiApplicationHelperPrivate::fetchSizeMode(bool
/*!
\enum DGuiApplicationHelper::Attribute
DGuiApplicationHelper::Attribute
\var DGuiApplicationHelper::Attribute DGuiApplicationHelper::UseInactiveColorGroup
Inactive状态时就会使用QPalette::Inactive的颜色
\var DGuiApplicationHelper::Attribute DGuiApplicationHelper::ColorCompositing
\var DGuiApplicationHelper::Attribute DGuiApplicationHelper::ReadOnlyLimit
\var DGuiApplicationHelper::Attribute DGuiApplicationHelper::IsDeepinPlatformTheme
使deepin的platformtheme插件platformtheme插件可以为Qt程序提供特定的控件样式使chameleon主题
\var DGuiApplicationHelper::Attribute DGuiApplicationHelper::IsDXcbPlatform
使dtk的xcb窗口插件dxcb插件提供了窗口圆角和阴影功能
\var DGuiApplicationHelper::Attribute DGuiApplicationHelper::IsXWindowPlatform
X11环境中
\var DGuiApplicationHelper::Attribute DGuiApplicationHelper::IsTableEnvironment
deepin平板环境中XDG_CURRENT_DESKTOP环境变量是不是tablet结尾
\var DGuiApplicationHelper::Attribute DGuiApplicationHelper::IsDeepinEnvironment
deepin桌面环境中XDG_CURRENT_DESKTOP环境变量是不是deepin
*/
@ -1334,6 +1343,11 @@ void DGuiApplicationHelper::setApplicationPalette(const DPalette &palette)
*/
DPalette DGuiApplicationHelper::windowPalette(QWindow *window) const
{
#if DTK_VERSION >= DTK_VERSION_CHECK(5, 0, 0, 0)
Q_UNUSED(window);
qCWarning(dgAppHelper) << "DGuiApplicationHelper::windowPalette is deprecated, please use applicationPalette instead.";
return applicationPalette();
#else
D_DC(DGuiApplicationHelper);
// 如果程序自定义了调色版, 则不再关心窗口对应的平台主题上的设置
@ -1348,6 +1362,7 @@ DPalette DGuiApplicationHelper::windowPalette(QWindow *window) const
}
return fetchPalette(theme);
#endif
}
#endif
@ -1487,11 +1502,37 @@ bool DGuiApplicationHelper::setSingleInstance(const QString &key, DGuiApplicatio
}
socket_key += key;
#ifdef Q_OS_LINUX
auto info = QFileInfo{"/proc/self/ns/pid"};
// maybe kernel doesn't support namespace
while (info.exists()) {
auto pidns = QFileInfo{info.symLinkTarget()}.fileName();
if (pidns.isEmpty()) {
break;
}
// maybe format has been changed
auto start = pidns.indexOf('[');
auto end = pidns.indexOf(']');
if (start == -1 || end == -1) {
break;
}
auto num = pidns.mid(start + 1, end - start - 1);
// append pid namespace
socket_key += QStringLiteral("_%1").arg(num);
break;
}
#endif
QString lockfile = socket_key;
if (!lockfile.startsWith(QLatin1Char('/'))) {
lockfile = QDir::cleanPath(QDir::tempPath());
lockfile += QLatin1Char('/') + socket_key;
}
lockfile += QStringLiteral(".lock");
static QScopedPointer <QLockFile> lock(new QLockFile(lockfile));
// 同一个进程多次调用本接口使用最后一次设置的 key
@ -1688,7 +1729,7 @@ bool DGuiApplicationHelper::loadTranslator(const QString &fileName, const QList<
if (locale.language() != QLocale::English) // English does not need translation
localeNames << locale.name();
}
if (!localeNames.isEmpty()) {
qWarning() << fileName << "can not find qm files for locales" << localeNames;
}

View File

@ -280,6 +280,7 @@ DPalette DPlatformTheme::fetchPalette(const DPalette &base, bool *ok) const
void DPlatformTheme::setPalette(const DPalette &palette)
{
Q_UNUSED(palette);
#define SET_PALETTE(Role) \
set##Role(palette.color(QPalette::Normal, DPalette::Role))
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)

View File

@ -28,6 +28,11 @@ if("${QT_VERSION_MAJOR}" STREQUAL "6")
qt6_generate_wayland_protocol_client_sources(${PROJECT_NAME} FILES
${TREELAND_PROTOCOLS_DATA_DIR}/treeland-personalization-manager-v1.xml
)
if (${Qt6Core_VERSION} VERSION_GREATER_EQUAL "6.10.0")
set(QT_NO_PRIVATE_MODULE_WARNING ON)
find_package(Qt6 REQUIRED COMPONENTS GuiPrivate CorePrivate WaylandClientPrivate)
endif()
else()
# ECM setup
include(FeatureSummary)

View File

@ -95,11 +95,19 @@ bool MoveWindowHelper::windowEvent(QWindow *w, QEvent *event)
isTouchDown = false;
}
if (isTouchDown && event->type() == QEvent::MouseButtonPress) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
touchBeginPosition = static_cast<QMouseEvent*>(event)->globalPosition();
#else
touchBeginPosition = static_cast<QMouseEvent*>(event)->globalPos();
#endif
}
// add some redundancy to distinguish trigger between system menu and system move
if (event->type() == QEvent::MouseMove) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QPointF currentPos = static_cast<QMouseEvent*>(event)->globalPosition();
#else
QPointF currentPos = static_cast<QMouseEvent*>(event)->globalPos();
#endif
QPointF delta = touchBeginPosition - currentPos;
if (delta.manhattanLength() < QGuiApplication::styleHints()->startDragDistance()) {
return DVtableHook::callOriginalFun(w, &QWindow::event, event);
@ -123,8 +131,13 @@ bool MoveWindowHelper::windowEvent(QWindow *w, QEvent *event)
self->m_windowMoving = false;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (is_mouse_move && !event->isAccepted()
&& w->geometry().contains(static_cast<QMouseEvent*>(event)->globalPosition().toPoint())) {
#else
if (is_mouse_move && !event->isAccepted()
&& w->geometry().contains(static_cast<QMouseEvent*>(event)->globalPos())) {
#endif
if (!self->m_windowMoving && self->m_enableSystemMove) {
self->m_windowMoving = true;

View File

@ -250,14 +250,14 @@ QColor DXCBPlatformInterface::darkActiveColor() const
return qvariant_cast<QColor>(value);
}
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
#define GET_COLOR(Role) qvariant_cast<QColor>(getSetting(QByteArrayLiteral(#Role)))
static QColor getSetting(const QByteArray &key)
{
Q_UNUSED(key);
qWarning() << "Not implemented, key:" << key;
return {};
}
#define GET_COLOR(Role) qvariant_cast<QColor>(getSetting(QByteArrayLiteral(#Role)))
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
QColor DXCBPlatformInterface::window() const
{
return GET_COLOR(window);
@ -541,13 +541,14 @@ void DXCBPlatformInterface::setDarkActiveColor(const QColor &activeColor)
d->theme->setSetting("Qt/DarkActiveColor", activeColor);
}
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
#define SET_COLOR(Role) setSetting(QByteArrayLiteral(#Role), Role)
static void setSetting(const QByteArray &key, const QColor &color)
{
Q_UNUSED(key);
Q_UNUSED(color);
qWarning() << "Not implemented, key: " << key << "value: " << color;
}
#define SET_COLOR(Role) setSetting(QByteArrayLiteral(#Role), Role)
#if DTK_VERSION < DTK_VERSION_CHECK(6, 0, 0, 0)
void DXCBPlatformInterface::setWindow(const QColor &window)
{
SET_COLOR(window);

View File

@ -18,7 +18,7 @@ DGUI_BEGIN_NAMESPACE
#define DXCB_PLUGIN_KEY "dxcb"
#define DXCB_PLUGIN_SYMBOLIC_PROPERTY "_d_isDxcb"
#define DEFINE_CONST_CHAR(Name) const char _##Name[] = "_d_" #Name
#define DEFINE_CONST_CHAR(Name) [[maybe_unused]] const char _##Name[] = "_d_" #Name
DEFINE_CONST_CHAR(useDxcb);
DEFINE_CONST_CHAR(redirectContent);

View File

@ -17,7 +17,7 @@ class DFontManagerPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate
public:
DFontManagerPrivate(DFontManager *qq);
int fontPixelSize[DFontManager::NSizeTypes] = {40, 30, 24, 20, 17, 14, 13, 12, 11, 10};
int fontPixelSize[DFontManager::NSizeTypes] = {40, 30, 24, 20, 16, 14, 13, 12, 11, 10, 8};
int baseFontSizeType = DFontManager::T6;
// 字号的差值
int fontPixelSizeDiff = 0;

View File

@ -658,7 +658,11 @@ static const DDciIconEntry::ScalableLayer &findScalableLayer(const DDciIconEntry
const DDciIconEntry::ScalableLayer *maxLayer = nullptr;
const int imagePixelRatio = qCeil(devicePixelRatio);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
for (const auto &i : std::as_const(entry->scalableLayers)) {
#else
for (const auto &i : qAsConst(entry->scalableLayers)) {
#endif
if (!maxLayer || i.imagePixelRatio > maxLayer->imagePixelRatio)
maxLayer = &i;
if (i.imagePixelRatio > imagePixelRatio)
@ -1137,7 +1141,11 @@ int DDciIconImage::currentImageNumber() const
void DDciIconImagePrivate::init()
{
readers.reserve(layers.size());
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
for (const auto &layer : std::as_const(layers)) {
#else
for (const auto &layer : qAsConst(layers)) {
#endif
ReaderData *data = new ReaderData;
Q_ASSERT(data);
auto buffer = new QBuffer();

View File

@ -207,7 +207,11 @@ bool DDciIconImagePlayer::setPalette(const DDciIconPalette &palette)
d->palette = palette;
bool hasPalette = false;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
for (const auto &i : std::as_const(d->images))
#else
for (const auto &i : qAsConst(d->images))
#endif
if (i.hasPalette())
hasPalette = true;
@ -649,7 +653,7 @@ void DDciIconPlayerPrivate::initPlayer()
// Remove the finished animation
animationJobs.removeFirst();
qCDebug(diPlayer, "Number of animations remaining is %i", animationJobs.size());
qCDebug(diPlayer) << "Number of animations remaining is" << animationJobs.size();
if (!animationJobs.isEmpty()) {
_q_playFromQueue();
return;
@ -1075,10 +1079,15 @@ QImage DDciIconPlayer::currentImage() const
return d->image;
}
void DDciIconPlayer::play(DDciIcon::Mode mode)
void DDciIconPlayer::play(DDciIcon::Mode mode, DDciIconImagePlayer::Flags flags)
{
D_D(DDciIconPlayer);
d->play(mode, DDciIconImagePlayer::IgnoreLastImageLoop);
d->play(mode, flags);
}
void DDciIconPlayer::play(DDciIcon::Mode mode)
{
play(mode, DDciIconImagePlayer::IgnoreLastImageLoop);
}
void DDciIconPlayer::stop()

View File

@ -49,7 +49,7 @@ DFontManager::~DFontManager()
\value T4
T4 , 20 px
\value T5
T5 , 17 px
T5 , 16 px
\value T6
T6 , 14 px
\value T7
@ -60,6 +60,8 @@ DFontManager::~DFontManager()
T9 , 11 px
\value T10
T10 , 10 px
\value T11
T11 , 8 px
\omitvalue NSizeTypes
*/

View File

@ -126,12 +126,12 @@ bool DIconTheme::isBuiltinIcon(const QIcon &icon)
bool DIconTheme::isXdgIcon(const QIcon &icon)
{
#ifdef DTK_DISABLE_LIBXDG
return false;
#else
if (icon.isNull())
return false;
#ifdef DTK_DISABLE_LIBXDG
return false;
#else
QIconEngine *engine = const_cast<QIcon &>(icon).data_ptr()->engine;
if (auto proxyEngine = dynamic_cast<DIconProxyEngine *>(engine))
return !proxyEngine->proxyKey().compare("XdgIconProxyEngine");

View File

@ -304,6 +304,8 @@ QString DBuiltinIconEngine::iconName()
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QList<QSize> DBuiltinIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state)
{
Q_UNUSED(mode);
Q_UNUSED(state);
ensureLoaded();
QList<QSize> sizes;

View File

@ -162,9 +162,10 @@ const
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
QList<QSize> DDciIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state)
{
Q_UNUSED(state);
ensureIconTheme();
const auto availableSizes = m_dciIcon.availableSizes(dciTheme(), DDciIcon::Normal);
const auto availableSizes = m_dciIcon.availableSizes(dciTheme(), dciMode(mode));
QList<QSize> sizes;
sizes.reserve(availableSizes.size());

View File

@ -275,9 +275,8 @@ void DIconProxyEngine::ensureEngine()
}
#endif
if (!m_iconEngine && !nonCache[theme].contains(m_iconName)) {
qWarning("create icon [%s] engine failed.[theme:%s] nonCache[theme].size[%d]",
m_iconName.toUtf8().data(),
theme.toUtf8().data(), nonCache[theme].size());
qWarning() << "create icon [" << m_iconName << "] engine failed."
<< "theme:" << theme << "and nonCache's size:" << nonCache.size();
nonCache[theme].insert(m_iconName);
return;
}

View File

@ -1,6 +1,13 @@
set(BIN_NAME ut-DtkGui)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Test)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Gui Widgets DBus Network Test)
if ("${QT_VERSION_MAJOR}" STREQUAL "6")
if (${Qt6Core_VERSION} VERSION_GREATER_EQUAL "6.10.0")
set(QT_NO_PRIVATE_MODULE_WARNING ON)
find_package(Qt6 REQUIRED COMPONENTS CorePrivate GuiPrivate WaylandClientPrivate)
endif()
endif()
find_package(GTest REQUIRED)
file(GLOB test_SRC
@ -44,7 +51,7 @@ target_link_libraries(${BIN_NAME} PRIVATE
m
)
if(NOT DTK_DISABLE_EX_IMAGE_FORMAT AND EX_IMAGE_FORMAT_LIBS_FOUND)
if(NOT DTK_DISABLE_EX_IMAGE_FORMAT AND EX_IMAGE_FORMAT_LIBS_FOUND)
target_link_libraries(${BIN_NAME} PRIVATE
PkgConfig::libraw
FreeImage::FreeImage
@ -52,7 +59,7 @@ target_link_libraries(${BIN_NAME} PRIVATE
endif()
if(NOT DTK_DISABLE_LIBXDG)
target_link_libraries(${BIN_NAME} PRIVATE
target_link_libraries(${BIN_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}XdgIconLoader
)
endif()

View File

@ -127,6 +127,15 @@ void DDynamicMetaObject::init(const QMetaObject *metaObject)
QMetaPropertyBuilder op;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
switch (mp.metaType().id()) {
case QMetaType::QByteArray:
case QMetaType::QString:
case QMetaType::QColor:
case QMetaType::Int:
case QMetaType::Double:
case QMetaType::Bool:
#else
switch (mp.type()) {
case QVariant::Type::ByteArray:
case QVariant::Type::String:
@ -134,6 +143,7 @@ void DDynamicMetaObject::init(const QMetaObject *metaObject)
case QVariant::Type::Int:
case QVariant::Type::Double:
case QVariant::Type::Bool:
#endif
op = ob.addProperty(mp);
break;
default:

View File

@ -16,7 +16,7 @@
DGUI_BEGIN_NAMESPACE
class DPlatformSettings;
class DDynamicMetaObject : public QAbstractDynamicMetaObject
class Q_DECL_HIDDEN DDynamicMetaObject : public QAbstractDynamicMetaObject
{
public:
explicit DDynamicMetaObject(QObject *base, DPlatformSettings *settings, bool global_settings);

View File

@ -15,7 +15,7 @@ QT_END_NAMESPACE
DGUI_BEGIN_NAMESPACE
class DPlatformSettings
class Q_DECL_HIDDEN DPlatformSettings
{
public:
virtual ~DPlatformSettings() {}

View File

@ -5,7 +5,7 @@
#include "dummysettings.h"
#include <QDebug>
class DummySettingsPrivate : public QObject
class Q_DECL_HIDDEN DummySettingsPrivate : public QObject
{
public:
DummySettingsPrivate(DummySettings *q, const QString &domain, QObject *parent = nullptr);

View File

@ -10,7 +10,7 @@
DGUI_USE_NAMESPACE
class DummySettingsPrivate;
class DummySettings : public DPlatformSettings
class Q_DECL_HIDDEN DummySettings : public DPlatformSettings
{
public:
explicit DummySettings(const QString &domain = QString());

View File

@ -13,11 +13,18 @@
#include <qpa/qplatformwindow.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtGlobal>
#include <private/qgenericunixeventdispatcher_p.h>
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
#include <private/qdesktopunixservices_p.h>
#else
#include <private/qgenericunixservices_p.h>
#endif
MinimalIntegration::MinimalIntegration(const QStringList &parameters)
{
Q_UNUSED(parameters);
m_primaryScreen = new MinimalScreen();
m_primaryScreen->mGeometry = QRect(0, 0, 240, 320);
@ -53,7 +60,11 @@ QAbstractEventDispatcher *MinimalIntegration::createEventDispatcher() const
QPlatformServices *MinimalIntegration::services() const
{
if (!m_services)
#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
m_services.reset(new QDesktopUnixServices);
#else
m_services.reset(new QGenericUnixServices);
#endif
return m_services.get();
}

View File

@ -17,7 +17,7 @@ DGUI_USE_NAMESPACE
#define ICONNAME "icon_Layout"
#define ICONSIZE 16
class ut_DBuiltinIconEngine : public testing::Test
class GTEST_API_ ut_DBuiltinIconEngine : public testing::Test
{
protected:
void SetUp() override;

View File

@ -14,7 +14,7 @@
DGUI_USE_NAMESPACE
class ut_DDciIconEngine : public testing::Test
class GTEST_API_ ut_DDciIconEngine : public testing::Test
{
protected:
void SetUp() override;

View File

@ -9,7 +9,7 @@
DGUI_USE_NAMESPACE
class ut_DDciIcon : public DTest
class GTEST_API_ ut_DDciIcon : public DTest
{
public:
ut_DDciIcon()

View File

@ -90,7 +90,7 @@ TEST(ut_DDciIconImage, render)
}
}
class ut_DDciIconPlayer : public DTest
class GTEST_API_ ut_DDciIconPlayer : public DTest
{
protected:
ut_DDciIconPlayer()

View File

@ -21,7 +21,7 @@ TEST(ut_DFontManager, StaticFunction)
ASSERT_TRUE(DFontManager::fontPixelSize(tF) > 0);
}
class TDFontManager : public DTestWithParam<int>
class GTEST_API_ TDFontManager : public DTestWithParam<int>
{
protected:
void SetUp();
@ -76,4 +76,5 @@ TEST_F(TDFontManager, testFontSize)
ASSERT_EQ(manager->t8().pixelSize(), manager->fontPixelSize(DFontManager::T8));
ASSERT_EQ(manager->t9().pixelSize(), manager->fontPixelSize(DFontManager::T9));
ASSERT_EQ(manager->t10().pixelSize(), manager->fontPixelSize(DFontManager::T10));
ASSERT_EQ(manager->t11().pixelSize(), manager->fontPixelSize(DFontManager::T11));
}

View File

@ -14,14 +14,14 @@ DGUI_BEGIN_NAMESPACE
#define WmClass "_d_WmClass"
#define ProcessId "_d_ProcessId"
class TDForeignWindow : public DTest
class GTEST_API_ TDForeignWindow : public DTest
{
protected:
virtual void SetUp()
{
const QVector<quint32> &currentIdList = DWindowManagerHelper::instance()->currentWorkspaceWindowIdList();
foreignWindows.clear();
for (quint32 currentId : qAsConst(currentIdList)) {
for (const auto &currentId : currentIdList) {
foreignWindows.append(DForeignWindow::fromWinId(currentId));
}
}
@ -36,13 +36,13 @@ protected:
TEST_F(TDForeignWindow, wmClass)
{
for (auto foreignWindow : qAsConst(foreignWindows))
for (auto foreignWindow : foreignWindows)
ASSERT_NE(foreignWindow->wmClass(), QString());
}
TEST_F(TDForeignWindow, pid)
{
for (auto foreignWindow : qAsConst(foreignWindows))
for (auto foreignWindow : foreignWindows)
ASSERT_NE(foreignWindow->pid(), 0);
}
@ -51,7 +51,7 @@ TEST_F(TDForeignWindow, event)
QDynamicPropertyChangeEvent wmevent(WmClass);
QDynamicPropertyChangeEvent pidevent(ProcessId);
for (auto foreignWindow : qAsConst(foreignWindows)) {
for (auto foreignWindow : foreignWindows) {
QSignalSpy wmspy(foreignWindow, SIGNAL(wmClassChanged()));
ASSERT_TRUE(foreignWindow->event(&wmevent));
ASSERT_EQ(wmspy.count(), 1);

View File

@ -23,7 +23,7 @@ private:
void virtual_hook(int id, void *data) override;
*/
class ut_DIconProxyEngine : public testing::Test
class GTEST_API_ ut_DIconProxyEngine : public testing::Test
{
protected:
void SetUp() override;

View File

@ -20,8 +20,9 @@ TEST(ut_DIconTheme, builtinIcon)
// icon2 只可能是从外部找到的图标,不会与 icon1 相同
ASSERT_TRUE(icon1.cacheKey() != icon2.cacheKey());
#ifndef DTK_DISABLE_LIBXDG
if (!icon2.isNull())
if (!icon2.isNull()) {
ASSERT_TRUE(DIconTheme::isXdgIcon(icon2));
}
#endif
}
@ -36,8 +37,9 @@ TEST(ut_DIconTheme, cachedTheme)
// icon2 只可能是从外部找到的图标,不会与 icon1 相同
ASSERT_TRUE(icon1.cacheKey() != icon2.cacheKey());
#ifndef DTK_DISABLE_LIBXDG
if (!icon2.isNull())
if (!icon2.isNull()) {
ASSERT_TRUE(DIconTheme::isXdgIcon(icon2));
}
#endif
const QIcon icon1_cached1 = DIconTheme::cached()->findQIcon("edit");

View File

@ -15,7 +15,7 @@
DGUI_USE_NAMESPACE
DCORE_USE_NAMESPACE
class ut_DNativeSettings : public testing::Test
class GTEST_API_ ut_DNativeSettings : public testing::Test
{
public:
static void SetUpTestSuite();

View File

@ -106,13 +106,21 @@ TEST_F(TDPlatformHandle, testFunction)
DPlatformHandle::setDisableWindowOverrideCursor(window, true);
QVariant windowRadius = window->property(WINDOWRADIUS);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (windowRadius.isValid() && windowRadius.canConvert<int>()) {
#else
if (windowRadius.isValid() && windowRadius.canConvert(QVariant::Int)) {
#endif
ASSERT_EQ(pHandle->windowRadius(), windowRadius.toInt());
}
QVariant borderWidth = window->property(BORDERWIDTH);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (borderWidth.isValid() && borderWidth.canConvert<int>()) {
#else
if (borderWidth.isValid() && borderWidth.canConvert(QVariant::Int)) {
#endif
ASSERT_EQ(pHandle->borderWidth(), borderWidth.toInt());
} else {
ASSERT_EQ(pHandle->borderWidth(), 0);
@ -120,7 +128,11 @@ TEST_F(TDPlatformHandle, testFunction)
QVariant borderColor = window->property(BORDRCOLOR);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (borderColor.isValid() && borderColor.canConvert<QColor>()) {
#else
if (borderColor.isValid() && borderColor.canConvert(QVariant::Color)) {
#endif
ASSERT_EQ(pHandle->borderColor(), borderColor.value<QColor>());
} else {
ASSERT_FALSE(pHandle->borderColor().isValid());
@ -128,7 +140,11 @@ TEST_F(TDPlatformHandle, testFunction)
QVariant shadowRadius = window->property(SHADOWRADIUS);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (shadowRadius.isValid() && shadowRadius.canConvert<int>()) {
#else
if (shadowRadius.isValid() && shadowRadius.canConvert(QVariant::Int)) {
#endif
ASSERT_EQ(pHandle->shadowRadius(), shadowRadius.toInt());
} else {
ASSERT_FALSE(pHandle->borderColor().isValid());
@ -136,7 +152,11 @@ TEST_F(TDPlatformHandle, testFunction)
QVariant shadowOffset = window->property(SHADOWOFFSET);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (shadowOffset.isValid() && shadowOffset.canConvert<QPoint>()) {
#else
if (shadowOffset.isValid() && shadowOffset.canConvert(QVariant::Point)) {
#endif
ASSERT_EQ(pHandle->shadowOffset(), shadowOffset.value<QPoint>());
} else {
ASSERT_TRUE(pHandle->shadowOffset().isNull());
@ -144,7 +164,11 @@ TEST_F(TDPlatformHandle, testFunction)
QVariant shadowColor = window->property(SHADOWCOLOR);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (shadowColor.isValid() && shadowColor.canConvert<QColor>()) {
#else
if (shadowColor.isValid() && shadowColor.canConvert(QVariant::Color)) {
#endif
ASSERT_EQ(pHandle->shadowColor(), shadowColor.value<QColor>());
} else {
ASSERT_FALSE(pHandle->shadowColor().isValid());
@ -160,7 +184,11 @@ TEST_F(TDPlatformHandle, testFunction)
QVariant frameMask = window->property(FRAMEMASK);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (frameMask.isValid() && frameMask.canConvert<QRegion>()) {
#else
if (frameMask.isValid() && frameMask.canConvert(QVariant::Region)) {
#endif
ASSERT_EQ(pHandle->frameMask(), frameMask.value<QRegion>());
} else {
ASSERT_TRUE(pHandle->frameMask().isEmpty());
@ -175,35 +203,55 @@ TEST_F(TDPlatformHandle, testFunction)
}
QVariant translucentBackground = window->property(TRANSLUCENTBACKGROUND);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (translucentBackground.isValid() && translucentBackground.canConvert<bool>()) {
#else
if (translucentBackground.isValid() && translucentBackground.canConvert(QVariant::Bool)) {
#endif
ASSERT_EQ(pHandle->translucentBackground(), translucentBackground.toBool());
} else {
ASSERT_FALSE(pHandle->translucentBackground());
}
QVariant enableSystemResize = window->property(ENABLESYSTEMRESIZE);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (enableSystemResize.isValid() && enableSystemResize.canConvert<bool>()) {
#else
if (enableSystemResize.isValid() && enableSystemResize.canConvert(QVariant::Bool)) {
#endif
ASSERT_EQ(pHandle->enableSystemResize(), enableSystemResize.toBool());
} else {
ASSERT_FALSE(pHandle->enableSystemResize());
}
QVariant enableSystemMove = window->property(ENABLESYSTEMMOVE);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (enableSystemMove.isValid() && enableSystemMove.canConvert<bool>()) {
#else
if (enableSystemMove.isValid() && enableSystemMove.canConvert(QVariant::Bool)) {
#endif
ASSERT_EQ(pHandle->enableSystemMove(), enableSystemMove.toBool());
} else {
ASSERT_FALSE(pHandle->enableSystemMove());
}
QVariant enableBlurWindow = window->property(ENABLEBLURWINDOW);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (enableBlurWindow.isValid() && enableBlurWindow.canConvert<bool>()) {
#else
if (enableBlurWindow.isValid() && enableBlurWindow.canConvert(QVariant::Bool)) {
#endif
ASSERT_EQ(pHandle->enableBlurWindow(), enableBlurWindow.toBool());
} else {
ASSERT_FALSE(pHandle->enableBlurWindow());
}
QVariant autoInputMaskByClipPath = window->property(AUTOINPUTMASKBYCLIPPATH);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (autoInputMaskByClipPath.isValid() && autoInputMaskByClipPath.canConvert<bool>()) {
#else
if (autoInputMaskByClipPath.isValid() && autoInputMaskByClipPath.canConvert(QVariant::Bool)) {
#endif
ASSERT_EQ(pHandle->autoInputMaskByClipPath(), autoInputMaskByClipPath.toBool());
} else {
ASSERT_FALSE(pHandle->autoInputMaskByClipPath());

View File

@ -50,12 +50,14 @@ TEST_F(TDWindowMangerHelper, testStaticFunction)
DWindowManagerHelper::setMotifDecorations(w->windowHandle(), DWindowManagerHelper::MotifDecorations(TestDecorations));
DWindowManagerHelper::MotifDecorations mDecos = DWindowManagerHelper::getMotifDecorations(w->windowHandle());
if (wm_helper->windowManagerName() == DWindowManagerHelper::KWinWM)
if (wm_helper->windowManagerName() == DWindowManagerHelper::KWinWM) {
ASSERT_EQ(mDecos, TestDecorations);
}
mDecos = DWindowManagerHelper::setMotifDecorations(w->windowHandle(), DWindowManagerHelper::MotifDecorations(TestAllDecorations), true);
if (wm_helper->windowManagerName() == DWindowManagerHelper::KWinWM)
if (wm_helper->windowManagerName() == DWindowManagerHelper::KWinWM) {
ASSERT_EQ(mDecos, TestAllDecorations);
}
// 没有崩溃则测试成功
enum { TestWindowType = DWindowManagerHelper::DesktopType | DWindowManagerHelper::MenuType };

View File

@ -18,7 +18,7 @@
DGUI_USE_NAMESPACE
class ut_XdgIconProxyEngine : public testing::Test
class GTEST_API_ ut_XdgIconProxyEngine : public testing::Test
{
protected:
void SetUp() override;

View File

@ -3,3 +3,4 @@ add_subdirectory(dci-image-converter)
add_subdirectory(dci-icon-theme)
add_subdirectory(dde-kwin-debug)
add_subdirectory(image-handler)
add_subdirectory(dci-iconfinder)

View File

@ -1,12 +1,15 @@
set(BIN_NAME dci-icon-theme)
set(TARGET_NAME ${BIN_NAME}${DTK_VERSION_MAJOR})
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Concurrent REQUIRED)
add_executable(${TARGET_NAME}
main.cpp
)
target_link_libraries(${TARGET_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Gui
Qt${QT_VERSION_MAJOR}::Concurrent
${LIB_NAME}
)
set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${BIN_NAME})

View File

@ -6,8 +6,6 @@ For example, the tool is used in the following ways:
dci-icon-theme /usr/share/icons/hicolor/256x256/apps -o ~/Desktop/hicolor -O 3=95
dci-icon-theme -m *.png /usr/share/icons/hicolor/256x256/apps -o ~/Desktop/hicolor -O 3=95
dci-icon-theme --fix-dark-theme <input dci files directory> -o <output directory path>
dci-icon-theme --find <icon name>
dci-icon-theme --find <icon name> -t bloom
dci-icon-theme <input file directory> -o <output directory path> -s <csv file> -O <qualities>
@ -34,8 +32,6 @@ Options:
--fix-dark-theme Create symlinks from light theme for
dark theme files.
--find Find dci icon file path
-t, --theme <theme name> Give a theme name to find dci icon file
path
-O, --scale-quality <scale quality> Quility of dci scaled icon image
The value may like <scale size>=<quality
value> e.g. 2=98:3=95

View File

@ -9,16 +9,28 @@
#include <QBuffer>
#include <QDebug>
#include <QtConcurrent/QtConcurrent>
#include <DDciFile>
#include <DIconTheme>
#include <DGuiApplicationHelper>
#include <DPlatformTheme>
#include <stdexcept>
#include <atomic>
DCORE_USE_NAMESPACE
DGUI_USE_NAMESPACE
// Custom exception for DCI processing errors
class DciProcessingError : public std::runtime_error {
public:
explicit DciProcessingError(const QString &message, int code = -1)
: std::runtime_error(message.toStdString()), errorCode(code) {}
int getErrorCode() const { return errorCode; }
private:
int errorCode;
};
#define MAX_SCALE 10
#define INVALIDE_QUALITY -2
#define SCALABLE_SIZE 256
static int quality4Scaled[MAX_SCALE] = {};
static inline void initQuality() {
for (int i = 0; i < MAX_SCALE; ++i)
@ -28,10 +40,45 @@ static inline void initQuality() {
static inline void dciChecker(bool result, std::function<const QString()> cb) {
if (!result) {
qWarning() << "Failed on writing dci file" << cb();
exit(-6);
throw DciProcessingError("Failed on writing dci file", -6);
}
}
// TODO 应该使用xdg图标查找规范解析index.theme来查找尺寸
static int foundSize(const QFileInfo &fileInfo) {
QDir dir = fileInfo.absoluteDir();
// 解析尺寸
auto parseSize = [](const QString &dirName) -> int {
bool ok;
if (int size = dirName.toUInt(&ok); ok) {
return size;
}
if (dirName.contains('x') && dirName.split('x').size() == 2) {
if (int size = dirName.split('x').first().toUInt(&ok); ok) {
return size;
}
}
if (dirName == "scalable") {
return SCALABLE_SIZE;
}
return 0;
};
if (int size = parseSize(dir.dirName()); size > 0) {
return size;
}
// 尝试找上一级目录
if (!dir.cdUp())
return 0;
return parseSize(dir.dirName());
}
static inline QByteArray webpImageData(const QImage &image, int quality) {
QByteArray data;
QBuffer buffer(&data);
@ -41,28 +88,18 @@ static inline QByteArray webpImageData(const QImage &image, int quality) {
return data;
}
static bool writeScaledImage(DDciFile &dci, const QString &imageFile, const QString &targetDir, int scale/* = 2*/)
static bool writeScaledImage(DDciFile &dci, const QImage &image, const QString &targetDir, const int baseSize, int scale/* = 2*/)
{
QString sizeDir = targetDir.mid(1, targetDir.indexOf("/", 1) - 1);
bool ok = false;
int baseSize = sizeDir.toInt(&ok);
if (!ok)
baseSize = 256;
int size = scale * baseSize;
QImageReader image(imageFile);
if (!image.canRead()) {
qWarning() << "Ignore the null image file:" << imageFile;
return false;
}
if (image.supportsOption(QImageIOHandler::ScaledSize)) {
image.setScaledSize(QSize(size, size));
QImage img;
if (image.width() == size) {
img = image;
} else {
img = image.scaledToWidth(size, Qt::SmoothTransformation);
}
dciChecker(dci.mkdir(targetDir + QString("/%1").arg(scale)), [&]{return dci.lastErrorString();});
const QImage &img = image.read().scaledToWidth(size, Qt::SmoothTransformation);
int quality = quality4Scaled[scale - 1];
int quality = quality4Scaled[scale - 1];
const QByteArray &data = webpImageData(img, quality);
dciChecker(dci.writeFile(targetDir + QString("/%1/1.webp").arg(scale), data), [&]{return dci.lastErrorString();});
@ -71,13 +108,28 @@ static bool writeScaledImage(DDciFile &dci, const QString &imageFile, const QStr
static bool writeImage(DDciFile &dci, const QString &imageFile, const QString &targetDir)
{
QString sizeDir = targetDir.mid(1, targetDir.indexOf("/", 1) - 1);
bool ok = false;
int baseSize = sizeDir.toInt(&ok);
if (!ok)
baseSize = 256;
QImageReader reader(imageFile);
if (!reader.canRead()) {
qWarning() << "Ignore the null image file:" << imageFile;
return false;
}
for (int i = 0; i < MAX_SCALE; ++i) {
if (quality4Scaled[i] == INVALIDE_QUALITY)
continue;
int scale = i + 1;
if (!writeScaledImage(dci, imageFile, targetDir, i + 1))
reader.setScaledSize(QSize(baseSize * scale, baseSize * scale));
auto image = reader.read();
if (!writeScaledImage(dci, image, targetDir, baseSize, scale))
return false;
}
}
return true;
}
@ -131,7 +183,7 @@ QMultiHash<QString, QString> parseIconFileSymlinkMap(const QString &csvFile) {
QFile file(csvFile);
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Failed on open symlink map file:" << csvFile;
exit(-7);
throw DciProcessingError("Failed on open symlink map file", -7);
}
QMultiHash<QString, QString> map;
@ -219,8 +271,6 @@ int main(int argc, char *argv[])
,
"csv file");
QCommandLineOption fixDarkTheme("fix-dark-theme", "Create symlinks from light theme for dark theme files.");
QCommandLineOption iconFinder("find", "Find dci icon file path");
QCommandLineOption themeOpt({"t","theme"}, "Give a theme name to find dci icon file path", "theme name");
QCommandLineOption scaleQuality({"O","scale-quality"}, "Quility of dci scaled icon image\n"
"The value may like <scale size>=<quality value> e.g. 2=98:3=95\n"
"The quality factor must be in the range 0 to 100 or -1.\n"
@ -229,8 +279,12 @@ int main(int argc, char *argv[])
"The higher the quality, the larger the dci icon file size", "scale quality");
QGuiApplication a(argc, argv);
a.setApplicationName("dci-icon-theme");
a.setApplicationVersion("0.0.6");
a.setApplicationVersion(QString("%1.%2.%3")
.arg(DTK_VERSION_MAJOR)
.arg(DTK_VERSION_MINOR)
.arg(DTK_VERSION_PATCH));
QCommandLineParser cp;
cp.setApplicationDescription("dci-icon-theme tool is a command tool that generate dci icons from common icons.\n"
@ -238,12 +292,10 @@ int main(int argc, char *argv[])
"\t dci-icon-theme /usr/share/icons/hicolor/256x256/apps -o ~/Desktop/hicolor -O 3=95\n"
"\t dci-icon-theme -m *.png /usr/share/icons/hicolor/256x256/apps -o ~/Desktop/hicolor -O 3=95\n"
"\t dci-icon-theme --fix-dark-theme <input dci files directory> -o <output directory path> \n"
"\t dci-icon-theme --find <icon name>\n"
"\t dci-icon-theme --find <icon name> -t bloom\n"
"\t dci-icon-theme <input file directory> -o <output directory path> -s <csv file> -O <qualities>\n"""
);
cp.addOptions({fileFilter, outputDirectory, symlinkMap, fixDarkTheme, iconFinder, themeOpt, scaleQuality});
cp.addOptions({fileFilter, outputDirectory, symlinkMap, fixDarkTheme, scaleQuality});
cp.addPositionalArgument("source", "Search the given directory and it's subdirectories, "
"get the files conform to rules of --match.",
"~/dci-png-icons");
@ -254,27 +306,11 @@ int main(int argc, char *argv[])
if (a.arguments().size() == 1)
cp.showHelp(-1);
bool isIconFinder = cp.isSet(iconFinder);
if (cp.positionalArguments().isEmpty()) {
qWarning() << "Not give a" << (isIconFinder ? "icon name." : "source directory.");
qWarning() << "Not give a source directory.";
cp.showHelp(-2);
}
QString iconThemeName;
if (cp.isSet(themeOpt)) {
iconThemeName = cp.value(themeOpt);
} else {
iconThemeName = DGuiApplicationHelper::instance()->applicationTheme()->iconThemeName();
}
if (isIconFinder) {
QString iconName = cp.positionalArguments().value(0);
QString iconPath = DIconTheme::findDciIconFile(iconName, iconThemeName);
qInfo() << iconName << "[" << iconThemeName << "]:" << iconPath;
return 0;
}
if (!cp.isSet(outputDirectory)) {
qWarning() << "Not give -o argument";
cp.showHelp(-4);
@ -295,10 +331,6 @@ int main(int argc, char *argv[])
#endif
QStringList qualityList = cp.value(scaleQuality).split(":", behavior);
#ifdef QT_DEBUG
surfix = cp.value(scaleQuality).prepend("-");
#endif
for (const QString &kv : qualityList) {
auto sq = kv.split("=");
if (sq.size() != 2) {
@ -331,12 +363,17 @@ int main(int argc, char *argv[])
QMultiHash<QString, QString> symlinksMap;
if (cp.isSet(symlinkMap)) {
symlinksMap = parseIconFileSymlinkMap(cp.value(symlinkMap));
try {
symlinksMap = parseIconFileSymlinkMap(cp.value(symlinkMap));
} catch (const DciProcessingError &e) {
qWarning() << "Error parsing symlink map:" << e.what();
return e.getErrorCode();
}
}
const QStringList nameFilter = cp.isSet(fileFilter) ? cp.values(fileFilter) : QStringList();
const auto sourceDirectory = cp.positionalArguments();
for (const auto &sd : qAsConst(sourceDirectory)) {
for (const auto &sd : sourceDirectory) {
QDir sourceDir(sd);
if (!sourceDir.exists()) {
qWarning() << "Ignore the non-exists directory:" << sourceDir;
@ -369,6 +406,8 @@ int main(int argc, char *argv[])
}
}
// Collect all files first, grouped by icon name to avoid concurrent access to same DCI file
QMap<QString, QList<QFileInfo>> iconGroups;
QDirIterator di(sourceDir.absolutePath(), nameFilter,
QDir::NoDotAndDotDot | QDir::Files,
QDirIterator::Subdirectories);
@ -380,7 +419,12 @@ int main(int argc, char *argv[])
continue;
if (cp.isSet(fixDarkTheme)) {
doFixDarkTheme(file, outputDir, symlinksMap);
try {
doFixDarkTheme(file, outputDir, symlinksMap);
} catch (const DciProcessingError &e) {
qWarning() << "Error fixing dark theme for file" << file.absoluteFilePath() << ":" << e.what();
return e.getErrorCode();
}
continue;
}
@ -389,46 +433,86 @@ int main(int argc, char *argv[])
continue;
}
QString dirName = file.absoluteDir().dirName();
bool isNum = false;
dirName.toInt(&isNum);
dirName.prepend("/");
iconGroups[file.completeBaseName()].append(file);
}
// Process with proper exception handling
std::atomic<bool> hasError{false};
int errorCode = 0;
// Process icon groups concurrently (each group shares same DCI file)
QList<QString> iconNames = iconGroups.keys();
QtConcurrent::blockingMap(iconNames, [&](const QString &iconName) {
if (hasError.load()) return; // Skip if already has error
try {
const QList<QFileInfo> &files = iconGroups[iconName];
const QString dciFilePath(outputDir.absoluteFilePath(iconName) + surfix + ".dci");
QScopedPointer<DDciFile> dciFile;
for (const QFileInfo &file : files) {
QString dirName = file.absoluteDir().dirName();
uint iconSize = foundSize(file);
dirName = iconSize > 0 ? QString("/%1").arg(iconSize) : dirName.prepend("/");
QScopedPointer<DDciFile> dciFile;
const QString dciFilePath(outputDir.absoluteFilePath(file.completeBaseName()) + surfix + ".dci");
if (QFileInfo::exists(dciFilePath)) {
dciFile.reset(new DDciFile(dciFilePath));
if (dciFile->isValid() && dciFile->exists(dirName)) {
qWarning() << "Skip exists dci file:" << dciFilePath << dirName << dciFile->list(dirName);
continue;
// Initialize DCI file once per icon group
if (dciFile.isNull()) {
if (QFileInfo::exists(dciFilePath)) {
dciFile.reset(new DDciFile(dciFilePath));
}
if (dciFile.isNull() || !dciFile->isValid()) {
dciFile.reset(new DDciFile);
}
}
if (dciFile->exists(dirName)) {
qWarning() << "Skip exists dci file:" << dciFilePath << dirName << dciFile->list(dirName);
continue;
}
qInfo() << "Writing to dci file:" << file.absoluteFilePath() << "==>" << dciFilePath;
QString sizeDir = iconSize > 0 ? dirName : "/256"; // "/256" as default
QString normalLight = sizeDir + "/normal.light"; // "/256/normal.light"
QString normalDark = sizeDir + "/normal.dark"; // "/256/normal.dark"
if (dciFile->exists(sizeDir)) {
qWarning() << "Skip exists dci file:" << dciFilePath << sizeDir << dciFile->list(sizeDir);
continue;
}
dciChecker(dciFile->mkdir(sizeDir), [&]{return dciFile->lastErrorString();});
dciChecker(dciFile->mkdir(normalLight), [&]{return dciFile->lastErrorString();});
if (!writeImage(*dciFile, file.filePath(), normalLight))
continue;
dciChecker(dciFile->mkdir(normalDark), [&]{return dciFile->lastErrorString();});
QFileInfo darkIcon(file.dir().absoluteFilePath("dark/" + file.fileName()));
if (darkIcon.exists()) {
writeImage(*dciFile, darkIcon.filePath(), normalDark);
} else {
dciChecker(recursionLink(*dciFile, normalLight, normalDark), [&]{return dciFile->lastErrorString();});
}
}
// Write DCI file once per icon group
if (!dciFile.isNull()) {
dciChecker(dciFile->writeToFile(dciFilePath), [&]{return dciFile->lastErrorString();});
// Create symlinks for all files in this group
for (const QFileInfo &file : files) {
makeLink(file, outputDir, dciFilePath, symlinksMap);
}
}
} catch (const DciProcessingError &e) {
qWarning() << "Error processing icon group" << iconName << ":" << e.what();
hasError.store(true);
errorCode = e.getErrorCode();
}
});
qInfo() << "Writing to dci file:" << file.absoluteFilePath() << "==>" << dciFilePath;
if (dciFile.isNull() || !dciFile->isValid())
dciFile.reset(new DDciFile);
QString sizeDir = isNum ? dirName : "/256"; // "/256"
QString normalLight = sizeDir + "/normal.light"; // "/256/normal.light"
QString normalDark = sizeDir + "/normal.dark"; // "/256/normal.dark"
dciChecker(dciFile->mkdir(sizeDir), [&]{return dciFile->lastErrorString();});
dciChecker(dciFile->mkdir(normalLight), [&]{return dciFile->lastErrorString();});
if (!writeImage(*dciFile, file.filePath(), normalLight))
continue;
dciChecker(dciFile->mkdir(normalDark), [&]{return dciFile->lastErrorString();});
QFileInfo darkIcon(file.dir().absoluteFilePath("dark/" + file.fileName()));
if (darkIcon.exists()) {
writeImage(*dciFile, darkIcon.filePath(), normalDark);
} else {
dciChecker(recursionLink(*dciFile, normalLight, normalDark), [&]{return dciFile->lastErrorString();});
}
dciChecker(dciFile->writeToFile(dciFilePath), [&]{return dciFile->lastErrorString();});
makeLink(file, outputDir, dciFilePath, symlinksMap);
if (hasError.load()) {
qWarning() << "Encountered errors during DCI file writing" << errorCode;
continue;
}
}

View File

@ -0,0 +1,14 @@
set(BIN_NAME dci-iconfinder)
set(TARGET_NAME ${BIN_NAME}${DTK_VERSION_MAJOR})
add_executable(${TARGET_NAME}
main.cpp
)
target_link_libraries(${TARGET_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Gui
${LIB_NAME}
)
set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME ${BIN_NAME})
install(TARGETS ${TARGET_NAME} DESTINATION "${TOOL_INSTALL_DIR}")

View File

@ -0,0 +1,61 @@
// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include <QGuiApplication>
#include <QCommandLineParser>
#include <QDebug>
#include <DIconTheme>
DGUI_USE_NAMESPACE
int main(int argc, char *argv[])
{
QGuiApplication a(argc, argv);
a.setApplicationName("dci-iconfinder");
a.setApplicationVersion(QString("%1.%2.%3")
.arg(DTK_VERSION_MAJOR)
.arg(DTK_VERSION_MINOR)
.arg(DTK_VERSION_PATCH));
QCommandLineParser cp;
cp.setApplicationDescription(
"dci-iconfinder tool is a command tool that find dci icons in the icon "
"theme.\n"
"For example, the tool is used in the following ways: \n"
"\t dci-iconfinder <icon name>\n"
"\t dci-iconfinder <icon name> -t bloom\n");
QCommandLineOption themeOpt({"t", "theme"},
"Give a theme name to find dci icon file path",
"theme name");
cp.addOptions({themeOpt});
cp.addPositionalArgument("iconnames", "The icon names to search for",
"iconnames");
cp.addHelpOption();
cp.addVersionOption();
cp.process(a);
if (cp.positionalArguments().isEmpty()) {
qWarning() << "Not give icon name.";
cp.showHelp(-1);
}
QString iconThemeName;
if (cp.isSet(themeOpt)) {
iconThemeName = cp.value(themeOpt);
} else {
iconThemeName = QIcon::themeName();
}
const auto icons = cp.positionalArguments();
for (const QString &iconName : icons) {
QString iconPath = DIconTheme::findDciIconFile(iconName, iconThemeName);
if (!iconPath.isEmpty())
qInfo().noquote() << iconName << "[" << iconThemeName << "]:" << iconPath;
}
return 0;
}