feat: email grouping by date + stability fixes

- EmailTreeModel: hierarchical model for grouped email view (QTreeView)
- DateGroupProxyModel: proxy model for date-based grouping logic
- MailListView: migrated from QTableView to QTreeView with grouping support
- ReaderView: subject frame with shadow, avatar moved to header, body inside header
- MainWindow: frameless with rounded corners, 1px border, edge resize, no status bar

- Stability fixes for grouping mode switch:
  * Proper tree cleanup (no double-delete of root)
  * Bounds checking on all array accesses
  * Null pointer checks in index/parent/data methods
  * Date validity checks before grouping
  * Index validation before accessing m_emails
This commit is contained in:
2026-08-24 01:04:00 +02:00
parent e28ab35fa9
commit 2281b01f49
19 changed files with 1998 additions and 219 deletions
+78
View File
@@ -0,0 +1,78 @@
#pragma once
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QVector>
#include <QDateTime>
#include <QDate>
#include "core/mailitem.h"
class EmailTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum Columns {
ColSubject = 0,
ColSender = 1,
ColDate = 2,
ColCount = 3
};
enum Roles {
MailIdRole = Qt::UserRole + 1,
IsGroupRole = Qt::UserRole + 2,
GroupNameRole = Qt::UserRole + 3,
GroupIndexRole = Qt::UserRole + 4,
MailIndexRole = Qt::UserRole + 5
};
explicit EmailTreeModel(QObject *parent = nullptr);
~EmailTreeModel() override;
// Basic model interface
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
// Email data management
void setEmails(const QVector<MailItem> &emails);
void clear();
// Grouping
enum GroupMode {
NoGrouping = 0,
GroupByDate = 1
};
void setGroupMode(GroupMode mode);
GroupMode groupMode() const { return m_groupMode; }
private:
struct TreeItem {
enum Type { Group, Mail } type;
QString groupName; // for Group type
int groupIndex = -1; // for Group type
int mailIndex = -1; // for Mail type (index in m_emails)
QVector<TreeItem*> children;
TreeItem* parent = nullptr;
int row = -1;
TreeItem(Type t, TreeItem* p = nullptr) : type(t), parent(p) {}
~TreeItem() { qDeleteAll(children); }
};
GroupMode m_groupMode = NoGrouping;
QVector<MailItem> m_emails;
TreeItem* m_rootItem = nullptr;
void setupModelData();
void buildGroups();
QString groupNameForDate(const QDateTime &date) const;
int groupIndexForDate(const QDateTime &date) const;
void deleteTree(TreeItem* item);
};