summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/carddelegate.cpp6
-rw-r--r--src/cardlayout.cpp33
-rw-r--r--src/cardlayout.h17
-rw-r--r--src/threadlistmodel.cpp18
-rw-r--r--src/threadlistmodel.h8
-rw-r--r--src/threadlistview.cpp4
-rw-r--r--src/threadlistview.h2
-rw-r--r--src/types.h6
8 files changed, 55 insertions, 39 deletions
diff --git a/src/carddelegate.cpp b/src/carddelegate.cpp
index fbdf13f..02f1df0 100644
--- a/src/carddelegate.cpp
+++ b/src/carddelegate.cpp
@@ -40,7 +40,7 @@ CardLayout::Input inputFor(const QModelIndex &index)
CardLayout::Input in;
in.isMessage = index.data(ThreadListModel::IsMessageRole).toBool();
in.depth = index.data(ThreadListModel::MessageDepthRole).toInt();
- in.replyCount = index.data(ThreadListModel::ReplyCountRole).toInt();
+ in.messageCount = index.data(ThreadListModel::MessageCountRole).toInt();
in.dateFormat = index.data(ThreadListModel::DateFormatRole).toString();
// Item 70's marks. The layout reserves a rect for each, so these have to
@@ -305,14 +305,14 @@ void CardDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
drawMark(card.receivedForwardRect, Marks::Mark::ReceivedForward);
drawMark(card.repliedRect, Marks::Mark::Replied);
- // The reply count, which is also the expander, drawn as a PILL.
+ // The message count, which is also the expander, drawn as a PILL.
//
// A bare "3" on the card's own background read as an unexplained number
// beside the subject and gave no hint that it could be clicked. The chip
// shape says "this is a control", matching the tag chips on line 3, and the
// word says what the number counts.
if (!card.expanderRect.isEmpty()) {
- const int count = index.data(ThreadListModel::ReplyCountRole).toInt();
+ const int count = index.data(ThreadListModel::MessageCountRole).toInt();
const QString label = CardLayout::expanderLabel(
count, option.state & QStyle::State_Open);
diff --git a/src/cardlayout.cpp b/src/cardlayout.cpp
index ec22635..f89d3ec 100644
--- a/src/cardlayout.cpp
+++ b/src/cardlayout.cpp
@@ -18,6 +18,7 @@
#include "cardlayout.h"
+#include <QCoreApplication>
#include <QFontMetrics>
#include <QLocale>
@@ -52,9 +53,9 @@ int CardLayout::markSide(const QFont &font)
return qMax(8, side);
}
-QString CardLayout::expanderLabel(int replyCount, bool expanded)
+QString CardLayout::expanderLabel(int messageCount, bool expanded)
{
- // "3 replies", not a bare "3". The count alone reads as an unexplained
+ // "5 messages", not a bare "5". The count alone reads as an unexplained
// number beside the subject, and the word is what says the card opens.
//
// The triangle is NO LONGER part of this string. Item 70 made it a drawn
@@ -65,14 +66,20 @@ QString CardLayout::expanderLabel(int replyCount, bool expanded)
// when the card opens, and a caller that stopped passing the state would
// hide that requirement rather than satisfy it.
//
- // Not translated through tr() here because CardLayout is a plain struct
- // rather than a QObject; the delegate is where a translated build would
- // wrap this, and the string is deliberately kept in one place so there is
- // exactly one thing to change.
+ // CardLayout is a plain struct rather than a QObject, so this cannot use
+ // tr(); QCoreApplication::translate() with an explicit context is the
+ // equivalent, and the literal context string is what lupdate extracts
+ // under. The word is translated rather than the whole "%1 %2", because
+ // %n's untranslated fallback on this Qt does not pluralise (measured:
+ // "%n message(s)" stays literally "(s)"), and the two forms read cleanly
+ // in the .ts.
Q_UNUSED(expanded);
- const QString word = replyCount == 1 ? QStringLiteral("reply")
- : QStringLiteral("replies");
- return QStringLiteral("%1 %2").arg(replyCount).arg(word);
+ const QString word = messageCount == 1
+ ? QCoreApplication::translate("CardLayout",
+ "message")
+ : QCoreApplication::translate("CardLayout",
+ "messages");
+ return QStringLiteral("%1 %2").arg(messageCount).arg(word);
}
QString CardLayout::widestDateSample(const QString &format)
@@ -196,18 +203,18 @@ CardLayout CardLayout::compute(const Input &input, const QRect &rect,
- kPaddingX),
metrics.height());
- // The expander is the reply count as a PILL, on line two and on the right.
+ // The expander is the message count as a PILL, on line two and on the right.
//
// Sized from the label actually drawn rather than from a fixed sample, so
// the background and the text inside it cannot disagree. Both states of the
// glyph are measured because the rect must not change width when the card
// is expanded: a pill that resized on click would shift the subject's
// elision under the pointer.
- if (input.replyCount > 0) {
+ if (input.messageCount > 0) {
const int collapsed = smallMetrics.horizontalAdvance(
- expanderLabel(input.replyCount, false));
+ expanderLabel(input.messageCount, false));
const int expanded = smallMetrics.horizontalAdvance(
- expanderLabel(input.replyCount, true));
+ expanderLabel(input.messageCount, true));
// The triangle is a drawn mark since item 70, so the pill has to
// reserve its width explicitly. It came free from the text metrics
// while it was a glyph in the label, which is exactly the kind of
diff --git a/src/cardlayout.h b/src/cardlayout.h
index 57edca7..9620900 100644
--- a/src/cardlayout.h
+++ b/src/cardlayout.h
@@ -35,7 +35,7 @@
/// The card is three lines, always:
///
/// sender ................................ date <- senderRect/dateRect
-/// * subject @ v 3 replies <- subjectRect/expanderRect
+/// * subject @ v 5 messages <- subjectRect/expanderRect
/// [tag] [tag] <- tagRect
struct CardLayout
{
@@ -45,7 +45,7 @@ struct CardLayout
{
bool isMessage = false;
int depth = 0; ///< 0 for a thread root, 1 for a direct reply.
- int replyCount = 0; ///< 0 means no expander.
+ int messageCount = 0; ///< Messages in the conversation; 0 means no expander.
/// A QDateTime::toString() pattern from [general] date_format, or empty
/// for the system's short format.
@@ -105,8 +105,8 @@ struct CardLayout
QRect subjectRect;
QRect tagRect;
- /// The reply count's rect, and the click target that toggles the thread.
- /// Empty when the row has no replies.
+ /// The message count's rect, and the click target that toggles the thread.
+ /// Empty when the row has no expander.
QRect expanderRect;
/// The flagged mark, at the start of line two before the subject. Empty
@@ -197,15 +197,18 @@ struct CardLayout
/// The widest string formatDate() can return, for reserving space.
static QString widestDateSample(const QString &format = QString());
- /// The expander's label: the reply count with its glyph, as drawn.
+ /// The expander's label: the message count with its word, as drawn.
///
/// Shared with the layout for the same reason as formatDate: the rect
/// reserved for the pill and the text put inside it must come from one
/// place, or a count wider than the sample the layout guessed at spills
/// out of its own background.
///
- /// `expanded` chooses which way the triangle points.
- static QString expanderLabel(int replyCount, bool expanded);
+ /// `expanded` chooses which way the triangle points. The word is
+ /// translated: a pill reading a foreign count in English next to an
+ /// otherwise local interface is a tiny broken window, and this is the one
+ /// user-facing string this plain struct owns.
+ static QString expanderLabel(int messageCount, bool expanded);
/// Padding inside the expander pill, matching a tag chip's, so the two read
/// as the same kind of object on the card.
diff --git a/src/threadlistmodel.cpp b/src/threadlistmodel.cpp
index 8bb9849..5c44dd4 100644
--- a/src/threadlistmodel.cpp
+++ b/src/threadlistmodel.cpp
@@ -439,7 +439,7 @@ QVariant ThreadListModel::data(const QModelIndex &index, int role) const
m_forwardPrefixes);
case IsRepliedRole:
return node.isReplied();
- case ReplyCountRole:
+ case MessageCountRole:
// A reply never offers an expander: nesting past the first level is
// drawn from depth, not from further parent-child structure.
return 0;
@@ -714,15 +714,19 @@ QVariant ThreadListModel::data(const QModelIndex &index, int role) const
m_forwardPrefixes);
case IsRepliedRole:
return thread.isReplied();
- case ReplyCountRole:
+ case MessageCountRole:
// Zero in a flat list, so the card draws no expander pill. The count
- // and hasChildren() must agree: a card advertising "3 replies" that
- // cannot be opened is the inert-glyph defect this project has already
- // shipped once.
+ // and hasChildren() must agree: a card advertising a count for a thread
+ // that cannot be opened is the inert-glyph defect this project has
+ // already shipped once.
if (m_flatMode)
return 0;
- // totalCount includes the root message, which is the card itself.
- return qMax(0, thread.totalCount - 1);
+ // The number of MESSAGES in the conversation, not the replies alone:
+ // the row stands for the conversation now (item 177), so a thread of
+ // one message and four replies reads "5 messages". A thread of one
+ // shows nothing: its row is the message, there is nothing to expand,
+ // and the pill is the expander.
+ return thread.totalCount > 1 ? thread.totalCount : 0;
case DateFormatRole:
return m_dateFormat;
default:
diff --git a/src/threadlistmodel.h b/src/threadlistmodel.h
index 93474ac..fee4a1f 100644
--- a/src/threadlistmodel.h
+++ b/src/threadlistmodel.h
@@ -83,7 +83,8 @@ public:
MessageIdRole,
/// The message's reply depth, for the view's indentation. 1 for a
- /// direct reply, since depth 0 is the root row itself.
+ /// direct reply; 0 is the thread's first message, which sits under the
+ /// conversation row unindented.
MessageDepthRole,
/// True when the row is a thread that has replies to show.
@@ -138,7 +139,8 @@ public:
/// bool; the message was replied to, from the Maildir "R" flag.
IsRepliedRole,
- ReplyCountRole, ///< int; 0 when a thread has no replies.
+ MessageCountRole, ///< int; messages in the conversation, 0 when the
+ ///< thread has one message and nothing to expand.
/// The [general] date_format pattern, or empty for the system's short
/// format. Same row value for every row.
@@ -207,7 +209,7 @@ public:
/// that is what keeps it from leaking: it is off by default, only the Sent
/// button turns it on, and every other query turns it off again. The
/// expander already comes from hasChildren() and the card's count from
- /// ReplyCountRole, so flat mode is those two answering differently and
+ /// MessageCountRole, so flat mode is those two answering differently and
/// nothing else changes.
///
/// The children are not discarded, only hidden. Leaving flat mode restores
diff --git a/src/threadlistview.cpp b/src/threadlistview.cpp
index 5a3ce77..019d7c9 100644
--- a/src/threadlistview.cpp
+++ b/src/threadlistview.cpp
@@ -27,10 +27,10 @@ void ThreadListView::mousePressEvent(QMouseEvent *event)
{
const QModelIndex index = indexAt(event->pos());
- // The reply count IS the expander. Anything outside its rect selects the
+ // The message count IS the expander. Anything outside its rect selects the
// card and opens it, which is what the rest of the card is for.
if (event->button() == Qt::LeftButton && index.isValid()
- && index.data(ThreadListModel::ReplyCountRole).toInt() > 0) {
+ && index.data(ThreadListModel::MessageCountRole).toInt() > 0) {
QStyleOptionViewItem option;
initViewItemOption(&option);
diff --git a/src/threadlistview.h b/src/threadlistview.h
index 3215153..a509389 100644
--- a/src/threadlistview.h
+++ b/src/threadlistview.h
@@ -40,7 +40,7 @@ public:
using QTreeView::QTreeView;
protected:
- /// Toggles a thread when its reply count is clicked.
+ /// Toggles a thread when its message count is clicked.
///
/// Being VISIBLE and being CLICKABLE are separate properties:
/// setRootIsDecorated(false), needed to stop the style drawing its own
diff --git a/src/types.h b/src/types.h
index 8e24cd1..ee2627f 100644
--- a/src/types.h
+++ b/src/types.h
@@ -190,9 +190,9 @@ struct MessageNode
QStringList tags;
QString filePath;
- /// Reply depth within the thread. 0 is the thread's first message, which
- /// occupies the ROOT row rather than a child row: the user's model is
- /// "N replies", so a thread of 7 shows 1 root and 6 descendants.
+ /// Reply depth within the thread. 0 is the thread's first message: since
+ /// item 177 a conversation lists it as a child row too, but it carries no
+ /// nesting. 1 is a direct reply.
int depth = 0;
bool isUnread() const { return tags.contains(QStringLiteral("unread")); }