aboutsummaryrefslogtreecommitdiffstats
path: root/src/avatar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/avatar.cpp')
-rw-r--r--src/avatar.cpp69
1 files changed, 64 insertions, 5 deletions
diff --git a/src/avatar.cpp b/src/avatar.cpp
index 7aaa6eb..9b292a2 100644
--- a/src/avatar.cpp
+++ b/src/avatar.cpp
@@ -26,6 +26,42 @@
namespace {
+/// The bare display name from whatever the card's first line holds.
+///
+/// That string is the RAW header for a reply row (`Name <addr>`) and a
+/// comma-joined author summary for a thread row, so a naive space split gives
+/// `T<` for one and one letter from each of two different people for the
+/// other. Take the first entry, drop the angle-addr and any quoting, and
+/// report nothing when what remains is itself an address: the caller's address
+/// branch reads that far better than the local part would.
+QString displayNameOf(const QString &raw)
+{
+ QString name = raw.trimmed();
+
+ // `Name <addr>`: everything before the bracket is the name. When there is
+ // nothing before it, the address itself is not a name.
+ const int bracket = name.indexOf(QLatin1Char('<'));
+ if (bracket >= 0)
+ name = name.left(bracket).trimmed();
+
+ // A comma joins either two authors or a `"Rossi, Mario"` quoted name. The
+ // quotes tell them apart, so strip them only after splitting.
+ if (!name.startsWith(QLatin1Char('"'))) {
+ const int comma = name.indexOf(QLatin1Char(','));
+ if (comma >= 0)
+ name = name.left(comma).trimmed();
+ }
+ if (name.size() >= 2 && name.startsWith(QLatin1Char('"'))
+ && name.endsWith(QLatin1Char('"'))) {
+ name = name.mid(1, name.size() - 2).trimmed();
+ }
+
+ // A bare address left standing is not a name.
+ if (name.contains(QLatin1Char('@')))
+ return QString();
+ return name;
+}
+
QString twoFrom(const QString &text)
{
const QString trimmed = text.trimmed();
@@ -43,8 +79,19 @@ namespace Avatar {
QString initialsFor(const QString &displayName, const QString &address,
const QString &accountLabel)
{
- const QStringList words = displayName.split(QLatin1Char(' '),
- Qt::SkipEmptyParts);
+ // Words, and a separator is not one. `INE - Expert IT Training` split on
+ // spaces alone gave `I-`, because the dash counted as the second word.
+ QStringList words;
+ const QStringList parts = displayNameOf(displayName)
+ .split(QLatin1Char(' '), Qt::SkipEmptyParts);
+ for (const QString &part : parts) {
+ // Trim leading punctuation so `(Team)` still contributes its `T`.
+ int at = 0;
+ while (at < part.size() && !part.at(at).isLetterOrNumber())
+ ++at;
+ if (at < part.size())
+ words.append(part.mid(at));
+ }
if (words.size() >= 2) {
return (words.at(0).left(1) + words.at(1).left(1)).toUpper();
}
@@ -83,7 +130,10 @@ Fill fillFor(const QString &displayName, bool isBusinessSender)
// heuristic, or a listed sender could never be pinned.
if (isBusinessSender)
return Fill::TwoTone;
- return displayName.trimmed().isEmpty() ? Fill::TwoTone : Fill::Identicon;
+ // The same normalisation initialsFor() uses: a raw `<addr>` header or a
+ // bare address is not a display name, so it must not read as a person.
+ return displayNameOf(displayName).isEmpty() ? Fill::TwoTone
+ : Fill::Identicon;
}
QColor colourFor(const QString &address)
@@ -150,9 +200,18 @@ QPixmap pixmapFor(const QString &seed, const QString &initials, Fill fill,
// behind the letters stays large and flat, which is the whole reason
// this fill exists beside the identicon.
const int angle = static_cast<quint8>(digest.at(1)) * 360 / 256;
- QLineF axis = QLineF::fromPolar(side, angle);
+ // The axis must span the DIAMETER through the centre, not a radius
+ // from it. fromPolar() starts at the origin, so translating by half
+ // the side put p1 at the centre and the 0.5 colour stop on the
+ // squircle's edge: one hue filled almost the whole face and the fill
+ // read as flat. The diagonal, so the split still crosses the shape at
+ // any angle.
+ const qreal reach = side * 0.71;
+ QLineF axis = QLineF::fromPolar(reach, angle);
axis.translate(side / 2.0, side / 2.0);
- QLinearGradient gradient(axis.p2(), axis.p1());
+ QLineF back = QLineF::fromPolar(reach, angle + 180.0);
+ back.translate(side / 2.0, side / 2.0);
+ QLinearGradient gradient(back.p2(), axis.p2());
gradient.setColorAt(0.0, base);
gradient.setColorAt(0.499, base);
gradient.setColorAt(0.5, base.darker(135));