feat: Pinned emails support (flag/follow-up)
- EmailListModel: new m_pinnedOnly filter + setShowPinnedOnly() - EmailListModel/EmailListModel.cpp: passes pinnedOnly to paginated queries - MailItemDao: countAll, countByFolderId, findAllPaginated, findByFolderIdPaginated accept pinnedOnly - MailListView: new 'Solo fijados' checkbox in filter bar - CompactMailDelegate: orange pushpin indicator (top-right) for pinned emails - Works in both tree view and compact card view - Pinned indicator uses QPainterPath for pushpin shape (orange)
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -555,4 +555,6 @@ CMakeFiles/wino-mail-qt.dir/src/ui/delegates/CompactMailDelegate.cpp.o: \
|
|||||||
/usr/include/x86_64-linux-gnu/qt6/QtCore/QDebug \
|
/usr/include/x86_64-linux-gnu/qt6/QtCore/QDebug \
|
||||||
/usr/include/x86_64-linux-gnu/qt6/QtCore/qdebug.h \
|
/usr/include/x86_64-linux-gnu/qt6/QtCore/qdebug.h \
|
||||||
/usr/include/x86_64-linux-gnu/qt6/QtGui/QCursor \
|
/usr/include/x86_64-linux-gnu/qt6/QtGui/QCursor \
|
||||||
/usr/include/x86_64-linux-gnu/qt6/QtGui/qcursor.h
|
/usr/include/x86_64-linux-gnu/qt6/QtGui/qcursor.h \
|
||||||
|
/usr/include/x86_64-linux-gnu/qt6/QtGui/QPainterPath \
|
||||||
|
/usr/include/x86_64-linux-gnu/qt6/QtGui/qpainterpath.h
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -438,7 +438,7 @@ int MailItemDao::countUnreadByFolderId(int folderId)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MailItemDao::countAll(const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments)
|
int MailItemDao::countAll(const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments, bool pinnedOnly)
|
||||||
{
|
{
|
||||||
QSqlDatabase db = DatabaseManager::instance().database();
|
QSqlDatabase db = DatabaseManager::instance().database();
|
||||||
QString sql = "SELECT COUNT(*) FROM MailCopy WHERE 1=1";
|
QString sql = "SELECT COUNT(*) FROM MailCopy WHERE 1=1";
|
||||||
@@ -457,6 +457,9 @@ int MailItemDao::countAll(const QString& searchFilter, bool unreadOnly, bool fla
|
|||||||
if (hasAttachments) {
|
if (hasAttachments) {
|
||||||
sql += " AND hasAttachment = 1";
|
sql += " AND hasAttachment = 1";
|
||||||
}
|
}
|
||||||
|
if (pinnedOnly) {
|
||||||
|
sql += " AND isPinned = 1";
|
||||||
|
}
|
||||||
|
|
||||||
query.prepare(sql);
|
query.prepare(sql);
|
||||||
if (!query.exec()) {
|
if (!query.exec()) {
|
||||||
@@ -469,7 +472,7 @@ int MailItemDao::countAll(const QString& searchFilter, bool unreadOnly, bool fla
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MailItemDao::countByFolderId(int folderId, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments)
|
int MailItemDao::countByFolderId(int folderId, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments, bool pinnedOnly)
|
||||||
{
|
{
|
||||||
QSqlDatabase db = DatabaseManager::instance().database();
|
QSqlDatabase db = DatabaseManager::instance().database();
|
||||||
QString sql = "SELECT COUNT(*) FROM MailCopy WHERE folderId = :folderId";
|
QString sql = "SELECT COUNT(*) FROM MailCopy WHERE folderId = :folderId";
|
||||||
@@ -489,6 +492,9 @@ int MailItemDao::countByFolderId(int folderId, const QString& searchFilter, bool
|
|||||||
if (hasAttachments) {
|
if (hasAttachments) {
|
||||||
sql += " AND hasAttachment = 1";
|
sql += " AND hasAttachment = 1";
|
||||||
}
|
}
|
||||||
|
if (pinnedOnly) {
|
||||||
|
sql += " AND isPinned = 1";
|
||||||
|
}
|
||||||
|
|
||||||
query.prepare(sql);
|
query.prepare(sql);
|
||||||
if (!query.exec()) {
|
if (!query.exec()) {
|
||||||
@@ -501,7 +507,7 @@ int MailItemDao::countByFolderId(int folderId, const QString& searchFilter, bool
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<MailItem> MailItemDao::findAllPaginated(int offset, int limit, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments)
|
QVector<MailItem> MailItemDao::findAllPaginated(int offset, int limit, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments, bool pinnedOnly)
|
||||||
{
|
{
|
||||||
QVector<MailItem> items;
|
QVector<MailItem> items;
|
||||||
QSqlDatabase db = DatabaseManager::instance().database();
|
QSqlDatabase db = DatabaseManager::instance().database();
|
||||||
@@ -519,6 +525,9 @@ QVector<MailItem> MailItemDao::findAllPaginated(int offset, int limit, const QSt
|
|||||||
if (hasAttachments) {
|
if (hasAttachments) {
|
||||||
sql += " AND hasAttachment = 1";
|
sql += " AND hasAttachment = 1";
|
||||||
}
|
}
|
||||||
|
if (pinnedOnly) {
|
||||||
|
sql += " AND isPinned = 1";
|
||||||
|
}
|
||||||
|
|
||||||
sql += " ORDER BY date DESC LIMIT :limit OFFSET :offset";
|
sql += " ORDER BY date DESC LIMIT :limit OFFSET :offset";
|
||||||
|
|
||||||
@@ -567,7 +576,7 @@ QVector<MailItem> MailItemDao::findAllPaginated(int offset, int limit, const QSt
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<MailItem> MailItemDao::findByFolderIdPaginated(int folderId, int offset, int limit, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments)
|
QVector<MailItem> MailItemDao::findByFolderIdPaginated(int folderId, int offset, int limit, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments, bool pinnedOnly)
|
||||||
{
|
{
|
||||||
QVector<MailItem> items;
|
QVector<MailItem> items;
|
||||||
QSqlDatabase db = DatabaseManager::instance().database();
|
QSqlDatabase db = DatabaseManager::instance().database();
|
||||||
@@ -588,6 +597,9 @@ QVector<MailItem> MailItemDao::findByFolderIdPaginated(int folderId, int offset,
|
|||||||
if (hasAttachments) {
|
if (hasAttachments) {
|
||||||
sql += " AND hasAttachment = 1";
|
sql += " AND hasAttachment = 1";
|
||||||
}
|
}
|
||||||
|
if (pinnedOnly) {
|
||||||
|
sql += " AND isPinned = 1";
|
||||||
|
}
|
||||||
|
|
||||||
sql += " ORDER BY date DESC LIMIT :limit OFFSET :offset";
|
sql += " ORDER BY date DESC LIMIT :limit OFFSET :offset";
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ public:
|
|||||||
static bool replaceAttachments(qint64 mailItemId, const QVector<StoredAttachmentRecord>& attachments);
|
static bool replaceAttachments(qint64 mailItemId, const QVector<StoredAttachmentRecord>& attachments);
|
||||||
static QVector<StoredAttachmentRecord> attachmentsForMail(qint64 mailItemId);
|
static QVector<StoredAttachmentRecord> attachmentsForMail(qint64 mailItemId);
|
||||||
static int countUnreadByFolderId(int folderId);
|
static int countUnreadByFolderId(int folderId);
|
||||||
static int countAll(const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments);
|
static int countAll(const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments, bool pinnedOnly);
|
||||||
static int countByFolderId(int folderId, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments);
|
static int countByFolderId(int folderId, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments, bool pinnedOnly);
|
||||||
static QVector<MailItem> findAllPaginated(int offset, int limit, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments);
|
static QVector<MailItem> findAllPaginated(int offset, int limit, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments, bool pinnedOnly);
|
||||||
static QVector<MailItem> findByFolderIdPaginated(int folderId, int offset, int limit, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments);
|
static QVector<MailItem> findByFolderIdPaginated(int folderId, int offset, int limit, const QString& searchFilter, bool unreadOnly, bool flaggedOnly, bool hasAttachments, bool pinnedOnly);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QCursor>
|
#include <QCursor>
|
||||||
|
#include <QPainterPath>
|
||||||
|
|
||||||
CompactMailDelegate::CompactMailDelegate(QObject *parent)
|
CompactMailDelegate::CompactMailDelegate(QObject *parent)
|
||||||
: QStyledItemDelegate(parent)
|
: QStyledItemDelegate(parent)
|
||||||
@@ -220,6 +221,21 @@ void CompactMailDelegate::drawCard(QPainter *painter, const QStyleOptionViewItem
|
|||||||
painter->setPen(dateColor);
|
painter->setPen(dateColor);
|
||||||
painter->drawText(QRect(x + senderMaxWidth + 8, line1Y, dateWidth, dateFM.height()), Qt::AlignRight | Qt::AlignVCenter, dateStr);
|
painter->drawText(QRect(x + senderMaxWidth + 8, line1Y, dateWidth, dateFM.height()), Qt::AlignRight | Qt::AlignVCenter, dateStr);
|
||||||
|
|
||||||
|
// Pinned indicator (top-right, next to unread dot)
|
||||||
|
bool isPinned = index.data(EmailListModel::IsPinnedRole).toBool();
|
||||||
|
if (isPinned) {
|
||||||
|
int pinSize = 10;
|
||||||
|
QRect pinRect(cardRect.right() - 12 - pinSize, cardRect.top() + 12, pinSize, pinSize);
|
||||||
|
painter->setBrush(QColor(0xff, 0x95, 0x00)); // orange
|
||||||
|
painter->setPen(Qt::NoPen);
|
||||||
|
// Draw pushpin shape
|
||||||
|
QPainterPath pinPath;
|
||||||
|
pinPath.addEllipse(pinRect.center().x() - 2, pinRect.top() + 2, 4, 4); // head
|
||||||
|
pinPath.moveTo(pinRect.center().x(), pinRect.top() + 6);
|
||||||
|
pinPath.lineTo(pinRect.center().x(), pinRect.bottom() - 2); // shaft
|
||||||
|
painter->drawPath(pinPath);
|
||||||
|
}
|
||||||
|
|
||||||
// Line 2: Subject + Attachment icon
|
// Line 2: Subject + Attachment icon
|
||||||
int line2Y = line1Y + senderFM.height() + 2;
|
int line2Y = line1Y + senderFM.height() + 2;
|
||||||
QString subjectPrefix = "";
|
QString subjectPrefix = "";
|
||||||
|
|||||||
@@ -69,6 +69,10 @@ void MailListView::setupUI()
|
|||||||
m_hasAttachmentsCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }");
|
m_hasAttachmentsCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }");
|
||||||
filterLayout->addWidget(m_hasAttachmentsCheck);
|
filterLayout->addWidget(m_hasAttachmentsCheck);
|
||||||
|
|
||||||
|
m_pinnedOnlyCheck = new QCheckBox("Solo fijados");
|
||||||
|
m_pinnedOnlyCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }");
|
||||||
|
filterLayout->addWidget(m_pinnedOnlyCheck);
|
||||||
|
|
||||||
filterLayout->addStretch();
|
filterLayout->addStretch();
|
||||||
|
|
||||||
// Display mode combo (Compact/Medium/Spacious)
|
// Display mode combo (Compact/Medium/Spacious)
|
||||||
@@ -209,6 +213,7 @@ void MailListView::setupUI()
|
|||||||
connect(m_unreadOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
connect(m_unreadOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
||||||
connect(m_flaggedOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
connect(m_flaggedOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
||||||
connect(m_hasAttachmentsCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
connect(m_hasAttachmentsCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
||||||
|
connect(m_pinnedOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MailListView::setModel(EmailListModel *model)
|
void MailListView::setModel(EmailListModel *model)
|
||||||
@@ -287,6 +292,7 @@ void MailListView::onFilterChanged()
|
|||||||
m_sourceModel->setShowUnreadOnly(m_unreadOnlyCheck->isChecked());
|
m_sourceModel->setShowUnreadOnly(m_unreadOnlyCheck->isChecked());
|
||||||
m_sourceModel->setShowFlaggedOnly(m_flaggedOnlyCheck->isChecked());
|
m_sourceModel->setShowFlaggedOnly(m_flaggedOnlyCheck->isChecked());
|
||||||
m_sourceModel->setShowHasAttachments(m_hasAttachmentsCheck->isChecked());
|
m_sourceModel->setShowHasAttachments(m_hasAttachmentsCheck->isChecked());
|
||||||
|
m_sourceModel->setShowPinnedOnly(m_pinnedOnlyCheck->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MailListView::onGroupByChanged(int index)
|
void MailListView::onGroupByChanged(int index)
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ private:
|
|||||||
QCheckBox *m_unreadOnlyCheck;
|
QCheckBox *m_unreadOnlyCheck;
|
||||||
QCheckBox *m_flaggedOnlyCheck;
|
QCheckBox *m_flaggedOnlyCheck;
|
||||||
QCheckBox *m_hasAttachmentsCheck;
|
QCheckBox *m_hasAttachmentsCheck;
|
||||||
|
QCheckBox *m_pinnedOnlyCheck;
|
||||||
QComboBox *m_groupByCombo;
|
QComboBox *m_groupByCombo;
|
||||||
QComboBox *m_displayModeCombo;
|
QComboBox *m_displayModeCombo;
|
||||||
CompactMailDelegate *m_compactDelegate = nullptr;
|
CompactMailDelegate *m_compactDelegate = nullptr;
|
||||||
|
|||||||
@@ -109,9 +109,9 @@ void EmailListModel::fetchMore(const QModelIndex &parent)
|
|||||||
|
|
||||||
QVector<MailItem> newEmails;
|
QVector<MailItem> newEmails;
|
||||||
if (m_folderId == -1) {
|
if (m_folderId == -1) {
|
||||||
newEmails = MailItemDao::findAllPaginated(offset, fetchCount, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments);
|
newEmails = MailItemDao::findAllPaginated(offset, fetchCount, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments, m_pinnedOnly);
|
||||||
} else {
|
} else {
|
||||||
newEmails = MailItemDao::findByFolderIdPaginated(m_folderId, offset, fetchCount, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments);
|
newEmails = MailItemDao::findByFolderIdPaginated(m_folderId, offset, fetchCount, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments, m_pinnedOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newEmails.isEmpty())
|
if (newEmails.isEmpty())
|
||||||
@@ -139,9 +139,9 @@ void EmailListModel::refresh()
|
|||||||
|
|
||||||
// Get total count
|
// Get total count
|
||||||
if (m_folderId == -1) {
|
if (m_folderId == -1) {
|
||||||
m_totalCount = MailItemDao::countAll(m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments);
|
m_totalCount = MailItemDao::countAll(m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments, m_pinnedOnly);
|
||||||
} else {
|
} else {
|
||||||
m_totalCount = MailItemDao::countByFolderId(m_folderId, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments);
|
m_totalCount = MailItemDao::countByFolderId(m_folderId, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments, m_pinnedOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit totalCountChanged(m_totalCount);
|
emit totalCountChanged(m_totalCount);
|
||||||
@@ -150,9 +150,9 @@ void EmailListModel::refresh()
|
|||||||
int fetchCount = qMin(m_batchSize, m_totalCount);
|
int fetchCount = qMin(m_batchSize, m_totalCount);
|
||||||
QVector<MailItem> firstBatch;
|
QVector<MailItem> firstBatch;
|
||||||
if (m_folderId == -1) {
|
if (m_folderId == -1) {
|
||||||
firstBatch = MailItemDao::findAllPaginated(0, fetchCount, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments);
|
firstBatch = MailItemDao::findAllPaginated(0, fetchCount, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments, m_pinnedOnly);
|
||||||
} else {
|
} else {
|
||||||
firstBatch = MailItemDao::findByFolderIdPaginated(m_folderId, 0, fetchCount, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments);
|
firstBatch = MailItemDao::findByFolderIdPaginated(m_folderId, 0, fetchCount, m_searchFilter, m_unreadOnly, m_flaggedOnly, m_hasAttachments, m_pinnedOnly);
|
||||||
}
|
}
|
||||||
m_emails = firstBatch;
|
m_emails = firstBatch;
|
||||||
m_loadedCount = firstBatch.size();
|
m_loadedCount = firstBatch.size();
|
||||||
@@ -191,4 +191,12 @@ void EmailListModel::setShowHasAttachments(bool show)
|
|||||||
return;
|
return;
|
||||||
m_hasAttachments = show;
|
m_hasAttachments = show;
|
||||||
refresh();
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmailListModel::setShowPinnedOnly(bool show)
|
||||||
|
{
|
||||||
|
if (m_pinnedOnly == show)
|
||||||
|
return;
|
||||||
|
m_pinnedOnly = show;
|
||||||
|
refresh();
|
||||||
}
|
}
|
||||||
@@ -49,8 +49,8 @@ public:
|
|||||||
void setShowUnreadOnly(bool show);
|
void setShowUnreadOnly(bool show);
|
||||||
void setShowFlaggedOnly(bool show);
|
void setShowFlaggedOnly(bool show);
|
||||||
void setShowHasAttachments(bool show);
|
void setShowHasAttachments(bool show);
|
||||||
// Set batch size for pagination
|
// Set filter for pinned emails
|
||||||
void setBatchSize(int size) { m_batchSize = size; }
|
void setShowPinnedOnly(bool show);
|
||||||
|
|
||||||
// Get all emails currently in the model (after filtering)
|
// Get all emails currently in the model (after filtering)
|
||||||
const QVector<MailItem>& emails() const { return m_emails; }
|
const QVector<MailItem>& emails() const { return m_emails; }
|
||||||
@@ -68,6 +68,7 @@ private:
|
|||||||
bool m_unreadOnly{false};
|
bool m_unreadOnly{false};
|
||||||
bool m_flaggedOnly{false};
|
bool m_flaggedOnly{false};
|
||||||
bool m_hasAttachments{false};
|
bool m_hasAttachments{false};
|
||||||
|
bool m_pinnedOnly{false};
|
||||||
int m_batchSize{50}; // emails per fetch
|
int m_batchSize{50}; // emails per fetch
|
||||||
int m_loadedCount{0}; // how many emails currently loaded
|
int m_loadedCount{0}; // how many emails currently loaded
|
||||||
int m_totalCount{0}; // total matching emails in DB
|
int m_totalCount{0}; // total matching emails in DB
|
||||||
|
|||||||
Reference in New Issue
Block a user