diff options
| -rw-r--r-- | src/config.cpp | 17 | ||||
| -rw-r--r-- | src/config.h | 10 | ||||
| -rw-r--r-- | src/mainwindow.cpp | 50 | ||||
| -rw-r--r-- | src/mainwindow.h | 2 |
4 files changed, 77 insertions, 2 deletions
diff --git a/src/config.cpp b/src/config.cpp index de6a783..f21bba9 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -89,6 +89,23 @@ void Config::load(const QString &path) account.address = settings.value(QStringLiteral("address")).toString(); account.maildir = settings.value(QStringLiteral("maildir")).toString(); account.drafts = settings.value(QStringLiteral("drafts")).toString(); + + // Both optional, and both describe this account's chip in the thread + // list. An account tag is a different taxonomy from a functional one, + // saying which mailbox a thread arrived in rather than what state it + // is in, so these live here rather than in [tagcolors]. + account.label = settings.value(QStringLiteral("label")).toString(); + + const QString colour = settings.value(QStringLiteral("color")).toString(); + if (!colour.isEmpty()) { + account.color = QColor(colour); + if (!account.color.isValid()) { + addProblem( + QStringLiteral("Account '%1' has an unparseable color '%2'; " + "using a generated one.") + .arg(account.key, colour)); + } + } settings.endGroup(); if (!account.isValid()) { diff --git a/src/config.h b/src/config.h index 270b780..943cbd8 100644 --- a/src/config.h +++ b/src/config.h @@ -18,6 +18,7 @@ #pragma once +#include <QColor> #include <QList> #include <QString> #include <QStringList> @@ -33,6 +34,15 @@ struct Account QString maildir; ///< Relative to notmuch's database.path. QString drafts; ///< Unused in v1; send is v2. + /// Chip colour in the thread list. Invalid when unset, in which case one + /// is generated from the account tag's name. + QColor color; + + /// Text shown on the chip. Empty falls back to the key, which can be long: + /// "privateemail-danilo.macri" is a lot of row for one bit of information. + /// This renames nothing in notmuch, only what the chip displays. + QString label; + bool isValid() const { return !key.isEmpty() && !maildir.isEmpty(); } /// Restricts a notmuch query to this account's subtree. diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index ba690b4..48b475c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -42,6 +42,7 @@ #include "messageview.h" #include "mimeparser.h" #include "notmuchworker.h" +#include "tagchip.h" #include "threadlistmodel.h" #include "version.h" @@ -73,6 +74,14 @@ MainWindow::MainWindow(const Config &config, QWidget *parent) { QSettings settings(Config::defaultPath(), QSettings::IniFormat); m_keyMap.loadOverrides(settings); + m_tagColors.load(settings); + } + + // An account's chip colour comes from its own stanza, since an account tag + // is a different taxonomy from a functional one. + for (const Account &account : m_config.accounts()) { + m_tagColors.setAccountColour(account.key, account.color); + m_tagColors.setAccountLabel(account.key, account.label); } buildUi(); @@ -163,6 +172,7 @@ void MainWindow::buildUi() // Thread list and message pane. m_model = new ThreadListModel(this); + m_model->setTagColors(&m_tagColors); m_threadView = new QTableView(central); m_threadView->setModel(m_model); m_threadView->setSelectionBehavior(QAbstractItemView::SelectRows); @@ -176,6 +186,10 @@ void MainWindow::buildUi() m_threadView->horizontalHeader()->setSectionResizeMode( column, QHeaderView::Interactive); } + + // The subject cell carries the account chip in front of its text. + m_threadView->setItemDelegateForColumn(ThreadListModel::SubjectColumn, + new SubjectDelegate(this)); // Widening a column past the viewport scrolls rather than squeezing the // others. Per-pixel so the scroll does not jump a whole column at a time. m_threadView->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); @@ -183,7 +197,6 @@ void MainWindow::buildUi() // Starting widths only; a drag overrides them, and they are what the // saved-widths item will persist. - m_threadView->setColumnWidth(ThreadListModel::TagsColumn, 160); m_threadView->setColumnWidth(ThreadListModel::DateColumn, 130); m_threadView->setColumnWidth(ThreadListModel::AuthorsColumn, 180); m_threadView->setColumnWidth(ThreadListModel::SubjectColumn, 520); @@ -193,6 +206,7 @@ void MainWindow::buildUi() this, &MainWindow::onThreadSelected); m_messageView = new MessageView(central); + m_messageView->setTagColors(&m_tagColors); connect(m_messageView, &MessageView::statusMessage, this, [this](const QString &text) { m_statusLabel->setText(text); }); @@ -356,6 +370,28 @@ void MainWindow::buildMenus() auto *about = helpMenu->addAction(tr("&About")); connect(about, &QAction::triggered, this, &MainWindow::showAbout); + // Standard names from the icon theme, so the buttons match the rest of the + // desktop rather than shipping bespoke art. A theme that lacks one leaves + // that action with text alone, which still works. + const QHash<QString, QString> themeIcons = { + { QStringLiteral("sync"), QStringLiteral("mail-receive") }, + { QStringLiteral("archive"), QStringLiteral("mail-mark-read") }, + { QStringLiteral("delete"), QStringLiteral("edit-delete") }, + { QStringLiteral("undo"), QStringLiteral("edit-undo") }, + { QStringLiteral("spam"), QStringLiteral("mail-mark-junk") }, + { QStringLiteral("flag"), QStringLiteral("mail-mark-important") }, + { QStringLiteral("quit"), QStringLiteral("application-exit") }, + { QStringLiteral("focus_query"), QStringLiteral("edit-find") }, + }; + for (auto it = themeIcons.cbegin(); it != themeIcons.cend(); ++it) { + QAction *action = m_actions.value(it.key()); + if (!action) + continue; + const QIcon icon = QIcon::fromTheme(it.value()); + if (!icon.isNull()) + action->setIcon(icon); + } + // The frequent subset only. A toolbar holding every action is as // unreadable as no toolbar. auto *toolBar = addToolBar(tr("Main")); @@ -543,7 +579,9 @@ void MainWindow::onThreadSelected(const QModelIndex ¤t, if (!current.isValid()) return; - m_currentThreadId = m_model->threadAt(current.row()).threadId; + const ThreadSummary thread = m_model->threadAt(current.row()); + m_currentThreadId = thread.threadId; + m_messageView->setTags(thread.tags); QMetaObject::invokeMethod(m_worker, "loadThread", Qt::QueuedConnection, Q_ARG(QString, m_currentThreadId), Q_ARG(QString, m_lastQuery), @@ -665,6 +703,14 @@ void MainWindow::sendThreadTagChange(const QStringList &threadIds, for (const QString &threadId : threadIds) m_model->applyTagChange(threadId, add, remove); + // The strip shows the open thread's tags, so it has to follow a change to + // that thread rather than waiting for the next selection. + if (threadIds.contains(m_currentThreadId)) { + const QModelIndex current = m_threadView->currentIndex(); + if (current.isValid()) + m_messageView->setTags(m_model->threadAt(current.row()).tags); + } + m_pendingThreadIds = threadIds; m_pendingChange = TagChange{ {}, add, remove, description }; diff --git a/src/mainwindow.h b/src/mainwindow.h index 30a128a..4445894 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -28,6 +28,7 @@ #include "config.h" #include "keymap.h" +#include "tagcolors.h" #include "types.h" class QAction; @@ -103,6 +104,7 @@ private: Config m_config; KeyMap m_keyMap; + TagColors m_tagColors; QThread m_workerThread; NotmuchWorker *m_worker = nullptr; |
