From 9bec59c98a9f00e6e7f2c0181659a7639bc04e72 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Fri, 28 Aug 2026 11:26:41 +0200 Subject: feat: let a row say whether it is a conversation One predicate for the question every scope, label and membership decision in item 177 keys on. It repeats hasChildren()'s rule deliberately: an expander and a conversation are the same fact, including that a loaded thread trusts its children over a count that included duplicates. --- src/threadlistmodel.cpp | 23 +++++++++++++++++++++++ src/threadlistmodel.h | 8 ++++++++ 2 files changed, 31 insertions(+) (limited to 'src') diff --git a/src/threadlistmodel.cpp b/src/threadlistmodel.cpp index f085b79..1bda728 100644 --- a/src/threadlistmodel.cpp +++ b/src/threadlistmodel.cpp @@ -1014,6 +1014,29 @@ bool ThreadListModel::isMessageRow(const QModelIndex &index) const return index.isValid() && index.parent().isValid(); } +bool ThreadListModel::isConversationRow(const QModelIndex &index) const +{ + if (!index.isValid() || isMessageRow(index)) + return false; + if (index.row() < 0 || index.row() >= m_threads.size()) + return false; + + // A flat view has no conversations by construction: every row is one + // message and there is nothing to expand. + if (m_flatMode) + return false; + + const ThreadNode &node = m_threads.at(index.row()); + + // Identical to hasChildren()'s rule, and deliberately so: an expander and a + // conversation are the same fact. Once loaded the children are the truth, + // which is how a thread whose totalCount counted DUPLICATES stops claiming + // to be a conversation it cannot open. + if (node.loaded) + return !node.children.isEmpty(); + return node.summary.totalCount > 1; +} + MessageNode ThreadListModel::messageAt(const QModelIndex &index) const { if (!isMessageRow(index)) diff --git a/src/threadlistmodel.h b/src/threadlistmodel.h index 4af09d8..d1baae1 100644 --- a/src/threadlistmodel.h +++ b/src/threadlistmodel.h @@ -324,6 +324,14 @@ public: /// True when the index is a message row rather than a thread root. bool isMessageRow(const QModelIndex &index) const; + /// Whether this row stands for a CONVERSATION rather than for one message. + /// + /// The single question every scope, label and membership decision keys on + /// (item 177). A row with replies is the conversation; a row without them + /// is its message and behaves as it always has. A message row is never + /// either, so the answer is false there rather than undefined. + bool isConversationRow(const QModelIndex &index) const; + /// The message row's node, or a default-constructed one for any index that /// is not a message row. MessageNode messageAt(const QModelIndex &index) const; -- cgit v1.2.3