From ffc173229973ffb768b52ad2e37c0cac4fa6397f Mon Sep 17 00:00:00 2001 From: Ivy233 Date: Mon, 2 Mar 2026 22:29:53 +0800 Subject: [PATCH 1/2] fix: align sidebar layout values to physical pixel grid at fractional DPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add Helper.pixelAligned() to snap logical pixel values so that value * DPR always lands on an integer physical pixel 2. Apply pixelAligned() to SideBar spacing, topPadding, bottomPadding and bottomMargin 3. Apply pixelAligned() to WindowedFrame sidebar topMargin, leftMargin and separator leftMargin 4. Apply pixelAligned() to BottomBar padding 5. At fractional DPR (1.25, 1.75), layout values like 10 produce fractional physical coordinates (e.g. 10 * 1.25 = 12.5), causing GPU bilinear interpolation blur on small icon textures during Scene Graph compositing. Snapping to integer physical pixels eliminates this. Log: Fixed sidebar icons appearing blurry at 125%/175% display scaling Influence: 1. Test sidebar icons (Computer, Pictures, Documents, Desktop, Control Center) at 125% and 175% scaling, icons should be sharp 2. Verify icons remain sharp at 100%, 150%, 200% scaling 3. Check bottom bar power and fullscreen button alignment 4. Verify sidebar layout spacing looks visually consistent across different DPR values fix: 修复非整数缩放比例下侧边栏图标模糊问题 1. 在 Helper 中新增 pixelAligned() 函数,将逻辑像素值对齐到整数物理像素 2. 对 SideBar 的 spacing、topPadding、bottomPadding、bottomMargin 应用 pixelAligned() 3. 对 WindowedFrame 中 sidebar 的 topMargin、leftMargin 及分隔线的 leftMargin 应用 pixelAligned() 4. 对 BottomBar 的 padding 应用 pixelAligned() 5. 在非整数 DPR(1.25、1.75)下,布局值如 10 会产生小数物理坐标 (如 10 × 1.25 = 12.5),导致 Scene Graph 合成时 GPU 双线性插值使小图标 纹理模糊。对齐到整数物理像素后即可消除此问题。 Log: 修复了 125%/175% 显示缩放下侧边栏图标模糊的问题 Influence: 1. 在 125% 和 175% 缩放下测试侧边栏图标(计算机、图片、文档、桌面、 控制中心),图标应清晰 2. 验证 100%、150%、200% 缩放下图标仍然清晰 3. 检查底栏电源按钮和全屏按钮的对齐情况 4. 验证不同 DPR 下侧边栏布局间距视觉一致性 PMS: BUG-288427 --- qml/Helper.qml | 8 +++++++- qml/windowed/BottomBar.qml | 5 +++-- qml/windowed/SideBar.qml | 10 +++++----- qml/windowed/WindowedFrame.qml | 6 +++--- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/qml/Helper.qml b/qml/Helper.qml index 52bc45a5..2fd78824 100644 --- a/qml/Helper.qml +++ b/qml/Helper.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -40,6 +40,12 @@ QtObject { } } + // Snap a logical pixel value so that value * DPR lands on an integer + // physical pixel, avoiding sub-pixel texture sampling blur in Scene Graph. + function pixelAligned(value, dpr) { + return Math.round(value * dpr) / dpr + } + function generateDragMimeData(desktopId, dockOnly = false) { // In some cases an app is not allowed to be pinned onto dock via drag-n-drop; // We only insert the MIME data for dde-dock in those allowed cases. diff --git a/qml/windowed/BottomBar.qml b/qml/windowed/BottomBar.qml index 77cf0f07..60f744c6 100644 --- a/qml/windowed/BottomBar.qml +++ b/qml/windowed/BottomBar.qml @@ -1,10 +1,11 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later import QtQuick 2.15 import QtQuick.Layouts 1.15 import QtQuick.Controls 2.15 +import QtQuick.Window 2.15 import org.deepin.dtk 1.0 import org.deepin.ds 1.0 import org.deepin.dtk.style 1.0 as DStyle @@ -20,7 +21,7 @@ Control { property Item nextKeyTabTarget property alias searchEdit: searchEdit - padding: 10 + padding: Helper.pixelAligned(10, Screen.devicePixelRatio) contentItem: RowLayout { ToolButton { diff --git a/qml/windowed/SideBar.qml b/qml/windowed/SideBar.qml index 215a0cc5..d2c91f74 100644 --- a/qml/windowed/SideBar.qml +++ b/qml/windowed/SideBar.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: GPL-3.0-or-later @@ -15,7 +15,7 @@ import org.deepin.launchpad 1.0 import org.deepin.launchpad.models 1.0 ColumnLayout { - spacing: 10 + spacing: Helper.pixelAligned(10, Screen.devicePixelRatio) property bool isFreeSort: CategorizedSortProxyModel.categoryType === CategorizedSortProxyModel.FreeCategory property Item keyTabTarget: title @@ -92,8 +92,8 @@ ColumnLayout { KeyNavigation.down: computer KeyNavigation.up: setting KeyNavigation.tab: nextKeyTabTarget - topPadding: 7 - bottomPadding: 5 + topPadding: Helper.pixelAligned(7, Screen.devicePixelRatio) + bottomPadding: Helper.pixelAligned(5, Screen.devicePixelRatio) ToolTip.visible: hovered ToolTip.delay: 500 ToolTip.text: qsTr("Sorting Mode") @@ -209,6 +209,6 @@ ColumnLayout { Item { height: title.height - Layout.bottomMargin: 10 + Layout.bottomMargin: Helper.pixelAligned(10, Screen.devicePixelRatio) } } diff --git a/qml/windowed/WindowedFrame.qml b/qml/windowed/WindowedFrame.qml index 1d2759a6..cbea3444 100644 --- a/qml/windowed/WindowedFrame.qml +++ b/qml/windowed/WindowedFrame.qml @@ -84,8 +84,8 @@ InputEventItem { anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom - anchors.topMargin: 10 - anchors.leftMargin: 10 + anchors.topMargin: Helper.pixelAligned(10, Screen.devicePixelRatio) + anchors.leftMargin: Helper.pixelAligned(10, Screen.devicePixelRatio) nextKeyTabTarget: bottomBar.keyTabTarget } @@ -95,7 +95,7 @@ InputEventItem { anchors.left: sideBar.right anchors.top: baseLayer.top anchors.bottom: bottomBar.top - anchors.leftMargin: 10 + anchors.leftMargin: Helper.pixelAligned(10, Screen.devicePixelRatio) property Palette backgroundColor: Palette { normal { From 3c36b8b2d6ae2143e3d5156849b7138f18e2bd5f Mon Sep 17 00:00:00 2001 From: Ivy233 Date: Wed, 4 Mar 2026 17:09:25 +0800 Subject: [PATCH 2/2] fix: use QPainter-based rendering for sidebar and bottom bar DCI icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QQuickPaintedItem (PaintedDciIcon) to render DCI icons via QPainter instead of the default Scene Graph GPU texture pipeline. This avoids the bilinear interpolation blur that occurs at fractional DPR scaling (e.g., 125%, 175%), matching the rendering quality of dde-file-manager. Affected icons: - Sidebar: sorting mode, arrow, computer, pictures, documents, desktop, settings - Bottom bar: shutdown, fullscreen fix: 使用基于 QPainter 的方式渲染侧边栏和底部栏的 DCI 图标 使用 QQuickPaintedItem(PaintedDciIcon)通过 QPainter 渲染 DCI 图标, 替代默认的 Scene Graph GPU 纹理管线。这避免了在非整数 DPR 缩放 (如 125%、175%)下由双线性插值引起的图标模糊问题, 使渲染质量与 dde-file-manager 一致。 涉及图标: - 侧边栏:排序模式、箭头、计算机、图片、文档、桌面、设置 - 底部栏:关机、全屏 PMS: BUG-288427 --- CMakeLists.txt | 1 + qml/windowed/BottomBar.qml | 14 +++++- qml/windowed/SideBar.qml | 30 +++++++----- src/quick/painteddciicon.cpp | 92 ++++++++++++++++++++++++++++++++++++ src/quick/painteddciicon.h | 48 +++++++++++++++++++ 5 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 src/quick/painteddciicon.cpp create mode 100644 src/quick/painteddciicon.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b2471dc..636745d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,7 @@ set(SOURCE_FILES launchercontroller.cpp launchercontroller.h debughelper.cpp debughelper.h inputeventitem.h inputeventitem.cpp + src/quick/painteddciicon.h src/quick/painteddciicon.cpp ) set(QML_FILES diff --git a/qml/windowed/BottomBar.qml b/qml/windowed/BottomBar.qml index 60f744c6..a1c5ca28 100644 --- a/qml/windowed/BottomBar.qml +++ b/qml/windowed/BottomBar.qml @@ -26,7 +26,12 @@ Control { contentItem: RowLayout { ToolButton { id: shutdownBtn - icon.name: "shutdown" + contentItem: PaintedDciIcon { + name: "shutdown" + sourceSize: Qt.size(16, 16) + width: 16 + height: 16 + } background: ItemBackground { button: shutdownBtn } @@ -96,7 +101,12 @@ Control { ToolButton { id: fullscreenBtn - icon.name: "launcher_fullscreen" + contentItem: PaintedDciIcon { + name: "launcher_fullscreen" + sourceSize: Qt.size(16, 16) + width: 16 + height: 16 + } background: ItemBackground { button: fullscreenBtn } diff --git a/qml/windowed/SideBar.qml b/qml/windowed/SideBar.qml index d2c91f74..60d9b432 100644 --- a/qml/windowed/SideBar.qml +++ b/qml/windowed/SideBar.qml @@ -100,19 +100,21 @@ ColumnLayout { contentItem: ColumnLayout { spacing: 2 - D.DciIcon { + PaintedDciIcon { sourceSize: Qt.size(16, 16) + width: 16 + height: 16 name: isFreeSort ? categorizedIcon("freeSort") : categorizedIcon(CategorizedSortProxyModel.categoryType) - palette: D.DTK.makeIconPalette(title.palette) - theme: D.DTK.toColorType(title.palette.window) + foreground: title.palette.windowText Layout.alignment: Qt.AlignHCenter } - D.DciIcon { + PaintedDciIcon { name: "arrow" sourceSize: Qt.size(12, 12) - palette: D.DTK.makeIconPalette(title.palette) - theme: D.DTK.toColorType(title.palette.window) + width: 12 + height: 12 + foreground: title.palette.windowText Layout.alignment: Qt.AlignHCenter } } @@ -133,15 +135,19 @@ ColumnLayout { component SideBarButton: D.ActionButton { id: btn + property alias iconSource: paintedIcon.name ToolTip.visible: hovered ToolTip.delay: 500 Layout.alignment: Qt.AlignCenter focusPolicy: Qt.NoFocus // don't inherit window's windowText (it's has opacity of 0.7 in dtkgui.) palette.windowText: D.ColorSelector.textColor - icon { + contentItem: PaintedDciIcon { + id: paintedIcon width: 16 height: 16 + sourceSize: Qt.size(16, 16) + foreground: D.ColorSelector.textColor } background: ItemBackground { button: btn @@ -150,7 +156,7 @@ ColumnLayout { SideBarButton { id: computer - icon.name: "computer-symbolic" + iconSource: "computer-symbolic" ToolTip.text: qsTr("Computer") KeyNavigation.down: images KeyNavigation.up: title @@ -161,7 +167,7 @@ ColumnLayout { SideBarButton { id: images - icon.name: "folder-pictures-symbolic" + iconSource: "folder-pictures-symbolic" ToolTip.text: qsTr("Pictures") KeyNavigation.down: documents KeyNavigation.up: computer @@ -172,7 +178,7 @@ ColumnLayout { SideBarButton { id: documents - icon.name: "folder-documents-symbolic" + iconSource: "folder-documents-symbolic" ToolTip.text: qsTr("Documents") KeyNavigation.down: desktop KeyNavigation.up: images @@ -183,7 +189,7 @@ ColumnLayout { SideBarButton { id: desktop - icon.name: "user-desktop-symbolic" + iconSource: "user-desktop-symbolic" ToolTip.text: qsTr("Desktop") KeyNavigation.down: setting KeyNavigation.up: documents @@ -194,7 +200,7 @@ ColumnLayout { SideBarButton { id: setting - icon.name: "setting" + iconSource: "setting" ToolTip.text: qsTr("Control Center") KeyNavigation.down: title KeyNavigation.up: desktop diff --git a/src/quick/painteddciicon.cpp b/src/quick/painteddciicon.cpp new file mode 100644 index 00000000..862a75a4 --- /dev/null +++ b/src/quick/painteddciicon.cpp @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "painteddciicon.h" + +#include +#include +#include +#include +#include + +DGUI_USE_NAMESPACE + +PaintedDciIcon::PaintedDciIcon(QQuickItem *parent) + : QQuickPaintedItem(parent) +{ + setRenderTarget(QQuickPaintedItem::Image); + + connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged, + this, [this]() { update(); }); +} + +void PaintedDciIcon::paint(QPainter *painter) +{ + if (m_dciIcon.isNull()) + return; + + qreal dpr = window() ? window()->devicePixelRatio() : qApp->devicePixelRatio(); + auto appTheme = DGuiApplicationHelper::instance()->themeType(); + DDciIcon::Theme theme = (appTheme == DGuiApplicationHelper::DarkType) + ? DDciIcon::Dark : DDciIcon::Light; + + QPalette pa = qApp->palette(); + DDciIconPalette palette( + m_foreground.isValid() ? m_foreground : pa.windowText().color(), + pa.window().color(), + pa.highlight().color(), + pa.highlightedText().color() + ); + + int iconW = m_sourceSize.isValid() ? m_sourceSize.width() : qRound(width()); + int iconH = m_sourceSize.isValid() ? m_sourceSize.height() : qRound(height()); + QRect iconRect(0, 0, iconW, iconH); + QRect itemRect(0, 0, qRound(width()), qRound(height())); + iconRect.moveCenter(itemRect.center()); + m_dciIcon.paint(painter, iconRect, dpr, theme, DDciIcon::Normal, + Qt::AlignCenter, palette); +} + +QString PaintedDciIcon::name() const +{ + return m_name; +} + +void PaintedDciIcon::setName(const QString &name) +{ + if (m_name == name) + return; + m_name = name; + m_dciIcon = DDciIcon::fromTheme(m_name); + emit nameChanged(); + update(); +} + +QColor PaintedDciIcon::foreground() const +{ + return m_foreground; +} + +void PaintedDciIcon::setForeground(const QColor &color) +{ + if (m_foreground == color) + return; + m_foreground = color; + emit foregroundChanged(); + update(); +} + +QSize PaintedDciIcon::sourceSize() const +{ + return m_sourceSize; +} + +void PaintedDciIcon::setSourceSize(const QSize &size) +{ + if (m_sourceSize == size) + return; + m_sourceSize = size; + emit sourceSizeChanged(); + update(); +} diff --git a/src/quick/painteddciicon.h b/src/quick/painteddciicon.h new file mode 100644 index 00000000..1bea9d72 --- /dev/null +++ b/src/quick/painteddciicon.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef PAINTEDDCIICON_H +#define PAINTEDDCIICON_H + +#include +#include +#include + +DGUI_USE_NAMESPACE + +class PaintedDciIcon : public QQuickPaintedItem +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QColor foreground READ foreground WRITE setForeground NOTIFY foregroundChanged) + Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize NOTIFY sourceSizeChanged) + +public: + explicit PaintedDciIcon(QQuickItem *parent = nullptr); + + void paint(QPainter *painter) override; + + QString name() const; + void setName(const QString &name); + + QColor foreground() const; + void setForeground(const QColor &color); + + QSize sourceSize() const; + void setSourceSize(const QSize &size); + +signals: + void nameChanged(); + void foregroundChanged(); + void sourceSizeChanged(); + +private: + QString m_name; + QColor m_foreground; + QSize m_sourceSize; + DDciIcon m_dciIcon; +}; + +#endif // PAINTEDDCIICON_H