diff --git a/src/ui/mainmainwindow.cpp b/src/ui/mainmainwindow.cpp index d0e1ee1..9a7cf72 100644 --- a/src/ui/mainmainwindow.cpp +++ b/src/ui/mainmainwindow.cpp @@ -22,6 +22,11 @@ #include #include #include +#include +#include +#include +#include +#include #ifdef Q_OS_WIN #include #endif @@ -57,12 +62,15 @@ void MainMainWindow::setupUI() { qDebug() << "[MainMainWindow::setupUI] Start"; // === Global stylesheet === - this->setStyleSheet( + /*this->setStyleSheet( "QMainWindow { background-color: #f5f5f7; }" "QSplitter::handle { background-color: #d1d1d6; width: 1px; }" "QTreeView { background-color: #ffffff; border: none; font-family: 'Segoe UI', Helvetica; font-size: 13px; }" "QToolBar { background-color: #f5f5f7; border-bottom: 1px solid #d1d1d6; spacing: 10px; }" - ); + + +, k + );*/ qDebug() << "[MainMainWindow::setupUI] Stylesheet set"; createTitleBar(); @@ -71,23 +79,23 @@ void MainMainWindow::setupUI() // === Central widget === QWidget *central = new QWidget(); central->setObjectName("CentralWidget"); - central->setStyleSheet( + /*central->setStyleSheet( "QWidget#CentralWidget {" " background-color: #f5f5f7;" " border-bottom-left-radius: 8px;" " border-bottom-right-radius: 8px;" "}" - ); + );*/ // Install event filter to handle resize from window edges central->installEventFilter(this); QHBoxLayout *centralLayout = new QHBoxLayout(central); centralLayout->setContentsMargins(0, 0, 0, 0); centralLayout->setSpacing(0); - // === Sidebar === - setupSidebar(); - qDebug() << "[MainMainWindow::setupUI] Sidebar created"; - centralLayout->addWidget(m_sidebar); + // === Left navigator panel (always visible) === + setupNavigator(); + qDebug() << "[MainMainWindow::setupUI] Navigator created"; + centralLayout->addWidget(m_navigatorPanel); // === Separator line === QFrame *separator = new QFrame(); @@ -187,28 +195,120 @@ void MainMainWindow::setupUI() qDebug() << "[MainMainWindow::setupUI] Done"; } -void MainMainWindow::setupSidebar() +void MainMainWindow::setupNavigator() { - m_sidebar = new QListWidget(); - m_sidebar->setFixedWidth(100); - m_sidebar->setIconSize(QSize(24, 24)); - m_sidebar->setSpacing(4); - m_sidebar->setFrameShape(QFrame::NoFrame); - m_sidebar->setStyleSheet( - "QListWidget { background-color: #2c2c2e; border: none; padding: 8px 0; }" - "QListWidget::item { color: #8e8e93; padding: 12px 0; text-align: center; font-size: 10px; border: none; border-radius: 8px; margin: 2px 8px; }" - "QListWidget::item:selected { background-color: #3a3a3c; color: #ffffff; }" - "QListWidget::item:hover { background-color: #3a3a3c; color: #ffffff; }" + // Always-visible left panel: its content (navigator) changes per section, + // and the three section buttons sit at the bottom of the panel. + m_navigatorPanel = new QWidget(); + m_navigatorPanel->setObjectName("NavigatorPanel"); + m_navigatorPanel->setFixedWidth(240); + m_navigatorPanel->setStyleSheet( + "QWidget#NavigatorPanel { background-color: #f7f7f9; }" ); + QVBoxLayout *panelLay = new QVBoxLayout(m_navigatorPanel); + panelLay->setContentsMargins(0, 0, 0, 0); + panelLay->setSpacing(0); - m_sidebar->addItem("📧\nMail"); - m_sidebar->addItem("✏️\nCompose"); - m_sidebar->addItem("⚙️\nSettings"); - m_sidebar->addItem("👥\nContacts"); - m_sidebar->addItem("📅\nCalendar"); + // The three navigator pages. + m_navStack = new QStackedWidget(); + m_navStack->setStyleSheet("background: transparent;"); - m_sidebar->setCurrentRow(0); - connect(m_sidebar, &QListWidget::currentRowChanged, this, &MainMainWindow::onNavChanged); + // Page 0: folder + category navigator (Emails). Built now; the folder/category + // trees are wired to models in connectModels(). + m_folderNav = new QWidget(); + m_folderNav->setObjectName("FolderNav"); + QVBoxLayout *folderNavLay = new QVBoxLayout(m_folderNav); + folderNavLay->setContentsMargins(4, 8, 4, 8); + folderNavLay->setSpacing(8); + + m_folderTree = new QTreeView(); + m_folderTree->setHeaderHidden(true); + m_folderTree->setIndentation(20); + m_folderTree->setMinimumWidth(150); + m_folderTree->setMaximumWidth(300); + m_folderTree->setFrameShape(QFrame::NoFrame); + m_folderTree->setExpandsOnDoubleClick(true); + m_folderTree->setItemDelegate(new FolderTreeDelegate(this)); + folderNavLay->addWidget(m_folderTree, 3); + + m_categoryTree = new CategoryTreeWidget(); + m_categoryTree->setMinimumHeight(120); + m_categoryTree->setMaximumHeight(320); + connect(m_categoryTree, &CategoryTreeWidget::categorySelected, this, &MainMainWindow::onCategorySelected); + connect(m_categoryTree, &CategoryTreeWidget::categoryDoubleClicked, this, &MainMainWindow::onCategoryDoubleClicked); + connect(m_categoryTree, &CategoryTreeWidget::assignCategoryRequested, this, &MainMainWindow::onAssignCategoryRequested); + connect(m_categoryTree, &CategoryTreeWidget::categoryCreated, this, &MainMainWindow::onCategoryCreated); + connect(m_categoryTree, &CategoryTreeWidget::categoryEdited, this, &MainMainWindow::onCategoryEdited); + connect(m_categoryTree, &CategoryTreeWidget::categoryDeleted, this, &MainMainWindow::onCategoryDeleted); + folderNavLay->addWidget(m_categoryTree, 2); + m_navStack->addWidget(m_folderNav); + + // Page 1: compact contact list navigator. + m_contactsNav = new QWidget(); + m_contactsNav->setObjectName("ContactsNav"); + QVBoxLayout *contactsNavLay = new QVBoxLayout(m_contactsNav); + contactsNavLay->setContentsMargins(4, 8, 4, 8); + QLabel *contactsTitle = new QLabel("Contactos"); + contactsTitle->setStyleSheet("font-weight:700; font-size:13px; color:#1d1d1f; padding:4px;"); + contactsNavLay->addWidget(contactsTitle); + m_contactsNavList = new QListWidget(); + m_contactsNavList->setStyleSheet( + "QListWidget { background:#ffffff; border:1px solid #e0e0e5; border-radius:6px; font-size:12px; }" + "QListWidget::item { padding:6px 8px; border-bottom:1px solid #f0f0f3; }" + "QListWidget::item:selected { background:#e8f1fd; color:#1c1c1e; }" + ); + contactsNavLay->addWidget(m_contactsNavList); + m_navStack->addWidget(m_contactsNav); + + // Page 2: mini calendar navigator. + m_calendarNav = new QWidget(); + m_calendarNav->setObjectName("CalendarNav"); + QVBoxLayout *calendarNavLay = new QVBoxLayout(m_calendarNav); + calendarNavLay->setContentsMargins(4, 8, 4, 8); + m_calendarNavWidget = new QCalendarWidget(); + m_calendarNavWidget->setStyleSheet( + "QCalendarWidget { background:#ffffff; border:1px solid #e0e0e5; border-radius:6px; }" + ); + calendarNavLay->addWidget(m_calendarNavWidget); + m_navStack->addWidget(m_calendarNav); + + panelLay->addWidget(m_navStack, 1); + + // Bottom navigation buttons: only Emails, Contacts, Calendar (no Compose, no Settings). + QWidget *navBtns = new QWidget(); + navBtns->setObjectName("NavButtons"); + QHBoxLayout *btnLay = new QHBoxLayout(navBtns); + btnLay->setContentsMargins(6, 8, 6, 8); + btnLay->setSpacing(4); + + auto makeNavBtn = [&](const QString &text, QToolButton **out) { + QToolButton *b = new QToolButton(); + b->setText(text); + b->setToolButtonStyle(Qt::ToolButtonTextOnly); + b->setCheckable(true); + b->setAutoExclusive(true); + b->setStyleSheet( + "QToolButton { border:none; border-radius:6px; padding:8px 4px; font-size:11px; color:#3a3a3c; }" + "QToolButton:hover { background:#e0e4ea; }" + "QToolButton:checked { background:#1976D2; color:#ffffff; font-weight:600; }" + ); + if (out) *out = b; + return b; + }; + m_navMailBtn = makeNavBtn("📧 Emails", nullptr); + m_navContactsBtn = makeNavBtn("👥 Contactos", nullptr); + m_navCalendarBtn = makeNavBtn("📅 Calendario", nullptr); + btnLay->addWidget(m_navMailBtn, 1); + btnLay->addWidget(m_navContactsBtn, 1); + btnLay->addWidget(m_navCalendarBtn, 1); + panelLay->addWidget(navBtns); + + connect(m_navMailBtn, &QToolButton::clicked, this, [this]() { switchToPage(PageMail); }); + connect(m_navContactsBtn, &QToolButton::clicked, this, [this]() { switchToPage(PageContacts); }); + connect(m_navCalendarBtn, &QToolButton::clicked, this, [this]() { switchToPage(PageCalendar); }); + + m_navMailBtn->setChecked(true); + m_navStack->setCurrentIndex(0); } void MainMainWindow::setupMailPage() @@ -217,45 +317,11 @@ void MainMainWindow::setupMailPage() mailLayout->setContentsMargins(0, 0, 0, 0); mailLayout->setSpacing(0); - // Main splitter with 3 panes: folders | categories | mail list | viewer + // Main splitter: mail list | viewer (folders/categories now live in the + // always-visible left navigator panel built by setupNavigator()). m_folderSplitter = new QSplitter(Qt::Horizontal); m_folderSplitter->setHandleWidth(1); - // Left pane: Folder tree + Category tree (vertical splitter) - QSplitter* leftSplitter = new QSplitter(Qt::Vertical); - leftSplitter->setHandleWidth(1); - leftSplitter->setChildrenCollapsible(false); - - // Folder tree - m_folderTree = new QTreeView(); - m_folderTree->setHeaderHidden(true); - m_folderTree->setIndentation(20); - m_folderTree->setMinimumWidth(160); // Reduced from 220 - m_folderTree->setMaximumWidth(350); - m_folderTree->setFrameShape(QFrame::NoFrame); - m_folderTree->setExpandsOnDoubleClick(true); - m_folderTree->setItemDelegate(new FolderTreeDelegate(this)); // Custom delegate with unread count badges - leftSplitter->addWidget(m_folderTree); - - // Category tree - m_categoryTree = new CategoryTreeWidget(); - m_categoryTree->setMinimumHeight(200); - m_categoryTree->setMaximumHeight(400); - connect(m_categoryTree, &CategoryTreeWidget::categorySelected, this, &MainMainWindow::onCategorySelected); - connect(m_categoryTree, &CategoryTreeWidget::categoryDoubleClicked, this, &MainMainWindow::onCategoryDoubleClicked); - connect(m_categoryTree, &CategoryTreeWidget::assignCategoryRequested, this, &MainMainWindow::onAssignCategoryRequested); - connect(m_categoryTree, &CategoryTreeWidget::categoryCreated, this, &MainMainWindow::onCategoryCreated); - connect(m_categoryTree, &CategoryTreeWidget::categoryEdited, this, &MainMainWindow::onCategoryEdited); - connect(m_categoryTree, &CategoryTreeWidget::categoryDeleted, this, &MainMainWindow::onCategoryDeleted); - leftSplitter->addWidget(m_categoryTree); - - // Set initial sizes for left splitter: folder tree gets 60%, categories 40% - leftSplitter->setSizes({400, 250}); - leftSplitter->setStretchFactor(0, 3); - leftSplitter->setStretchFactor(1, 2); - - m_folderSplitter->addWidget(leftSplitter); - // Mail list (QTableView with sorting) m_mailListView = new MailListView(); connect(m_mailListView, &MailListView::emailSelected, this, &MainMainWindow::onEmailSelected); @@ -331,7 +397,7 @@ void MainMainWindow::setupMailPage() m_folderSplitter->addWidget(m_viewerStack); // Default sizes: left pane (folders+categories) 300, list 380, viewer flex - m_folderSplitter->setSizes({300, 380, 600}); + m_folderSplitter->setSizes({420, 600}); // Allow each pane to shrink further m_folderSplitter->setHandleWidth(1); @@ -397,11 +463,83 @@ void MainMainWindow::onNavChanged(int index) void MainMainWindow::switchToPage(int pageIndex) { m_stack->setCurrentIndex(pageIndex); - m_sidebar->blockSignals(true); - m_sidebar->setCurrentRow(pageIndex); - m_sidebar->blockSignals(false); - // Show/hide toolbar actions per page + // Sync the left navigator panel: highlight the matching button and show the + // corresponding navigator content (folders/contacts/calendar). + if (m_navMailBtn && m_navContactsBtn && m_navCalendarBtn && m_navStack) { + m_navMailBtn->blockSignals(true); + m_navContactsBtn->blockSignals(true); + m_navCalendarBtn->blockSignals(true); + switch (static_cast(pageIndex)) { + case PageMail: + m_navMailBtn->setChecked(true); + m_navStack->setCurrentIndex(0); // folder/category navigator + break; + case PageContacts: + m_navContactsBtn->setChecked(true); + m_navStack->setCurrentIndex(1); // contacts navigator + refreshContactsNavigator(); + break; + case PageCalendar: + m_navCalendarBtn->setChecked(true); + m_navStack->setCurrentIndex(2); // mini calendar navigator + break; + default: + // Mail/Compose/Settings: keep the folder navigator highlighted. + m_navMailBtn->setChecked(true); + m_navStack->setCurrentIndex(0); + break; + } + m_navMailBtn->blockSignals(false); + m_navContactsBtn->blockSignals(false); + m_navCalendarBtn->blockSignals(false); + } +} + +void MainMainWindow::refreshContactsNavigator() +{ + if (!m_contactsNavList) return; + m_contactsNavList->clear(); + + // Aggregate contacts (sender + recipients) from local mails, most frequent first. + QHash contactCount; // email (lower) -> count + QHash contactName; // email (lower) -> display label + QRegularExpression emailRx("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"); + + auto ingest = [&](const QString &raw) { + QRegularExpressionMatch m = emailRx.match(raw); + if (!m.hasMatch()) return; + QString email = m.captured(0).toLower(); + contactCount[email]++; + // Prefer "Name " display; fallback to email. + if (!contactName.contains(email)) { + int lt = raw.indexOf('<'); + QString cand = lt > 0 ? raw.left(lt).trimmed() : email; + contactName[email] = cand.isEmpty() ? email : cand; + } + }; + + for (const MailItem &mail : MailItemDao::findAll()) { + ingest(mail.sender()); + ingest(mail.recipient()); + ingest(mail.to()); + ingest(mail.cc()); + } + + // Sort by frequency descending. + QStringList keys = contactCount.keys(); + std::sort(keys.begin(), keys.end(), [&](const QString &a, const QString &b) { + return contactCount.value(a) > contactCount.value(b); + }); + + for (const QString &email : keys) { + QString label = contactName.value(email, email); + QListWidgetItem *item = new QListWidgetItem(label + " (" + QString::number(contactCount.value(email)) + ")", + m_contactsNavList); + item->setToolTip(email); + item->setData(Qt::UserRole, email); + m_contactsNavList->addItem(item); + } } void MainMainWindow::onFolderSelected(const QModelIndex &index) diff --git a/src/ui/mainmainwindow.h b/src/ui/mainmainwindow.h index 0ac5727..3d91a37 100644 --- a/src/ui/mainmainwindow.h +++ b/src/ui/mainmainwindow.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include #include @@ -101,7 +103,7 @@ private slots: void onEmbeddedDetachRequested(QWidget *widget); private: void setupUI(); - void setupSidebar(); + void setupNavigator(); // builds left navigator panel (folders/contacts/calendar + nav buttons) void setupMailPage(); void connectModels(); void createTitleBar(); @@ -110,9 +112,18 @@ private: QWidget *createWindowControlButton(const QString &iconName, const QString &tooltip, std::function slot); void updateCategoryTreeForAccount(qint64 accountId); void assignCategoryToSelectedMails(qint64 categoryId); + void refreshContactsNavigator(); // populate the left-panel contact list // Navigation - QListWidget *m_sidebar; + QWidget *m_navigatorPanel; // always-visible left panel + QStackedWidget *m_navStack; // navigator content: 0=folder/category, 1=contact list, 2=mini calendar + QToolButton *m_navMailBtn, *m_navContactsBtn, *m_navCalendarBtn; + // Navigators (content of the left panel, swapped per section) + QWidget *m_folderNav; // folders + categories (shown for Emails) + QWidget *m_contactsNav; // compact contact list + QWidget *m_calendarNav; // mini calendar widget + QListWidget *m_contactsNavList; + QCalendarWidget *m_calendarNavWidget; QStackedWidget *m_stack; enum Page {