2026-06-17 22:44:27 +02:00
|
|
|
#include "ui/maillistview.h"
|
|
|
|
|
#include <QDateTime>
|
2026-08-24 01:04:00 +02:00
|
|
|
#include "ui/models/EmailListModel.h"
|
|
|
|
|
#include "ui/models/EmailTreeModel.h"
|
|
|
|
|
#include <QSortFilterProxyModel>
|
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
#include <QPushButton>
|
|
|
|
|
#include <QLabel>
|
|
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QCheckBox>
|
|
|
|
|
#include <QHBoxLayout>
|
|
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QHeaderView>
|
2026-08-24 23:25:13 +02:00
|
|
|
#include <QResizeEvent>
|
|
|
|
|
#include <QListView>
|
|
|
|
|
#include <QStyledItemDelegate>
|
2026-08-25 22:20:06 +02:00
|
|
|
#include <QScrollBar>
|
2026-08-26 02:38:33 +02:00
|
|
|
#include <QToolButton>
|
|
|
|
|
#include <QMenu>
|
2026-08-26 04:05:56 +02:00
|
|
|
#include <QDrag>
|
|
|
|
|
#include <QMimeData>
|
2026-08-26 04:45:10 +02:00
|
|
|
#include <QInputDialog>
|
2026-08-24 23:25:13 +02:00
|
|
|
#include "ui/delegates/CompactMailDelegate.h"
|
2026-06-17 22:44:27 +02:00
|
|
|
|
2026-08-26 02:38:33 +02:00
|
|
|
MailListView::MailListView(QWidget *parent) : QWidget(parent), m_sourceModel(nullptr), m_multiSelectMode(false)
|
2026-08-24 23:25:13 +02:00
|
|
|
{
|
2026-06-17 22:44:27 +02:00
|
|
|
setupUI();
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
void MailListView::setupUI()
|
|
|
|
|
{
|
2026-06-17 22:44:27 +02:00
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
layout->setSpacing(0);
|
|
|
|
|
|
2026-08-23 14:49:58 +02:00
|
|
|
// Search bar
|
|
|
|
|
QWidget *searchBar = new QWidget();
|
|
|
|
|
searchBar->setFixedHeight(44);
|
|
|
|
|
searchBar->setStyleSheet("background-color: #f5f5f7; border-bottom: 1px solid #d1d1d6;");
|
|
|
|
|
QHBoxLayout *searchLayout = new QHBoxLayout(searchBar);
|
|
|
|
|
searchLayout->setContentsMargins(12, 4, 12, 4);
|
|
|
|
|
searchLayout->setSpacing(8);
|
|
|
|
|
|
|
|
|
|
m_searchEdit = new QLineEdit();
|
|
|
|
|
m_searchEdit->setPlaceholderText("Buscar correos…");
|
|
|
|
|
m_searchEdit->setClearButtonEnabled(true);
|
|
|
|
|
m_searchEdit->setStyleSheet(
|
|
|
|
|
"QLineEdit { background: white; border: 1px solid #d1d1d6; border-radius: 6px; "
|
|
|
|
|
"padding: 6px 12px; font-size: 13px; color: #1d1d1f; }"
|
|
|
|
|
"QLineEdit:focus { border: 2px solid #0071e3; }"
|
|
|
|
|
);
|
|
|
|
|
searchLayout->addWidget(m_searchEdit);
|
|
|
|
|
connect(m_searchEdit, &QLineEdit::textChanged, this, &MailListView::onSearchTextChanged);
|
|
|
|
|
|
2026-08-26 04:45:10 +02:00
|
|
|
// Online search button
|
|
|
|
|
m_onlineSearchBtn = new QToolButton();
|
|
|
|
|
m_onlineSearchBtn->setText("🌐 Online");
|
|
|
|
|
m_onlineSearchBtn->setToolTip("Buscar en servidor (IMAP)");
|
|
|
|
|
m_onlineSearchBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
|
|
|
m_onlineSearchBtn->setStyleSheet(
|
|
|
|
|
"QToolButton { background: #e3f2fd; border: 1px solid #90caf9; border-radius: 6px; "
|
|
|
|
|
"padding: 6px 12px; font-size: 12px; color: #1565c0; }"
|
|
|
|
|
"QToolButton:hover { background: #bbdefb; border-color: #42a5f5; }"
|
|
|
|
|
"QToolButton:pressed { background: #90caf9; }"
|
|
|
|
|
);
|
|
|
|
|
connect(m_onlineSearchBtn, &QToolButton::clicked, this, &MailListView::onOnlineSearchClicked);
|
|
|
|
|
searchLayout->addWidget(m_onlineSearchBtn);
|
|
|
|
|
|
2026-08-23 14:49:58 +02:00
|
|
|
layout->addWidget(searchBar);
|
|
|
|
|
|
|
|
|
|
// Filter bar
|
|
|
|
|
QWidget *filterBar = new QWidget();
|
|
|
|
|
filterBar->setFixedHeight(36);
|
|
|
|
|
filterBar->setStyleSheet("background-color: #f5f5f7; border-bottom: 1px solid #d1d1d6;");
|
|
|
|
|
QHBoxLayout *filterLayout = new QHBoxLayout(filterBar);
|
|
|
|
|
filterLayout->setContentsMargins(12, 2, 12, 2);
|
|
|
|
|
filterLayout->setSpacing(12);
|
|
|
|
|
|
|
|
|
|
m_unreadOnlyCheck = new QCheckBox("No leídos");
|
|
|
|
|
m_unreadOnlyCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }");
|
|
|
|
|
filterLayout->addWidget(m_unreadOnlyCheck);
|
|
|
|
|
|
|
|
|
|
m_flaggedOnlyCheck = new QCheckBox("Marcados");
|
|
|
|
|
m_flaggedOnlyCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }");
|
|
|
|
|
filterLayout->addWidget(m_flaggedOnlyCheck);
|
|
|
|
|
|
|
|
|
|
m_hasAttachmentsCheck = new QCheckBox("Con adjuntos");
|
|
|
|
|
m_hasAttachmentsCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }");
|
|
|
|
|
filterLayout->addWidget(m_hasAttachmentsCheck);
|
|
|
|
|
|
2026-08-26 00:04:20 +02:00
|
|
|
m_pinnedOnlyCheck = new QCheckBox("Solo fijados");
|
|
|
|
|
m_pinnedOnlyCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }");
|
|
|
|
|
filterLayout->addWidget(m_pinnedOnlyCheck);
|
|
|
|
|
|
2026-08-23 14:49:58 +02:00
|
|
|
filterLayout->addStretch();
|
|
|
|
|
|
2026-08-26 03:33:10 +02:00
|
|
|
// Pivot control (Focused/Other) - only shown for Inbox folders
|
|
|
|
|
m_pivotCombo = new QComboBox();
|
|
|
|
|
m_pivotCombo->addItem("Principal"); // Focused
|
|
|
|
|
m_pivotCombo->addItem("Otros"); // Other
|
|
|
|
|
m_pivotCombo->setFixedWidth(140);
|
|
|
|
|
m_pivotCombo->setStyleSheet(
|
|
|
|
|
"QComboBox { border: 1px solid #d1d1d6; border-radius: 4px; padding: 4px 8px; "
|
|
|
|
|
"background: white; min-height: 20px; }"
|
|
|
|
|
"QComboBox:hover { border-color: #bdbdbd; }"
|
|
|
|
|
"QComboBox:focus { border-color: #1976D2; }"
|
|
|
|
|
);
|
|
|
|
|
m_pivotCombo->setVisible(false); // Only visible for Inbox
|
|
|
|
|
connect(m_pivotCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
|
|
|
this, &MailListView::onPivotChanged);
|
|
|
|
|
filterLayout->addWidget(m_pivotCombo);
|
|
|
|
|
|
2026-08-25 13:41:43 +02:00
|
|
|
// Display mode combo (Compact/Medium/Spacious)
|
|
|
|
|
m_displayModeCombo = new QComboBox();
|
|
|
|
|
m_displayModeCombo->addItem("Compacto");
|
|
|
|
|
m_displayModeCombo->addItem("Medio");
|
|
|
|
|
m_displayModeCombo->addItem("Espacioso");
|
|
|
|
|
m_displayModeCombo->setFixedWidth(120);
|
|
|
|
|
m_displayModeCombo->setStyleSheet(
|
|
|
|
|
"QComboBox { border: 1px solid #d1d1d6; border-radius: 4px; padding: 4px 8px; "
|
|
|
|
|
"background: white; min-height: 20px; }"
|
|
|
|
|
"QComboBox:hover { border-color: #bdbdbd; }"
|
|
|
|
|
"QComboBox:focus { border-color: #1976D2; }"
|
|
|
|
|
);
|
|
|
|
|
connect(m_displayModeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
|
|
|
this, &MailListView::onDisplayModeChanged);
|
|
|
|
|
filterLayout->addWidget(m_displayModeCombo);
|
|
|
|
|
|
2026-08-24 01:04:00 +02:00
|
|
|
// Group by combo
|
|
|
|
|
m_groupByCombo = new QComboBox();
|
|
|
|
|
m_groupByCombo->addItem("Sin agrupación");
|
|
|
|
|
m_groupByCombo->addItem("Agrupar por fecha");
|
2026-08-25 18:53:51 +02:00
|
|
|
m_groupByCombo->addItem("Agrupar por conversación");
|
|
|
|
|
m_groupByCombo->addItem("Agrupar por remitente");
|
2026-08-24 01:04:00 +02:00
|
|
|
m_groupByCombo->setFixedWidth(180);
|
|
|
|
|
m_groupByCombo->setStyleSheet(
|
|
|
|
|
"QComboBox { border: 1px solid #d1d1d6; border-radius: 4px; padding: 4px 8px; "
|
|
|
|
|
"background: white; min-height: 20px; }"
|
|
|
|
|
"QComboBox:hover { border-color: #bdbdbd; }"
|
|
|
|
|
"QComboBox:focus { border-color: #1976D2; }"
|
|
|
|
|
);
|
|
|
|
|
connect(m_groupByCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
|
|
|
this, &MailListView::onGroupByChanged);
|
|
|
|
|
filterLayout->addWidget(m_groupByCombo);
|
2026-08-23 14:49:58 +02:00
|
|
|
|
|
|
|
|
layout->addWidget(filterBar);
|
|
|
|
|
|
2026-08-26 02:38:33 +02:00
|
|
|
// Action bar (hidden by default, shown when multi-select mode active)
|
|
|
|
|
m_actionBar = new QWidget();
|
|
|
|
|
m_actionBar->setFixedHeight(44);
|
|
|
|
|
m_actionBar->setStyleSheet("background-color: #e3f2fd; border-bottom: 1px solid #bbdefb;");
|
|
|
|
|
m_actionBar->setVisible(false);
|
|
|
|
|
QHBoxLayout *actionBarLayout = new QHBoxLayout(m_actionBar);
|
|
|
|
|
actionBarLayout->setContentsMargins(12, 4, 12, 4);
|
|
|
|
|
actionBarLayout->setSpacing(8);
|
|
|
|
|
|
|
|
|
|
m_selectAllCheck = new QCheckBox("Seleccionar todo");
|
|
|
|
|
m_selectAllCheck->setStyleSheet("QCheckBox { font-size: 13px; color: #1d1d1f; }");
|
|
|
|
|
connect(m_selectAllCheck, &QCheckBox::toggled, this, &MailListView::onSelectAllToggled);
|
|
|
|
|
actionBarLayout->addWidget(m_selectAllCheck);
|
|
|
|
|
|
|
|
|
|
QLabel *selectionLabel = new QLabel("0 elementos seleccionados");
|
|
|
|
|
selectionLabel->setStyleSheet("font-size: 13px; color: #1d1d1f; font-weight: 500;");
|
|
|
|
|
selectionLabel->setObjectName("selectionLabel");
|
|
|
|
|
actionBarLayout->addWidget(selectionLabel);
|
|
|
|
|
|
|
|
|
|
actionBarLayout->addStretch();
|
|
|
|
|
|
|
|
|
|
// Action buttons
|
|
|
|
|
auto createActionButton = [&](const QString& text, std::function<void()> slot) -> QToolButton* {
|
|
|
|
|
QToolButton *btn = new QToolButton();
|
|
|
|
|
btn->setText(text);
|
|
|
|
|
btn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
|
|
|
|
btn->setStyleSheet(
|
|
|
|
|
"QToolButton { background: white; border: 1px solid #d1d1d6; border-radius: 6px; "
|
|
|
|
|
"padding: 6px 12px; font-size: 12px; color: #1d1d1f; }"
|
|
|
|
|
"QToolButton:hover { background: #f0f0f5; border-color: #bdbdbd; }"
|
|
|
|
|
"QToolButton:pressed { background: #e0e0e5; }"
|
|
|
|
|
);
|
|
|
|
|
connect(btn, &QToolButton::clicked, this, slot);
|
|
|
|
|
return btn;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
QToolButton *btnFlag = createActionButton("★ Marcar", [this]() { onBatchFlagClicked(); });
|
|
|
|
|
actionBarLayout->addWidget(btnFlag);
|
|
|
|
|
|
|
|
|
|
QToolButton *btnUnread = createActionButton("✎ No leído", [this]() { onBatchMarkUnreadClicked(); });
|
|
|
|
|
actionBarLayout->addWidget(btnUnread);
|
|
|
|
|
|
|
|
|
|
QToolButton *btnMove = createActionButton("↗ Mover", [this]() { onBatchMoveClicked(); });
|
|
|
|
|
actionBarLayout->addWidget(btnMove);
|
|
|
|
|
|
|
|
|
|
QToolButton *btnDelete = createActionButton("🗑 Borrar", [this]() { onBatchDeleteClicked(); });
|
|
|
|
|
btnDelete->setStyleSheet(btnDelete->styleSheet() + "QToolButton { color: #ff3b30; border-color: #ff3b30; }");
|
|
|
|
|
actionBarLayout->addWidget(btnDelete);
|
|
|
|
|
|
|
|
|
|
QToolButton *btnExit = new QToolButton();
|
|
|
|
|
btnExit->setText("✕ Salir selección");
|
|
|
|
|
btnExit->setToolButtonStyle(Qt::ToolButtonTextOnly);
|
|
|
|
|
btnExit->setStyleSheet(
|
|
|
|
|
"QToolButton { padding: 6px 12px; font-size: 12px; color: #0071e3; }"
|
|
|
|
|
"QToolButton:hover { background: #e8f0fe; border-radius: 6px; }"
|
|
|
|
|
);
|
|
|
|
|
connect(btnExit, &QToolButton::clicked, this, [this]() { exitMultiSelectMode(); });
|
|
|
|
|
actionBarLayout->addWidget(btnExit);
|
|
|
|
|
|
|
|
|
|
layout->insertWidget(1, m_actionBar); // Insert after search bar
|
|
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
// View Stack (TreeView for normal, ListView for compact)
|
|
|
|
|
m_viewStack = new QStackedWidget();
|
|
|
|
|
layout->addWidget(m_viewStack, 1);
|
|
|
|
|
|
|
|
|
|
// --- Normal Tree View ---
|
2026-08-24 01:04:00 +02:00
|
|
|
m_treeView = new QTreeView();
|
|
|
|
|
m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
2026-08-26 02:38:33 +02:00
|
|
|
m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); // Multi-selection
|
2026-08-24 01:04:00 +02:00
|
|
|
m_treeView->setAlternatingRowColors(true);
|
|
|
|
|
m_treeView->header()->setStretchLastSection(false);
|
|
|
|
|
m_treeView->header()->setSectionsClickable(true);
|
2026-08-24 23:25:13 +02:00
|
|
|
m_treeView->setSortingEnabled(false);
|
2026-08-24 01:04:00 +02:00
|
|
|
m_treeView->setFrameShape(QFrame::NoFrame);
|
|
|
|
|
m_treeView->setRootIsDecorated(true);
|
|
|
|
|
m_treeView->setItemsExpandable(true);
|
|
|
|
|
m_treeView->setUniformRowHeights(true);
|
2026-08-26 04:05:56 +02:00
|
|
|
// Enable drag & drop
|
|
|
|
|
m_treeView->setDragEnabled(true);
|
|
|
|
|
m_treeView->setAcceptDrops(true);
|
|
|
|
|
m_treeView->setDropIndicatorShown(true);
|
|
|
|
|
m_treeView->setDragDropMode(QAbstractItemView::DragDrop);
|
2026-08-24 01:04:00 +02:00
|
|
|
m_treeView->setStyleSheet(
|
|
|
|
|
"QTreeView { background-color: #ffffff; alternate-background-color: #f9f9fb; border: none; }"
|
|
|
|
|
"QTreeView::item { padding: 8px; border-bottom: 1px solid #e8e8ed; }"
|
|
|
|
|
"QTreeView::item:selected { background-color: #e3f2fd; color: #1a1a2e; }"
|
|
|
|
|
"QTreeView::branch { background: transparent; }"
|
2026-06-17 22:44:27 +02:00
|
|
|
"QHeaderView::section { background-color: #f5f5f7; padding: 8px; border: none; border-bottom: 1px solid #d1d1d6; font-weight: 600; color: #555; }"
|
|
|
|
|
);
|
|
|
|
|
|
2026-08-24 01:04:00 +02:00
|
|
|
m_treeModel = new EmailTreeModel(this);
|
2026-06-17 22:44:27 +02:00
|
|
|
m_proxyModel = new QSortFilterProxyModel(this);
|
2026-08-24 01:04:00 +02:00
|
|
|
m_proxyModel->setSourceModel(m_treeModel);
|
2026-08-23 14:49:58 +02:00
|
|
|
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
2026-08-24 23:25:13 +02:00
|
|
|
m_proxyModel->setFilterKeyColumn(-1);
|
2026-06-17 22:44:27 +02:00
|
|
|
|
2026-08-24 01:04:00 +02:00
|
|
|
m_treeView->setModel(m_proxyModel);
|
2026-06-17 22:44:27 +02:00
|
|
|
|
2026-08-24 01:04:00 +02:00
|
|
|
m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColSubject, QHeaderView::Stretch);
|
|
|
|
|
m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColSender, QHeaderView::Stretch);
|
|
|
|
|
m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColDate, QHeaderView::ResizeToContents);
|
|
|
|
|
|
2026-08-26 02:38:33 +02:00
|
|
|
connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
|
|
|
|
|
this, &MailListView::onSelectionChanged);
|
2026-08-24 01:04:00 +02:00
|
|
|
connect(m_treeView, &QTreeView::doubleClicked, this, [this](const QModelIndex &index) {
|
2026-06-18 18:58:27 +02:00
|
|
|
if (!index.isValid()) return;
|
2026-08-24 01:04:00 +02:00
|
|
|
QModelIndex proxyIdx = m_proxyModel->mapToSource(index);
|
2026-08-26 02:38:33 +02:00
|
|
|
|
2026-08-25 18:53:51 +02:00
|
|
|
// Check if it's a thread node - toggle expansion
|
|
|
|
|
if (proxyIdx.isValid() && proxyIdx.data(EmailTreeModel::IsThreadRole).toBool()) {
|
|
|
|
|
QString threadId = proxyIdx.data(EmailTreeModel::ThreadIdRole).toString();
|
|
|
|
|
if (!threadId.isEmpty()) {
|
|
|
|
|
bool expanded = proxyIdx.data(EmailTreeModel::IsThreadExpandedRole).toBool();
|
|
|
|
|
m_treeModel->setThreadExpanded(threadId, !expanded);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-08-26 02:38:33 +02:00
|
|
|
|
2026-08-25 18:53:51 +02:00
|
|
|
// Otherwise open mail
|
2026-08-24 01:04:00 +02:00
|
|
|
if (proxyIdx.isValid() && proxyIdx.data(EmailTreeModel::MailIdRole).isValid()) {
|
|
|
|
|
int mailId = proxyIdx.data(EmailTreeModel::MailIdRole).toInt();
|
|
|
|
|
emit emailOpenRequested(mailId);
|
|
|
|
|
}
|
2026-06-18 18:58:27 +02:00
|
|
|
});
|
2026-06-17 22:44:27 +02:00
|
|
|
|
2026-08-24 01:04:00 +02:00
|
|
|
m_treeView->expandAll();
|
|
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
// --- Compact List View ---
|
|
|
|
|
m_listView = new QListView();
|
2026-08-26 02:38:33 +02:00
|
|
|
m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection); // Multi-selection
|
2026-08-24 23:25:13 +02:00
|
|
|
m_listView->setUniformItemSizes(true);
|
|
|
|
|
m_listView->setFrameShape(QFrame::NoFrame);
|
|
|
|
|
m_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
2026-08-26 04:05:56 +02:00
|
|
|
// Enable drag & drop
|
|
|
|
|
m_listView->setDragEnabled(true);
|
|
|
|
|
m_listView->setAcceptDrops(true);
|
|
|
|
|
m_listView->setDropIndicatorShown(true);
|
|
|
|
|
m_listView->setDragDropMode(QAbstractItemView::DragDrop);
|
2026-08-24 23:25:13 +02:00
|
|
|
m_listView->setStyleSheet(
|
|
|
|
|
"QListView { background-color: #ffffff; border: none; outline: none; }"
|
|
|
|
|
"QListView::item { border: none; }"
|
|
|
|
|
"QListView::item:selected { background: transparent; }"
|
|
|
|
|
"QListView::item:hover { background: transparent; }"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
m_compactDelegate = new CompactMailDelegate(this);
|
|
|
|
|
m_listView->setItemDelegate(m_compactDelegate);
|
|
|
|
|
|
|
|
|
|
// Connect compact delegate signals
|
|
|
|
|
connect(m_compactDelegate, &CompactMailDelegate::flagRequested,
|
|
|
|
|
this, &MailListView::onCompactFlagRequested);
|
|
|
|
|
connect(m_compactDelegate, &CompactMailDelegate::deleteRequested,
|
|
|
|
|
this, &MailListView::onCompactDeleteRequested);
|
|
|
|
|
connect(m_compactDelegate, &CompactMailDelegate::categoryRequested,
|
|
|
|
|
this, &MailListView::onCompactCategoryRequested);
|
|
|
|
|
connect(m_compactDelegate, &CompactMailDelegate::moreRequested,
|
|
|
|
|
this, &MailListView::onCompactMoreRequested);
|
|
|
|
|
connect(m_compactDelegate, &CompactMailDelegate::mailClicked,
|
|
|
|
|
this, &MailListView::onCompactMailClicked);
|
2026-08-26 02:38:33 +02:00
|
|
|
connect(m_compactDelegate, &CompactMailDelegate::replyRequested,
|
|
|
|
|
this, &MailListView::onCompactReplyRequested);
|
|
|
|
|
connect(m_compactDelegate, &CompactMailDelegate::forwardRequested,
|
|
|
|
|
this, &MailListView::onCompactForwardRequested);
|
|
|
|
|
connect(m_compactDelegate, &CompactMailDelegate::markUnreadRequested,
|
|
|
|
|
this, &MailListView::onCompactMarkUnreadRequested);
|
2026-08-24 23:25:13 +02:00
|
|
|
|
|
|
|
|
// Same proxy model for list view (single column)
|
|
|
|
|
m_listView->setModel(m_proxyModel);
|
|
|
|
|
m_listView->setModelColumn(0); // Use first column (subject)
|
|
|
|
|
|
2026-08-26 02:38:33 +02:00
|
|
|
connect(m_listView->selectionModel(), &QItemSelectionModel::selectionChanged,
|
|
|
|
|
this, &MailListView::onSelectionChanged);
|
|
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
// Add both views to stack
|
|
|
|
|
m_viewStack->addWidget(m_treeView); // index 0 = normal
|
|
|
|
|
m_viewStack->addWidget(m_listView); // index 1 = compact
|
|
|
|
|
|
|
|
|
|
// Show normal by default
|
|
|
|
|
m_viewStack->setCurrentIndex(0);
|
2026-08-24 01:04:00 +02:00
|
|
|
|
|
|
|
|
// Connect filter checkboxes
|
|
|
|
|
connect(m_unreadOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
|
|
|
|
connect(m_flaggedOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
|
|
|
|
connect(m_hasAttachmentsCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
2026-08-26 00:04:20 +02:00
|
|
|
connect(m_pinnedOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged);
|
2026-06-17 22:44:27 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
void MailListView::setModel(EmailListModel *model)
|
|
|
|
|
{
|
2026-08-24 01:04:00 +02:00
|
|
|
m_sourceModel = model;
|
2026-08-24 23:25:13 +02:00
|
|
|
|
|
|
|
|
// Connect to source model changes to refresh both views
|
|
|
|
|
connect(m_sourceModel, &QAbstractItemModel::modelReset, this, &MailListView::refreshViews);
|
|
|
|
|
connect(m_sourceModel, &QAbstractItemModel::layoutChanged, this, &MailListView::refreshViews);
|
2026-08-25 22:20:06 +02:00
|
|
|
connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, &MailListView::onRowsInserted);
|
2026-08-24 23:25:13 +02:00
|
|
|
connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, this, &MailListView::refreshViews);
|
2026-08-25 22:20:06 +02:00
|
|
|
connect(m_sourceModel, &EmailListModel::totalCountChanged, this, &MailListView::onTotalCountChanged);
|
|
|
|
|
|
|
|
|
|
// Connect tree view scroll for fetchMore
|
|
|
|
|
connect(m_treeView->verticalScrollBar(), &QScrollBar::valueChanged, this, &MailListView::onTreeViewScrolled);
|
|
|
|
|
connect(m_listView->verticalScrollBar(), &QScrollBar::valueChanged, this, &MailListView::onListViewScrolled);
|
2026-08-24 23:25:13 +02:00
|
|
|
|
2026-08-24 01:04:00 +02:00
|
|
|
// Initial population
|
2026-08-24 23:25:13 +02:00
|
|
|
refreshViews();
|
2026-08-24 01:04:00 +02:00
|
|
|
}
|
2026-06-17 22:44:27 +02:00
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
void MailListView::refreshViews()
|
|
|
|
|
{
|
2026-08-24 01:04:00 +02:00
|
|
|
if (!m_sourceModel) return;
|
2026-08-24 23:25:13 +02:00
|
|
|
|
2026-08-25 22:20:06 +02:00
|
|
|
// For paginated model, just update the tree model with currently loaded emails
|
2026-08-24 01:04:00 +02:00
|
|
|
const QVector<MailItem> &emails = m_sourceModel->emails();
|
|
|
|
|
m_treeModel->setEmails(emails);
|
|
|
|
|
m_treeView->expandAll();
|
2026-08-24 23:25:13 +02:00
|
|
|
|
|
|
|
|
// List view uses same proxy model, just refresh
|
|
|
|
|
m_listView->update();
|
2026-06-17 22:44:27 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
void MailListView::resizeEvent(QResizeEvent *event)
|
|
|
|
|
{
|
|
|
|
|
QWidget::resizeEvent(event);
|
|
|
|
|
updateViewMode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::updateViewMode()
|
|
|
|
|
{
|
|
|
|
|
int viewportWidth = m_viewStack->width(); // available width
|
|
|
|
|
bool wantCompact = (viewportWidth < m_compactThreshold);
|
|
|
|
|
|
|
|
|
|
if (wantCompact != m_compactMode) {
|
|
|
|
|
m_compactMode = wantCompact;
|
|
|
|
|
m_viewStack->setCurrentIndex(m_compactMode ? 1 : 0);
|
|
|
|
|
|
|
|
|
|
if (m_compactMode) {
|
|
|
|
|
m_treeView->header()->hide();
|
|
|
|
|
} else {
|
|
|
|
|
m_treeView->header()->show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-26 02:38:33 +02:00
|
|
|
QVector<int> MailListView::getSelectedMailIds() const
|
2026-08-24 23:25:13 +02:00
|
|
|
{
|
2026-08-26 02:38:33 +02:00
|
|
|
QVector<int> mailIds;
|
|
|
|
|
QAbstractItemView *currentView = qobject_cast<QAbstractItemView*>(m_viewStack->currentWidget());
|
|
|
|
|
QItemSelectionModel *selectionModel = currentView->selectionModel();
|
|
|
|
|
|
|
|
|
|
if (!selectionModel) return mailIds;
|
|
|
|
|
|
|
|
|
|
QModelIndexList selectedIndexes = selectionModel->selectedRows();
|
|
|
|
|
for (const QModelIndex &proxyIdx : selectedIndexes) {
|
|
|
|
|
QModelIndex sourceIdx = m_proxyModel->mapToSource(proxyIdx);
|
|
|
|
|
if (sourceIdx.isValid() && sourceIdx.data(EmailTreeModel::MailIdRole).isValid()) {
|
|
|
|
|
int mailId = sourceIdx.data(EmailTreeModel::MailIdRole).toInt();
|
|
|
|
|
mailIds.append(mailId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return mailIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::updateActionBar()
|
|
|
|
|
{
|
|
|
|
|
QLabel *label = m_actionBar->findChild<QLabel*>("selectionLabel");
|
|
|
|
|
if (label) {
|
|
|
|
|
label->setText(QString("%1 elementos seleccionados").arg(m_selectedMailIds.size()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_selectAllCheck->blockSignals(true);
|
|
|
|
|
m_selectAllCheck->setChecked(m_selectedMailIds.size() > 0);
|
|
|
|
|
m_selectAllCheck->blockSignals(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onSelectionChanged()
|
|
|
|
|
{
|
|
|
|
|
m_selectedMailIds = getSelectedMailIds();
|
|
|
|
|
bool hasSelection = !m_selectedMailIds.isEmpty();
|
|
|
|
|
|
|
|
|
|
if (hasSelection && !m_multiSelectMode) {
|
|
|
|
|
enterMultiSelectMode();
|
|
|
|
|
} else if (!hasSelection && m_multiSelectMode) {
|
|
|
|
|
exitMultiSelectMode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateActionBar();
|
|
|
|
|
emit selectionChanged(m_selectedMailIds);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::enterMultiSelectMode()
|
|
|
|
|
{
|
|
|
|
|
m_multiSelectMode = true;
|
|
|
|
|
m_actionBar->setVisible(true);
|
|
|
|
|
updateActionBar();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::exitMultiSelectMode()
|
|
|
|
|
{
|
|
|
|
|
m_multiSelectMode = false;
|
|
|
|
|
m_selectedMailIds.clear();
|
|
|
|
|
m_actionBar->setVisible(false);
|
|
|
|
|
|
|
|
|
|
// Clear selections
|
|
|
|
|
m_treeView->clearSelection();
|
|
|
|
|
m_listView->clearSelection();
|
|
|
|
|
m_selectAllCheck->blockSignals(true);
|
|
|
|
|
m_selectAllCheck->setChecked(false);
|
|
|
|
|
m_selectAllCheck->blockSignals(false);
|
|
|
|
|
updateActionBar();
|
|
|
|
|
emit selectionChanged(QVector<int>());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onSelectAllToggled(bool checked)
|
|
|
|
|
{
|
|
|
|
|
QAbstractItemView *currentView = qobject_cast<QAbstractItemView*>(m_viewStack->currentWidget());
|
|
|
|
|
if (checked) {
|
|
|
|
|
currentView->selectAll();
|
|
|
|
|
} else {
|
|
|
|
|
currentView->clearSelection();
|
2026-08-24 01:04:00 +02:00
|
|
|
}
|
2026-08-23 14:49:58 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
void MailListView::onSearchTextChanged(const QString &text)
|
|
|
|
|
{
|
2026-08-24 01:04:00 +02:00
|
|
|
m_proxyModel->setFilterFixedString(text);
|
2026-08-23 14:49:58 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
void MailListView::onFilterChanged()
|
|
|
|
|
{
|
2026-08-24 01:04:00 +02:00
|
|
|
if (!m_sourceModel) return;
|
|
|
|
|
m_sourceModel->setShowUnreadOnly(m_unreadOnlyCheck->isChecked());
|
|
|
|
|
m_sourceModel->setShowFlaggedOnly(m_flaggedOnlyCheck->isChecked());
|
|
|
|
|
m_sourceModel->setShowHasAttachments(m_hasAttachmentsCheck->isChecked());
|
2026-08-26 00:04:20 +02:00
|
|
|
m_sourceModel->setShowPinnedOnly(m_pinnedOnlyCheck->isChecked());
|
2026-08-24 01:04:00 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-24 23:25:13 +02:00
|
|
|
void MailListView::onGroupByChanged(int index)
|
|
|
|
|
{
|
2026-08-25 18:53:51 +02:00
|
|
|
switch (index) {
|
|
|
|
|
case 0: // Sin agrupación
|
|
|
|
|
m_treeModel->setGroupMode(EmailTreeModel::NoGrouping);
|
|
|
|
|
break;
|
|
|
|
|
case 1: // Agrupar por fecha
|
|
|
|
|
m_treeModel->setGroupMode(EmailTreeModel::GroupByDate);
|
|
|
|
|
break;
|
|
|
|
|
case 2: // Agrupar por conversación/hilo
|
|
|
|
|
m_treeModel->setGroupMode(EmailTreeModel::GroupByThread);
|
|
|
|
|
break;
|
|
|
|
|
case 3: // Agrupar por remitente
|
|
|
|
|
m_treeModel->setGroupMode(EmailTreeModel::GroupBySender);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-08-24 01:04:00 +02:00
|
|
|
m_treeView->expandAll();
|
2026-08-24 23:25:13 +02:00
|
|
|
}
|
|
|
|
|
|
2026-08-25 13:41:43 +02:00
|
|
|
void MailListView::onDisplayModeChanged(int index)
|
|
|
|
|
{
|
|
|
|
|
CompactMailDelegate::DisplayMode mode = static_cast<CompactMailDelegate::DisplayMode>(index);
|
|
|
|
|
m_compactDelegate->setDisplayMode(mode);
|
|
|
|
|
m_listView->update();
|
2026-08-25 22:20:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onTotalCountChanged(int count)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(count);
|
|
|
|
|
// Could update a status label showing "X of Y emails"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onTreeViewScrolled(int value)
|
|
|
|
|
{
|
|
|
|
|
if (!m_sourceModel) return;
|
|
|
|
|
QScrollBar* scrollBar = m_treeView->verticalScrollBar();
|
|
|
|
|
int maxValue = scrollBar->maximum();
|
|
|
|
|
if (maxValue > 0 && value >= maxValue * 0.9) { // 90% threshold
|
|
|
|
|
m_sourceModel->fetchMore(QModelIndex());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onListViewScrolled(int value)
|
|
|
|
|
{
|
|
|
|
|
if (!m_sourceModel) return;
|
|
|
|
|
QScrollBar* scrollBar = m_listView->verticalScrollBar();
|
|
|
|
|
int maxValue = scrollBar->maximum();
|
|
|
|
|
if (maxValue > 0 && value >= maxValue * 0.9) { // 90% threshold
|
|
|
|
|
m_sourceModel->fetchMore(QModelIndex());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onRowsInserted(const QModelIndex &parent, int first, int last)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(parent);
|
|
|
|
|
Q_UNUSED(first);
|
|
|
|
|
Q_UNUSED(last);
|
|
|
|
|
// New emails loaded via fetchMore - refresh tree model
|
|
|
|
|
if (m_sourceModel) {
|
|
|
|
|
const QVector<MailItem> &emails = m_sourceModel->emails();
|
|
|
|
|
m_treeModel->setEmails(emails);
|
|
|
|
|
m_treeView->expandAll();
|
|
|
|
|
}
|
2026-08-26 02:38:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onRowSelected(const QModelIndex &index)
|
|
|
|
|
{
|
|
|
|
|
if (!index.isValid()) return;
|
|
|
|
|
QModelIndex proxyIdx = m_proxyModel->mapToSource(index);
|
|
|
|
|
if (proxyIdx.isValid() && proxyIdx.data(EmailTreeModel::MailIdRole).isValid()) {
|
|
|
|
|
int mailId = proxyIdx.data(EmailTreeModel::MailIdRole).toInt();
|
|
|
|
|
emit emailSelected(mailId);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onCompactFlagRequested(int mailId)
|
|
|
|
|
{
|
|
|
|
|
emit flagRequested(mailId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onCompactDeleteRequested(int mailId)
|
|
|
|
|
{
|
|
|
|
|
emit deleteRequested(mailId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onCompactCategoryRequested(int mailId)
|
|
|
|
|
{
|
|
|
|
|
emit categoryRequested(mailId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onCompactMoreRequested(int mailId, const QPoint &globalPos)
|
|
|
|
|
{
|
|
|
|
|
emit moreRequested(mailId, globalPos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onCompactMailClicked(int mailId)
|
|
|
|
|
{
|
|
|
|
|
emit emailSelected(mailId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onCompactReplyRequested(int mailId)
|
|
|
|
|
{
|
|
|
|
|
emit replyRequested(mailId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onCompactForwardRequested(int mailId)
|
|
|
|
|
{
|
|
|
|
|
emit forwardRequested(mailId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onCompactMarkUnreadRequested(int mailId)
|
|
|
|
|
{
|
|
|
|
|
emit markUnreadRequested(mailId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Batch action slots
|
|
|
|
|
void MailListView::onBatchFlagClicked()
|
|
|
|
|
{
|
|
|
|
|
if (m_selectedMailIds.isEmpty()) return;
|
|
|
|
|
emit batchFlagRequested(m_selectedMailIds, true);
|
|
|
|
|
exitMultiSelectMode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onBatchMarkUnreadClicked()
|
|
|
|
|
{
|
|
|
|
|
if (m_selectedMailIds.isEmpty()) return;
|
|
|
|
|
emit batchMarkUnreadRequested(m_selectedMailIds);
|
|
|
|
|
exitMultiSelectMode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onBatchMoveClicked()
|
|
|
|
|
{
|
|
|
|
|
if (m_selectedMailIds.isEmpty()) return;
|
|
|
|
|
// TODO: Show folder picker dialog
|
|
|
|
|
// For now, just emit signal
|
|
|
|
|
// emit batchMoveRequested(m_selectedMailIds, targetFolderId);
|
|
|
|
|
exitMultiSelectMode();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onBatchDeleteClicked()
|
|
|
|
|
{
|
|
|
|
|
if (m_selectedMailIds.isEmpty()) return;
|
|
|
|
|
emit batchDeleteRequested(m_selectedMailIds);
|
|
|
|
|
exitMultiSelectMode();
|
2026-08-26 03:33:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::setFolderType(const QString& folderType)
|
|
|
|
|
{
|
|
|
|
|
m_pivotCombo->setVisible(folderType == "inbox");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::onPivotChanged(int index)
|
|
|
|
|
{
|
|
|
|
|
// 0 = Focused (Principal), 1 = Other (Otros)
|
|
|
|
|
// TODO: Implement pivot filtering logic
|
|
|
|
|
// For now, just update the model filter
|
|
|
|
|
if (!m_sourceModel) return;
|
|
|
|
|
|
|
|
|
|
if (index == 0) {
|
|
|
|
|
// Focused: important emails (could be based on sender frequency, AI, etc.)
|
|
|
|
|
// m_sourceModel->setShowFocusedOnly(true);
|
|
|
|
|
} else {
|
|
|
|
|
// Other: promotional, notifications, etc.
|
|
|
|
|
// m_sourceModel->setShowFocusedOnly(false);
|
|
|
|
|
}
|
|
|
|
|
refreshViews();
|
2026-08-26 04:05:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::dragEnterEvent(QDragEnterEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (event->mimeData()->hasFormat("application/x-wino-mail-ids")) {
|
|
|
|
|
event->acceptProposedAction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::dragMoveEvent(QDragMoveEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (event->mimeData()->hasFormat("application/x-wino-mail-ids")) {
|
|
|
|
|
event->acceptProposedAction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::dropEvent(QDropEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (event->mimeData()->hasFormat("application/x-wino-mail-ids")) {
|
|
|
|
|
QByteArray data = event->mimeData()->data("application/x-wino-mail-ids");
|
|
|
|
|
QDataStream stream(&data, QIODevice::ReadOnly);
|
|
|
|
|
QVector<int> mailIds;
|
|
|
|
|
stream >> mailIds;
|
|
|
|
|
|
|
|
|
|
if (!mailIds.isEmpty()) {
|
|
|
|
|
// Get target folder from the folder tree (via MainMainWindow)
|
|
|
|
|
// For now, emit signal with mailIds - the target folder will be handled by MainMainWindow
|
|
|
|
|
emit batchMoveRequested(mailIds, -1); // -1 = target folder from context
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
event->acceptProposedAction();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::initDrag()
|
|
|
|
|
{
|
|
|
|
|
QVector<int> mailIds = getSelectedMailIds();
|
|
|
|
|
if (mailIds.isEmpty()) return;
|
|
|
|
|
|
|
|
|
|
QMimeData *mimeData = new QMimeData();
|
|
|
|
|
QByteArray data;
|
|
|
|
|
QDataStream stream(&data, QIODevice::WriteOnly);
|
|
|
|
|
stream << mailIds;
|
|
|
|
|
mimeData->setData("application/x-wino-mail-ids", data);
|
|
|
|
|
|
|
|
|
|
QDrag *drag = new QDrag(this);
|
|
|
|
|
drag->setMimeData(mimeData);
|
|
|
|
|
|
|
|
|
|
// Optional: set drag pixmap
|
|
|
|
|
// drag->setPixmap(createDragPixmap(mailIds));
|
|
|
|
|
|
|
|
|
|
Qt::DropAction dropAction = drag->exec(Qt::MoveAction);
|
|
|
|
|
|
|
|
|
|
Q_UNUSED(dropAction);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-26 04:45:10 +02:00
|
|
|
void MailListView::onOnlineSearchClicked()
|
|
|
|
|
{
|
|
|
|
|
QString query = m_searchEdit->text().trimmed();
|
|
|
|
|
if (query.isEmpty()) {
|
|
|
|
|
// If search box is empty, show a dialog to enter search query
|
|
|
|
|
bool ok;
|
|
|
|
|
query = QInputDialog::getText(this, "Buscar en servidor",
|
|
|
|
|
"Introduce términos de búsqueda:",
|
|
|
|
|
QLineEdit::Normal,
|
|
|
|
|
"", &ok);
|
|
|
|
|
if (!ok || query.trimmed().isEmpty()) return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit onlineSearchRequested(query);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-26 04:05:56 +02:00
|
|
|
void MailListView::mousePressEvent(QMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (event->button() == Qt::LeftButton) {
|
|
|
|
|
// Start drag distance timer
|
|
|
|
|
m_dragStartPosition = event->pos();
|
|
|
|
|
}
|
|
|
|
|
QWidget::mousePressEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MailListView::mouseMoveEvent(QMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (!(event->buttons() & Qt::LeftButton))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if ((event->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
initDrag();
|
2026-06-17 22:44:27 +02:00
|
|
|
}
|