[frameworks/kirigami] /: Icon: keep the aspect ratio of portrait images with roundToIconSize

Méven Car <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 04c0b2a467372d4a07a24c1e95fdfe6f85a3fd51 by Méven Car.
Committed on 21/07/2026 at 10:28.
Pushed by meven into branch 'master'.

Icon: keep the aspect ratio of portrait images with roundToIconSize

updatePaintedGeometry() preserved the aspect ratio only for landscape images.
A taller-than-wide (portrait) image fell into the else branch and was painted
at roundedWidth x roundedWidth, a square, so it was stretched horizontally.
This shows up for instance in the Plasma desktop Folder View, whose preview
icons use a non-square box, where portrait photo previews came out stretched
wide.

Scale the width to preserve the aspect ratio for portrait images too, mirroring
the landscape branch. Square images keep the roundedWidth square as before.

Add a regression test using a 40x80 source in a wide box, asserting the painted
area stays taller than it is wide.

A  +-    --    autotests/portrait-icon.png
M  +30   -0    autotests/tst_icon.qml
M  +4    -0    src/primitives/icon.cpp

https://invent.kde.org/frameworks/kirigami/-/commit/04c0b2a467372d4a07a24c1e95fdfe6f85a3fd51

diff --git a/autotests/portrait-icon.png b/autotests/portrait-icon.png
new file mode 100644
index 000000000..f9d7bb649
Binary files /dev/null and b/autotests/portrait-icon.png differ
diff --git a/autotests/tst_icon.qml b/autotests/tst_icon.qml
index 3d285a42b..c0ab954bf 100644
--- a/autotests/tst_icon.qml
+++ b/autotests/tst_icon.qml
@@ -32,6 +32,16 @@ TestCase {
             source: Qt.resolvedUrl("stop-icon.svg")
         }
     }
+    Component {
+        id: portraitIcon
+        Kirigami.Icon {
+            // A non-square box, wider than tall.
+            width: 100
+            height: 50
+            // A 40x80 image: taller than it is wide.
+            source: Qt.resolvedUrl("portrait-icon.png")
+        }
+    }
     Kirigami.ImageColors {
         id: imageColors
     }
@@ -55,6 +65,26 @@ TestCase {
         verify(waitForRendering(icon))
     }
 
+    function test_portrait_aspect_ratio_data() {
+        return [
+            { tag: "roundToIconSize", roundToIconSize: true },
+            { tag: "no roundToIconSize", roundToIconSize: false },
+        ]
+    }
+
+    // A portrait image must keep its aspect ratio and not be stretched to a square.
+    function test_portrait_aspect_ratio(data) {
+        let icon = createTemporaryObject(portraitIcon, testCase, { roundToIconSize: data.roundToIconSize })
+        verify(icon)
+        verify(waitForRendering(icon))
+        // The source loads asynchronously; before it is ready the icon uses a box-sized
+        // placeholder, so wait for the real image to be loaded before checking its aspect.
+        tryVerify(() => icon.status === Kirigami.Icon.Ready)
+        verify(icon.paintedWidth > 0 && icon.paintedHeight > 0)
+        verify(icon.paintedWidth < icon.paintedHeight,
+               "portrait icon must not be stretched wide: paintedWidth=" + icon.paintedWidth + " paintedHeight=" + icon.paintedHeight)
+    }
+
     function test_absolutepath_recoloring() {
         skip("This test depends too much on environment and other factors to work reliably")
 
diff --git a/src/primitives/icon.cpp b/src/primitives/icon.cpp
index 7f3239b4a..9a2821878 100644
--- a/src/primitives/icon.cpp
+++ b/src/primitives/icon.cpp
@@ -610,7 +610,11 @@ void Icon::updatePaintedGeometry()
         }
         if (m_roundToIconSize && m_units) {
             if (m_icon.width() > m_icon.height()) {
+                // landscape image
                 newSize = QSizeF(roundedWidth, m_icon.height() * (roundedWidth / static_cast<qreal>(m_icon.width())));
+            } else if (m_icon.height() > m_icon.width()) {
+                // portrait image
+                newSize = QSizeF(m_icon.width() * (roundedWidth / static_cast<qreal>(m_icon.height())), roundedWidth);
             } else {
                 newSize = QSizeF(roundedWidth, roundedWidth);
             }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.