#include "ui/maillistview.h" #include #include "ui/models/EmailListModel.h" #include "ui/models/EmailTreeModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui/delegates/CompactMailDelegate.h" MailListView::MailListView(QWidget *parent) : QWidget(parent), m_sourceModel(nullptr), m_multiSelectMode(false) { setupUI(); } void MailListView::setupUI() { QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); // 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); // 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); 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); m_pinnedOnlyCheck = new QCheckBox("Solo fijados"); m_pinnedOnlyCheck->setStyleSheet("QCheckBox { font-size: 12px; color: #333; }"); filterLayout->addWidget(m_pinnedOnlyCheck); filterLayout->addStretch(); // 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::of(&QComboBox::currentIndexChanged), this, &MailListView::onPivotChanged); filterLayout->addWidget(m_pivotCombo); // 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::of(&QComboBox::currentIndexChanged), this, &MailListView::onDisplayModeChanged); filterLayout->addWidget(m_displayModeCombo); // Group by combo m_groupByCombo = new QComboBox(); m_groupByCombo->addItem("Sin agrupación"); m_groupByCombo->addItem("Agrupar por fecha"); m_groupByCombo->addItem("Agrupar por conversación"); m_groupByCombo->addItem("Agrupar por remitente"); 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::of(&QComboBox::currentIndexChanged), this, &MailListView::onGroupByChanged); filterLayout->addWidget(m_groupByCombo); layout->addWidget(filterBar); // 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 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 // View Stack (TreeView for normal, ListView for compact) m_viewStack = new QStackedWidget(); layout->addWidget(m_viewStack, 1); // --- Normal Tree View --- m_treeView = new QTreeView(); m_treeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection); // Multi-selection m_treeView->setAlternatingRowColors(true); m_treeView->header()->setStretchLastSection(false); m_treeView->header()->setSectionsClickable(true); m_treeView->setSortingEnabled(false); m_treeView->setFrameShape(QFrame::NoFrame); m_treeView->setRootIsDecorated(true); m_treeView->setItemsExpandable(true); m_treeView->setUniformRowHeights(true); // Enable drag & drop m_treeView->setDragEnabled(true); m_treeView->setAcceptDrops(true); m_treeView->setDropIndicatorShown(true); m_treeView->setDragDropMode(QAbstractItemView::DragDrop); 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; }" "QHeaderView::section { background-color: #f5f5f7; padding: 8px; border: none; border-bottom: 1px solid #d1d1d6; font-weight: 600; color: #555; }" ); m_treeModel = new EmailTreeModel(this); m_proxyModel = new QSortFilterProxyModel(this); m_proxyModel->setSourceModel(m_treeModel); m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_proxyModel->setFilterKeyColumn(-1); m_treeView->setModel(m_proxyModel); m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColSubject, QHeaderView::Stretch); m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColSender, QHeaderView::Stretch); m_treeView->header()->setSectionResizeMode(EmailTreeModel::ColDate, QHeaderView::ResizeToContents); connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MailListView::onSelectionChanged); connect(m_treeView, &QTreeView::doubleClicked, this, [this](const QModelIndex &index) { if (!index.isValid()) return; QModelIndex proxyIdx = m_proxyModel->mapToSource(index); // 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; } } // Otherwise open mail if (proxyIdx.isValid() && proxyIdx.data(EmailTreeModel::MailIdRole).isValid()) { int mailId = proxyIdx.data(EmailTreeModel::MailIdRole).toInt(); emit emailOpenRequested(mailId); } }); m_treeView->expandAll(); // --- Compact List View --- m_listView = new QListView(); m_listView->setSelectionMode(QAbstractItemView::ExtendedSelection); // Multi-selection m_listView->setUniformItemSizes(true); m_listView->setFrameShape(QFrame::NoFrame); m_listView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Enable drag & drop m_listView->setDragEnabled(true); m_listView->setAcceptDrops(true); m_listView->setDropIndicatorShown(true); m_listView->setDragDropMode(QAbstractItemView::DragDrop); 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); connect(m_compactDelegate, &CompactMailDelegate::replyRequested, this, &MailListView::onCompactReplyRequested); connect(m_compactDelegate, &CompactMailDelegate::forwardRequested, this, &MailListView::onCompactForwardRequested); connect(m_compactDelegate, &CompactMailDelegate::markUnreadRequested, this, &MailListView::onCompactMarkUnreadRequested); // Same proxy model for list view (single column) m_listView->setModel(m_proxyModel); m_listView->setModelColumn(0); // Use first column (subject) connect(m_listView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MailListView::onSelectionChanged); // 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); // 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); connect(m_pinnedOnlyCheck, &QCheckBox::toggled, this, &MailListView::onFilterChanged); } void MailListView::setModel(EmailListModel *model) { m_sourceModel = model; // 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); connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, &MailListView::onRowsInserted); connect(m_sourceModel, &QAbstractItemModel::rowsRemoved, this, &MailListView::refreshViews); 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); // Initial population refreshViews(); } void MailListView::refreshViews() { if (!m_sourceModel) return; // For paginated model, just update the tree model with currently loaded emails const QVector &emails = m_sourceModel->emails(); m_treeModel->setEmails(emails); m_treeView->expandAll(); // List view uses same proxy model, just refresh m_listView->update(); } 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(); } } } QVector MailListView::getSelectedMailIds() const { QVector mailIds; QAbstractItemView *currentView = qobject_cast(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("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()); } void MailListView::onSelectAllToggled(bool checked) { QAbstractItemView *currentView = qobject_cast(m_viewStack->currentWidget()); if (checked) { currentView->selectAll(); } else { currentView->clearSelection(); } } void MailListView::onSearchTextChanged(const QString &text) { m_proxyModel->setFilterFixedString(text); } void MailListView::onFilterChanged() { if (!m_sourceModel) return; m_sourceModel->setShowUnreadOnly(m_unreadOnlyCheck->isChecked()); m_sourceModel->setShowFlaggedOnly(m_flaggedOnlyCheck->isChecked()); m_sourceModel->setShowHasAttachments(m_hasAttachmentsCheck->isChecked()); m_sourceModel->setShowPinnedOnly(m_pinnedOnlyCheck->isChecked()); } void MailListView::onGroupByChanged(int index) { 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; } m_treeView->expandAll(); } void MailListView::onDisplayModeChanged(int index) { CompactMailDelegate::DisplayMode mode = static_cast(index); m_compactDelegate->setDisplayMode(mode); m_listView->update(); } 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 &emails = m_sourceModel->emails(); m_treeModel->setEmails(emails); m_treeView->expandAll(); } } 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(); } 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(); } 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 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 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); } 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); } 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(); }