[kdevplatform] shell: Favour strongly distinct working set icons

Sven Brauch svenbrauch at googlemail.com
Tue Dec 24 01:40:00 GMT 2013


Git commit 0b4e46220da82a6cc50bc8554bc9778632f7b51e by Sven Brauch.
Committed on 24/12/2013 at 01:39.
Pushed by brauch into branch 'master'.

Favour strongly distinct working set icons

When a new working set is created, the code will try to pick an icon
which is sufficiently distinct from the existing icons. This should work
nicely unless you have dozens of sets opened. Tell me if it does not. ;)

CCMAIL:kdevelop at kde.org

M  +27   -2    shell/workingsetcontroller.cpp
M  +1    -0    shell/workingsetcontroller.h
M  +11   -16   shell/workingsets/workingset.cpp
M  +35   -0    shell/workingsets/workingset.h

http://commits.kde.org/kdevplatform/0b4e46220da82a6cc50bc8554bc9778632f7b51e

diff --git a/shell/workingsetcontroller.cpp b/shell/workingsetcontroller.cpp
index 417a3b4..1b40750 100644
--- a/shell/workingsetcontroller.cpp
+++ b/shell/workingsetcontroller.cpp
@@ -97,10 +97,35 @@ void WorkingSetController::cleanup()
     m_emptyWorkingSet = 0;
 }
 
+const QString WorkingSetController::makeSetId(const QString& prefix) const
+{
+    QString newId;
+    const int maxRetries = 10;
+    for(unsigned int retry = 2; retry <= maxRetries; retry++) {
+        newId = QString("%1_%2").arg(prefix).arg(qrand() % 10000000);
+        WorkingSetIconParameters params(newId);
+        foreach(WorkingSet* set, m_workingSets) {
+            if(set->isEmpty()) {
+                continue;
+            }
+            // The last retry will always generate a valid set
+            const int maxSimilarity = retry > maxRetries / 2 ? 55 : 35;
+            if(retry != maxRetries && WorkingSetIconParameters(set->id()).similarity(params) >= retry*8) {
+                newId = QString();
+                break;
+            }
+        }
+        if(! newId.isEmpty()) {
+            break;
+        }
+    }
+    return newId;
+}
+
 WorkingSet* WorkingSetController::newWorkingSet(const QString& prefix)
 {
-    QString newId = QString("%1_%2").arg(prefix).arg(qrand() % 10000000);
-    return getWorkingSet(newId);
+
+    return getWorkingSet(makeSetId(prefix));
 }
 
 WorkingSet* WorkingSetController::getWorkingSet(const QString& id)
diff --git a/shell/workingsetcontroller.h b/shell/workingsetcontroller.h
index ea6985b..1bd2138 100644
--- a/shell/workingsetcontroller.h
+++ b/shell/workingsetcontroller.h
@@ -103,6 +103,7 @@ private slots:
 
 private:
     void setupActions();
+    const QString makeSetId(const QString& prefix) const;
 
     QSet<QString> m_usedIcons;
     QMap<QString, WorkingSet*> m_workingSets;
diff --git a/shell/workingsets/workingset.cpp b/shell/workingsets/workingset.cpp
index bfe93ca..bc4f430 100644
--- a/shell/workingsets/workingset.cpp
+++ b/shell/workingsets/workingset.cpp
@@ -42,21 +42,16 @@ bool WorkingSet::m_loading = false;
 
 namespace {
 
-QIcon generateIcon(const QString& id)
+QIcon generateIcon(const WorkingSetIconParameters& params)
 {
     QImage pixmap(16, 16, QImage::Format_ARGB32);
     // fill the background with a transparent color
     pixmap.fill(QColor::fromRgba(qRgba(0, 0, 0, 0)));
-    // calculate layout and colors depending on the working set ID
-    // modulo it so it's around 2^28, leaving some space before uint overflows
-    const uint setId = qHash(id) % 268435459;
-    // amount of colored squares in this icon (the rest is grey or whatever you set as default color)
-    // use 4-6-4-1 weighting for 1, 2, 3, 4 squares, because that's the number of arrangements for each
-    const uint coloredCount = (setId % 15 < 4) ? 1 : (setId % 15 < 10) ? 2 : (setId % 15 == 14) ? 4 : 3;
+    const uint coloredCount = params.coloredCount;
     // coordinates of the rectangles to draw, for 16x16 icons specifically
     QList<QRect> rects;
     rects << QRect(1, 1, 5, 5) << QRect(1, 9, 5, 5) << QRect(9, 1, 5, 5) << QRect(9, 9, 5, 5);
-    if ( setId % 31 < 16 ) {
+    if ( params.swapDiagonal ) {
         rects.swap(1, 2);
     }
 
@@ -67,31 +62,31 @@ QIcon generateIcon(const QString& id)
     // color for colored squares
     // this code is not fragile, you can just tune the magic formulas at random and see what looks good.
     // just make sure to keep it within the 0-360 / 0-255 / 0-255 space of the HSV model
-    QColor brightColor = QColor::fromHsv((setId % 273 * 81) % 360, qMin<uint>(255, 215 + (setId*5) % 150),
-                                         205 + (setId*11) % 50);
+    QColor brightColor = QColor::fromHsv(params.hue, qMin<uint>(255, 215 + (params.setId*5) % 150),
+                                         205 + (params.setId*11) % 50);
     // Y'UV "Y" value, the approximate "lightness" of the color
     // If it is above 0.6, then making the color darker a bit is okay,
     // if it is below 0.35, then the color should be a bit brighter.
     float brightY = 0.299 * brightColor.redF() + 0.587 * brightColor.greenF() + 0.114 * brightColor.blueF();
     if ( brightY > 0.6 ) {
-        if ( setId % 7 < 2 ) {
+        if ( params.setId % 7 < 2 ) {
             // 2/7 chance to make the color significantly darker
-            brightColor = brightColor.darker(120 + (setId*7) % 35);
+            brightColor = brightColor.darker(120 + (params.setId*7) % 35);
         }
-        else if ( setId % 5 == 0 ) {
+        else if ( params.setId % 5 == 0 ) {
             // 1/5 chance to make it a bit darker
-            brightColor = brightColor.darker(110 + (setId*3) % 10);
+            brightColor = brightColor.darker(110 + (params.setId*3) % 10);
         }
     }
     if ( brightY < 0.35 ) {
         // always make the color brighter to avoid very dark colors (like rgb(0, 0, 255))
-        brightColor = brightColor.lighter(120 + (setId*13) % 55);
+        brightColor = brightColor.lighter(120 + (params.setId*13) % 55);
     }
     int at = 0;
     foreach ( const QRect& rect, rects ) {
         QColor currentColor;
         // pick the colored squares; you can get different patterns by re-ordering the "rects" list
-        if ( (at + setId*7) % 4 < coloredCount ) {
+        if ( (at + params.setId*7) % 4 < coloredCount ) {
             currentColor = brightColor;
         }
         else {
diff --git a/shell/workingsets/workingset.h b/shell/workingsets/workingset.h
index 047240f..98fbff3 100644
--- a/shell/workingsets/workingset.h
+++ b/shell/workingsets/workingset.h
@@ -23,6 +23,7 @@
 #include <QIcon>
 #include <KConfigGroup>
 #include <QPointer>
+#include <QDebug>
 
 namespace Sublime {
 class Area;
@@ -32,6 +33,40 @@ class View;
 
 namespace KDevelop {
 
+/// Contains all significant parameters which control the appearance of a working set icon
+struct WorkingSetIconParameters {
+    WorkingSetIconParameters(const QString& id)
+        : setId(qHash(id) % 268435459)
+        , coloredCount((setId % 15 < 4) ? 1 : (setId % 15 < 10) ? 2 : (setId % 15 == 14) ? 4 : 3)
+        , hue((setId % 273 * 83) % 360)
+        , swapDiagonal(setId % 31 < 16)
+    { };
+    // calculate layout and colors depending on the working set ID
+    // modulo it so it's around 2^28, leaving some space before uint overflows
+    const uint setId;
+    // amount of colored squares in this icon (the rest is grey or whatever you set as default color)
+    // use 4-6-4-1 weighting for 1, 2, 3, 4 squares, because that's the number of arrangements for each
+    const uint coloredCount;
+    const uint hue;
+    bool swapDiagonal;
+    // between 0 and 100, 100 = very similar, 0 = very different
+    // 20 points should make a significantly different icon.
+    uint similarity(const WorkingSetIconParameters& other) const {
+        int sim = 100;
+        uint hueDiff = qAbs<int>(hue - other.hue);
+        hueDiff = hueDiff > 180 ? 360 - hueDiff : hueDiff;
+        sim -= hueDiff > 35 ? 50 : (hueDiff * 50) / 180;
+        if ( coloredCount != other.coloredCount ) {
+            sim -= 50;
+        }
+        else if ( coloredCount == 2 && swapDiagonal != other.swapDiagonal ) {
+            sim -= 35;
+        }
+        return sim;
+    };
+};
+
+
 class WorkingSet : public QObject {
     Q_OBJECT
 



More information about the KDevelop mailing list