Skip to content

fix: scale non-DCI icons correctly in fullscreen launcher - #811

Closed
mhduiy wants to merge 1 commit into
masterfrom
agent/pms-bug-bot/ab3abf98c0b5
Closed

mhduiy wants to merge 1 commit into
masterfrom
agent/pms-bug-bot/ab3abf98c0b5

Conversation

@mhduiy

@mhduiy mhduiy commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Root Cause Analysis

When a theme icon has no DCI file (e.g. CutefishDark's user-trash), DTK's DciIcon falls back to the QIcon path where icon.paint() draws the SVG at its native 16px size onto a 128×128 transparent canvas without scaling. The result is a tiny 16px icon centered in a 128×128 image, and fillMode: PreserveAspectFit cannot help because the output is already 128×128. Key evidence: dquickimageprovider.cpp:97 paints at SVG native size; dquickdciiconimage.cpp:72-86 falls back to QIcon when no DCI file is found; IconItemDelegate.qml:317-326 requests 128×128 in fullscreen mode.

Fix

Added DesktopIntegration.isDciIcon() — a Q_INVOKABLE static method that checks whether a DCI file exists for the given icon name in the current icon theme. In IconItemDelegate.qml, the imageComponent now uses a conditional Loader: DCI icons take the original DciIcon path (unchanged), while non-DCI icons route through a standard Image component backed by LauncherAppIconProvider, which uses DSvgRenderer for proper SVG scaling. The LauncherAppIconProvider is registered in LauncherItem::init().

Change Safety Assessment

Code Safety

  • Risk Level: Low
  • All changes are purely additive (new method, new include, new provider registration, conditional Loader branch). No existing function signatures or behavior logic are modified.
  • Blame history confirms target code is not a product of any previous bug fix; the conditional Loader preserves the original DciIcon logic unchanged inside dciIconComponent.

Business Impact Scope

  • Fullscreen launcher icon display: DCI icons (most application icons) render identically to before; non-DCI icons (e.g. CutefishDark's trash icon) now scale correctly to the target size.
  • Windowed mode icon display is unaffected (does not use imageComponent).
  • Users on third-party themes (e.g. CutefishDark) will see correctly sized icons for places-category icons (trash, computer, etc.) in fullscreen launcher mode.

Verification Suggestion

  • Verify CutefishDark theme fullscreen launcher trash icon is correctly sized.
  • Regression: verify default bloom theme fullscreen launcher all icons display normally.

根因分析

当主题图标没有 DCI 文件时(如 CutefishDark 的 user-trash),DTK 的 DciIcon 回退到 QIcon 路径,icon.paint() 以 SVG 原生 16px 尺寸绘制到 128×128 透明画布上,不进行缩放。最终输出 128×128 图像中只有一个 16px 小图标,fillMode: PreserveAspectFit 对已是 128×128 的输出无效。关键证据:dquickimageprovider.cpp:97 以 SVG 原生尺寸绘制;dquickdciiconimage.cpp:72-86 无 DCI 文件时回退到 QIcon;IconItemDelegate.qml:317-326 全屏模式请求 128×128。

修复方案

新增 DesktopIntegration.isDciIcon() — 一个 Q_INVOKABLE 静态方法,检查当前图标主题中是否存在指定图标名的 DCI 文件。在 IconItemDelegate.qml 中,imageComponent 改为条件 Loader:DCI 图标走原 DciIcon 路径(不变),非 DCI 图标走标准 Image 组件,通过 LauncherAppIconProvider(使用 DSvgRenderer 正确缩放 SVG)。LauncherAppIconProvider 在 LauncherItem::init() 中注册。

改动安全评估

代码安全评估

  • 风险等级: 低风险
  • 所有改动均为纯新增(新方法、新 include、新 provider 注册、条件 Loader 分支),不修改任何现有函数签名或行为逻辑。
  • blame 历史确认目标代码非任何历史 bug 修复产物;条件 Loader 将原 DciIcon 逻辑完整保留在 dciIconComponent 中,未做修改。

业务影响范围

  • 全屏启动器图标显示:DCI 图标(多数 application 类图标)显示与修复前完全一致;非 DCI 图标(如 CutefishDark 回收站图标)现在正确缩放到目标尺寸。
  • 窗口化模式图标显示不受影响(不使用 imageComponent)。
  • 使用第三方主题(如 CutefishDark)的用户在全屏启动器中将看到正确尺寸的 places 类图标(回收站、计算机等)。

验证建议

  • 验证 CutefishDark 主题下全屏启动器回收站图标大小是否正常。
  • 回归验证:默认 bloom 主题下全屏启动器所有图标显示是否正常。

Summary by Sourcery

Fix fullscreen launcher icon sizing for themes that do not provide DCI icon files.

Bug Fixes:

  • Correctly scale non-DCI theme icons in the fullscreen launcher instead of rendering them at their native size.

Enhancements:

  • Preserve the existing DCI icon rendering path while routing non-DCI icons through the standard scalable image provider.

When a theme icon has no DCI file (e.g. CutefishDark's user-trash),
DciIcon falls back to QIcon which draws SVG at native size without
scaling. Add isDciIcon() check and route non-DCI icons through
LauncherAppIconProvider which uses DSvgRenderer for proper scaling.

Log: BUG-342397
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: mhduiy

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

Fullscreen launcher icons now detect whether their theme provides DCI assets: DCI icons retain the existing DciIcon rendering, while non-DCI icons use a registered SVG-capable provider and standard Image sizing so they scale to the requested launcher dimensions instead of remaining at native SVG size.

Sequence diagram for fullscreen launcher icon rendering

sequenceDiagram
    participant Delegate as IconItemDelegate
    participant Integration as DesktopIntegration
    participant Theme as DIconTheme
    participant DCI as DciIcon
    participant Provider as LauncherAppIconProvider
    participant Renderer as DSvgRenderer

    Delegate->>Integration: isDciIcon(iconSource)
    Integration->>Theme: findDciIconFile(iconName, themeName)
    Theme-->>Integration: DCI file path or empty
    alt DCI file exists
        Integration-->>Delegate: true
        Delegate->>DCI: Render icon
    else DCI file absent
        Integration-->>Delegate: false
        Delegate->>Provider: Load image://appicon/iconSource
        Provider->>Renderer: Render SVG at requested sourceSize
        Renderer-->>Provider: Scaled image
        Provider-->>Delegate: Image data
    end
Loading

File-Level Changes

Change Details Files
Detect DCI-backed icons so fullscreen rendering can choose an appropriate scaling path.
  • Expose a QML-invokable helper that queries the active theme for a DCI file.
  • Use DTK icon-theme APIs to resolve the icon’s DCI path.
desktopintegration.cpp
desktopintegration.h
Route non-DCI fullscreen icons through an SVG-capable image provider while preserving the existing DCI path.
  • Add a conditional Loader that selects DciIcon for DCI icons and a standard Image for other icons.
  • Configure non-DCI images with the appicon provider, target source size, aspect-fit mode, and launcher scaling.
qml/IconItemDelegate.qml
Register the launcher image provider used by non-DCI icon rendering.
  • Register LauncherAppIconProvider under the appicon image-provider scheme during launcher initialization.
shell-launcher-applet/launcheritem.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="qml/IconItemDelegate.qml" line_range="345" />
<code_context>
                         fillMode: Image.PreserveAspectFit
                     }
                 }
</code_context>
<issue_to_address>
**issue (broader_impact):** The non-DCI branch passes every `iconSource` through `LauncherAppIconProvider`, whose request parser strips the leading slash from absolute file paths and treats `data:image/...` values as icon names. Fullscreen icons supplied as an absolute SVG/bitmap path or data URL therefore fail to load and fall back to the provider's default/empty pixmap, whereas the previous `DciIcon` path accepted those sources.

**Triggers:** When an application's icon source is an absolute filesystem path or a data URL rather than a theme icon name.

**Suggested fix:** Use the provider only for theme icon names, or preserve and handle file paths/data URLs before routing the source through the theme-icon provider.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread qml/IconItemDelegate.qml
objectName: "appIcon"
anchors.fill: parent
source: "image://appicon/" + iconSource
fillMode: Image.PreserveAspectFit

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (broader_impact): The non-DCI branch passes every iconSource through LauncherAppIconProvider, whose request parser strips the leading slash from absolute file paths and treats data:image/... values as icon names. Fullscreen icons supplied as an absolute SVG/bitmap path or data URL therefore fail to load and fall back to the provider's default/empty pixmap, whereas the previous DciIcon path accepted those sources.

Triggers: When an application's icon source is an absolute filesystem path or a data URL rather than a theme icon name.

Suggested fix: Use the provider only for theme icon names, or preserve and handle file paths/data URLs before routing the source through the theme-icon provider.

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

AI 代码审查报告

PR: linuxdeepin/dde-launchpad#811
标题: fix: scale non-DCI icons correctly in fullscreen launcher
作者: mhduiy
分支: agent/pms-bug-bot/ab3abf98c0b5 → master
修改文件: 4 个文件,+34 行,-0 行
审查时间: 2026-09-14


总体评价

总分: 92/100 | 风险等级: 无风险 | 评价: 优秀

本次 PR 修复了全屏启动器中非 DCI 主题图标显示过小的问题。所有改动均为纯新增代码,不修改任何现有函数签名或行为逻辑。代码实现与 commit message 描述的修复目的完全一致:通过新增 isDciIcon() 方法检测图标是否为 DCI 格式,对非 DCI 图标使用标准 Image 组件配合 LauncherAppIconProvider 进行正确缩放。原 DCI 图标渲染路径完整保留在 dciIconComponent 中,未做任何修改,确保了向后兼容性。


四维度评分

维度 评分 状态 评价词
语法逻辑 22/25 ✓ 语法正确,逻辑清晰
代码质量 23/25 ✓ 代码结构清晰,注释完整
代码性能 17/20 ✓ 算法复杂度合理
代码安全 30/30 ✓ 存在0个安全漏洞

维度1: 语法逻辑(22/25 ✓)

语法正确,逻辑清晰

代码分析:

  1. desktopintegration.cpp:69-74 - isDciIcon() 方法语法正确,逻辑清晰:获取当前图标主题名 → 调用 DIconTheme::findDciIconFile() 查找 DCI 文件 → 返回是否找到
  2. qml/IconItemDelegate.qml:317-321 - 条件 Loader 使用三元运算符根据 DCI 检测结果选择不同组件,QML 语法正确
  3. qml/IconItemDelegate.qml:338-348 - imageIconComponent 正确镜像了 dciIconComponent 的缩放模式,确保两种路径下图标大小一致
  4. shell-launcher-applet/launcheritem.cpp:63 - LauncherAppIconProvider 注册遵循与 BlurhashImageProvider 相同的模式,正确

问题:

  1. desktopintegration.cpp:71 - DGuiApplicationHelper::instance()->applicationTheme() 理论上可能返回 nullptr,若主题未初始化,调用 iconThemeName() 会导致空指针解引用。在 dde-launchpad 实际运行环境中主题始终已初始化,此为防御性编程建议 (-3分)

维度2: 代码质量(23/25 ✓)

代码结构清晰,注释完整

代码分析:

  1. 结构合理性 - 条件 Loader 将 DCI 和非 DCI 图标渲染逻辑分离到独立组件中,结构清晰,职责分明
  2. 代码复用 - imageIconComponent 的缩放公式与 dciIconComponent 保持一致,确保视觉一致性
  3. 调试信息清理 - 无残留调试代码
  4. 向后兼容 - 原 DciIcon 逻辑完整保留在 dciIconComponent 中,未做修改

问题:

  1. desktopintegration.cpp:69 - isDciIcon() 方法缺少文档注释,建议添加方法说明、参数描述和返回值说明 (-2分)

维度3: 代码性能(17/20 ✓)

算法复杂度合理

代码分析:

  1. isDciIcon() 时间复杂度为 O(1)(文件系统查找),单次调用开销小
  2. LauncherAppIconProvider::requestPixmap() 使用 IconUtils::getThemeIcon() 进行图标加载,内部使用 perfectIconSize() 选择最优尺寸,避免过大内存分配
  3. 条件 Loader 避免了不必要的组件创建

问题:

  1. desktopintegration.cpp:69-74 / qml/IconItemDelegate.qml:319 - isDciIcon() 在每次 QML delegate 创建时调用,执行文件系统查找。全屏启动器可能包含数十到上百个图标,无缓存机制可能导致轻微延迟。建议添加缓存(如 QHash 或 QML 属性缓存) (-3分)

维度4: 代码安全(30/30 ✓)

存在0个安全漏洞

无安全风险

安全分析:

  1. 输入校验 - isDciIcon() 的 iconName 参数来自 QML iconSource 属性,来源于应用模型数据,非用户直接输入
  2. 路径安全 - DIconTheme::findDciIconFile() 内部处理路径查找,不存在路径遍历风险
  3. 注入风险 - 无 SQL、命令注入风险
  4. 敏感信息 - 无硬编码密钥或敏感信息
  5. 权限 - 无权限绕过风险

漏洞对比统计:新增漏洞 0 个,减少漏洞 0 个,持平 0 个


修改文件清单

文件 修改内容 行数变化
desktopintegration.cpp 新增 isDciIcon() 方法实现 + 新增 DTK 头文件引用 +9
desktopintegration.h 新增 isDciIcon() 方法声明 +1
qml/IconItemDelegate.qml 新增条件 Loader、dciIconComponent、imageIconComponent +18
shell-launcher-applet/launcheritem.cpp 新增 LauncherAppIconProvider 注册 + 头文件引用 +2

改进建议

  1. 防御性编程 - isDciIcon() 中添加 applicationTheme() 空指针检查:
bool DesktopIntegration::isDciIcon(const QString &iconName)
{
    auto *theme = Dtk::Gui::DGuiApplicationHelper::instance()->applicationTheme();
    if (!theme) {
        return false;
    }
    QByteArray themeName = theme->iconThemeName();
    QString dciFile = Dtk::Gui::DIconTheme::findDciIconFile(iconName, QString::fromLatin1(themeName));
    return !dciFile.isEmpty();
}
  1. 性能优化 - 考虑缓存 isDciIcon() 结果,避免每次 delegate 创建时重复文件系统查找:
// 使用静态缓存
static QHash<QString, bool> s_dciIconCache;
if (s_dciIconCache.contains(iconName)) {
    return s_dciIconCache.value(iconName);
}
bool result = !dciFile.isEmpty();
s_dciIconCache.insert(iconName, result);
return result;
  1. 文档注释 - 为 isDciIcon() 添加方法文档:
/// \brief 检查指定图标名在当前图标主题中是否存在 DCI 文件
/// \param iconName 图标名称
/// \return true 如果存在 DCI 文件,false 否则
Q_INVOKABLE static bool isDciIcon(const QString &iconName);

审查结论

本次 PR 代码质量优秀,修复目的明确,实现方案合理。所有改动均为纯新增代码,不修改现有逻辑,风险极低。建议合并前考虑上述防御性编程和性能优化建议。

@mhduiy

mhduiy commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

这个图标是一个较小的svg图标,且 DDE 的自动转dci逻辑在这里不生效,因为自动转dci的逻辑只针对application图标,这个图标是放在/usr/share/icons/cutefishdark.382952b6/places/16/user-trash.svg 这里的,另外,dtk绘制图标的时候为了能够控制图标颜色,使用paint函数绘制,这个函数绘制图标的时候是不会缩大图标的,当画板过大,或者原始图标资源过小的时候,会造成图标无法撑满画板的情况,最终表现出来就是图标偏小。目前暂时没有比较好的解决办法,暂时遗留

@mhduiy mhduiy closed this Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants