fix #948, work on label performance

This commit is contained in:
Martin Rotter 2023-06-09 07:39:26 +02:00
parent a3442c1829
commit a35be95d07
12 changed files with 154 additions and 49 deletions

View File

@ -143,6 +143,14 @@ Message Message::fromSqlRecord(const QSqlRecord& record, bool* result) {
message.m_accountId = record.value(MSG_DB_ACCOUNT_ID_INDEX).toInt();
message.m_customId = record.value(MSG_DB_CUSTOM_ID_INDEX).toString();
message.m_customHash = record.value(MSG_DB_CUSTOM_HASH_INDEX).toString();
message.m_assignedLabelsIds = record.value(MSG_DB_LABELS_IDS)
.toString()
.split('.',
#if QT_VERSION >= 0x050F00 // Qt >= 5.15.0
Qt::SplitBehaviorFlags::SkipEmptyParts);
#else
QString::SplitBehavior::SkipEmptyParts);
#endif
if (result != nullptr) {
*result = true;

View File

@ -92,6 +92,8 @@ class RSSGUARD_DLLSPEC Message {
QList<Label*> m_assignedLabelsByFilter;
QList<Label*> m_deassignedLabelsByFilter;
QStringList m_assignedLabelsIds;
// Is true if "created" date was obtained directly
// from the feed, otherwise is false
bool m_createdFromFeed = false;

View File

@ -2,6 +2,7 @@
#include "core/messagesmodel.h"
#include "3rd-party/boolinq/boolinq.h"
#include "core/messagesmodelcache.h"
#include "database/databasefactory.h"
#include "database/databasequeries.h"
@ -24,13 +25,15 @@
MessagesModel::MessagesModel(QObject* parent)
: QSqlQueryModel(parent), m_view(nullptr), m_cache(new MessagesModelCache(this)),
m_messageHighlighter(MessageHighlighter::NoHighlighting), m_customDateFormat(QString()),
m_customTimeFormat(QString()), m_newerArticlesRelativeTime(-1), m_selectedItem(nullptr), m_displayFeedIcons(false),
m_customTimeFormat(QString()), m_newerArticlesRelativeTime(-1), m_selectedItem(nullptr),
m_unreadIconType(MessageUnreadIcon::Dot),
m_multilineListItems(qApp->settings()->value(GROUP(Messages), SETTING(Messages::MultilineArticleList)).toBool()) {
updateFeedIconsDisplay();
updateDateFormat();
setupFonts();
setupIcons();
setupHeaderData();
updateDateFormat();
updateFeedIconsDisplay();
loadMessages(nullptr);
}
@ -38,10 +41,13 @@ MessagesModel::~MessagesModel() {
qDebugNN << LOGSEC_MESSAGEMODEL << "Destroying MessagesModel instance.";
}
#define RAD_COLOR 0, 180, 0
void MessagesModel::setupIcons() {
m_favoriteIcon = qApp->icons()->fromTheme(QSL("mail-mark-important"));
m_readIcon = qApp->icons()->fromTheme(QSL("mail-mark-read"));
m_unreadIcon = qApp->icons()->fromTheme(QSL("mail-mark-unread"));
m_unreadIcon = m_unreadIconType == MessageUnreadIcon::Dot ? generateUnreadIcon()
: qApp->icons()->fromTheme(QSL("mail-mark-unread"));
m_enclosuresIcon = qApp->icons()->fromTheme(QSL("mail-attachment"));
for (int i = int(MSG_SCORE_MIN); i <= int(MSG_SCORE_MAX); i += 10) {
@ -82,6 +88,46 @@ QIcon MessagesModel::generateIconForScore(double score) {
return pix;
}
QIcon MessagesModel::generateUnreadIcon() {
QPointF center(64, 64);
qreal radius = 32;
QRadialGradient grad(center, radius);
grad.setColorAt(0.000, QColor(RAD_COLOR, 255));
grad.setColorAt(0.8, QColor(RAD_COLOR, 0.8 * 255));
grad.setColorAt(1.000, QColor(RAD_COLOR, 0.000));
QPen pen;
pen.setWidth(96);
pen.setBrush(grad);
QPixmap pix(128, 128);
pix.fill(Qt::GlobalColor::transparent);
QPainter painter(&pix);
painter.setRenderHint(QPainter::RenderHint::Antialiasing);
painter.setPen(pen);
painter.drawPoint(center);
return QIcon(pix);
}
QString MessagesModel::descriptionOfUnreadIcon(MessageUnreadIcon type) {
switch (type) {
case MessagesModel::MessageUnreadIcon::Dot:
return tr("dot");
case MessagesModel::MessageUnreadIcon::Envelope:
return tr("envelope");
case MessagesModel::MessageUnreadIcon::FeedIcon:
return tr("feed icon");
default:
return QString();
}
}
MessagesView* MessagesModel::view() const {
return m_view;
}
@ -166,7 +212,7 @@ bool MessagesModel::setMessageImportantById(int id, RootItem::Importance importa
bool set = setData(index(i, MSG_DB_IMPORTANT_INDEX), int(important));
if (set) {
emit dataChanged(index(i, 0), index(i, MSG_DB_CUSTOM_HASH_INDEX));
emit dataChanged(index(i, 0), index(i, MSG_DB_LABELS_IDS));
}
return set;
@ -214,7 +260,8 @@ void MessagesModel::updateDateFormat() {
}
void MessagesModel::updateFeedIconsDisplay() {
m_displayFeedIcons = qApp->settings()->value(GROUP(Messages), SETTING(Messages::DisplayFeedIconsInList)).toBool();
m_unreadIconType =
MessageUnreadIcon(qApp->settings()->value(GROUP(Messages), SETTING(Messages::UnreadIconType)).toInt());
}
void MessagesModel::reloadWholeLayout() {
@ -344,6 +391,9 @@ QVariant MessagesModel::data(const QModelIndex& idx, int role) const {
return contents;
}
else if (index_column == MSG_DB_LABELS_IDS) {
return m_cache->containsData(idx.row()) ? m_cache->data(idx) : QSqlQueryModel::data(idx, role);
}
else if (index_column == MSG_DB_AUTHOR_INDEX) {
const QString author_name = QSqlQueryModel::data(idx, role).toString();
@ -477,11 +527,13 @@ QVariant MessagesModel::data(const QModelIndex& idx, int role) const {
const int index_column = idx.column();
if (index_column == MSG_DB_READ_INDEX) {
if (m_displayFeedIcons && m_selectedItem != nullptr) {
if (m_unreadIconType == MessageUnreadIcon::FeedIcon && m_selectedItem != nullptr) {
QModelIndex idx_feedid = index(idx.row(), MSG_DB_FEED_CUSTOM_ID_INDEX);
QVariant dta =
m_cache->containsData(idx_feedid.row()) ? m_cache->data(idx_feedid) : QSqlQueryModel::data(idx_feedid);
QString feed_custom_id = dta.toString();
// TODO: Very slow and repeats itself.
auto acc = m_selectedItem->getParentServiceRoot()->feedIconForMessage(feed_custom_id);
if (acc.isNull()) {
@ -496,7 +548,12 @@ QVariant MessagesModel::data(const QModelIndex& idx, int role) const {
QVariant dta =
m_cache->containsData(idx_read.row()) ? m_cache->data(idx_read) : QSqlQueryModel::data(idx_read);
return dta.toInt() == 1 ? m_readIcon : m_unreadIcon;
if (m_unreadIconType == MessageUnreadIcon::Dot) {
return dta.toInt() == 1 ? QVariant() : m_unreadIcon;
}
else {
return dta.toInt() == 1 ? m_readIcon : m_unreadIcon;
}
}
}
else if (index_column == MSG_DB_IMPORTANT_INDEX) {
@ -529,7 +586,7 @@ QVariant MessagesModel::data(const QModelIndex& idx, int role) const {
}
bool MessagesModel::setMessageRead(int row_index, RootItem::ReadStatus read) {
if (data(row_index, MSG_DB_READ_INDEX, Qt::EditRole).toInt() == int(read)) {
if (data(row_index, MSG_DB_READ_INDEX, Qt::ItemDataRole::EditRole).toInt() == int(read)) {
// Read status is the same is the one currently set.
// In that case, no extra work is needed.
return true;
@ -565,13 +622,32 @@ bool MessagesModel::setMessageRead(int row_index, RootItem::ReadStatus read) {
bool MessagesModel::setMessageReadById(int id, RootItem::ReadStatus read) {
for (int i = 0; i < rowCount(); i++) {
int found_id = data(i, MSG_DB_ID_INDEX, Qt::EditRole).toInt();
int found_id = data(i, MSG_DB_ID_INDEX, Qt::ItemDataRole::EditRole).toInt();
if (found_id == id) {
bool set = setData(index(i, MSG_DB_READ_INDEX), int(read));
if (set) {
emit dataChanged(index(i, 0), index(i, MSG_DB_CUSTOM_HASH_INDEX));
emit dataChanged(index(i, 0), index(i, MSG_DB_LABELS_IDS));
}
return set;
}
}
return false;
}
bool MessagesModel::setMessageLabelsById(int id, const QStringList& label_ids) {
for (int i = 0; i < rowCount(); i++) {
int found_id = data(i, MSG_DB_ID_INDEX, Qt::ItemDataRole::EditRole).toInt();
if (found_id == id) {
QString enc_ids = label_ids.isEmpty() ? QSL(".") : QSL(".") + label_ids.join('.') + QSL(".");
bool set = setData(index(i, MSG_DB_LABELS_IDS), enc_ids);
if (set) {
emit dataChanged(index(i, 0), index(i, MSG_DB_LABELS_IDS));
}
return set;

View File

@ -24,6 +24,10 @@ class MessagesModel : public QSqlQueryModel, public MessagesModelSqlLayer {
// for messages.
enum class MessageHighlighter { NoHighlighting = 100, HighlightUnread = 101, HighlightImportant = 102 };
enum class MessageUnreadIcon { Dot = 1, Envelope = 2, FeedIcon = 3 };
Q_ENUM(MessageUnreadIcon)
// Constructors and destructors.
explicit MessagesModel(QObject* parent = nullptr);
virtual ~MessagesModel();
@ -74,6 +78,8 @@ class MessagesModel : public QSqlQueryModel, public MessagesModelSqlLayer {
void setView(MessagesView* new_view);
static QIcon generateIconForScore(double score);
static QIcon generateUnreadIcon();
static QString descriptionOfUnreadIcon(MessagesModel::MessageUnreadIcon type);
public slots:
@ -81,6 +87,7 @@ class MessagesModel : public QSqlQueryModel, public MessagesModelSqlLayer {
// These are particularly used by msg browser.
bool setMessageImportantById(int id, RootItem::Importance important);
bool setMessageReadById(int id, RootItem::ReadStatus read);
bool setMessageLabelsById(int id, const QStringList& label_ids);
private:
void setupHeaderData();
@ -105,10 +112,11 @@ class MessagesModel : public QSqlQueryModel, public MessagesModelSqlLayer {
QIcon m_unreadIcon;
QIcon m_enclosuresIcon;
QList<QIcon> m_scoreIcons;
bool m_displayFeedIcons;
MessageUnreadIcon m_unreadIconType;
bool m_multilineListItems;
};
Q_DECLARE_METATYPE(MessagesModel::MessageHighlighter)
Q_DECLARE_METATYPE(MessagesModel::MessageUnreadIcon)
#endif // MESSAGESMODEL_H

View File

@ -261,6 +261,10 @@ void FeedMessageViewer::createConnections() {
&MessagePreviewer::markMessageImportant,
m_messagesView->sourceModel(),
&MessagesModel::setMessageImportantById);
connect(m_messagesBrowser,
&MessagePreviewer::setMessageLabelIds,
m_messagesView->sourceModel(),
&MessagesModel::setMessageLabelsById);
connect(m_messagesView, &MessagesView::currentMessageRemoved, this, &FeedMessageViewer::onMessageRemoved);
connect(m_messagesView, &MessagesView::currentMessageChanged, this, &FeedMessageViewer::displayMessage);

View File

@ -191,10 +191,14 @@ void MessagePreviewer::switchLabel(bool assign) {
if (assign) {
lbl->assignToMessage(m_message);
m_message.m_assignedLabelsIds.append(lbl->customId());
}
else {
lbl->deassignFromMessage(m_message);
m_message.m_assignedLabelsIds.removeOne(lbl->customId());
}
emit setMessageLabelIds(m_message.m_id, m_message.m_assignedLabelsIds);
}
void MessagePreviewer::markMessageAsRead() {
@ -276,39 +280,22 @@ void MessagePreviewer::updateLabels(bool only_clear) {
if (m_root.data() != nullptr && !m_root.data()->getParentServiceRoot()->labelsNode()->labels().isEmpty()) {
m_separator = m_toolBar->addSeparator();
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
auto lbls = m_root.data()->getParentServiceRoot()->labelsNode()->labels();
for (auto* label : lbls) {
/*
LabelButton* btn_label = new LabelButton(this);
btn_label->setLabel(label);
btn_label->setCheckable(true);
btn_label->setIcon(Label::generateIcon(label->color()));
btn_label->setAutoRaise(false);
btn_label->setText();
btn_label->setToolButtonStyle(Qt::ToolButtonStyle(qApp->settings()
->value(GROUP(GUI), SETTING(GUI::ToolbarStyle))
.toInt()));
btn_label->setToolTip(label->title());
btn_label->setChecked();
*/
LabelToolbarAction* act_label = new LabelToolbarAction(this);
act_label->setIcon(Label::generateIcon(label->color()));
act_label->setText(QSL(" ") + label->title());
act_label->setCheckable(true);
act_label->setChecked(DatabaseQueries::isLabelAssignedToMessage(database, label, m_message));
act_label->setChecked(m_message.m_assignedLabelsIds.contains(label->customId()));
act_label->setToolTip(label->title());
act_label->setLabel(label);
m_toolBar->addAction(act_label);
m_btnLabels.append(act_label);
connect(act_label, &QAction::toggled, this, &MessagePreviewer::switchLabel);
m_btnLabels.append(act_label);
}
}
}

View File

@ -61,6 +61,7 @@ class MessagePreviewer : public QWidget {
signals:
void markMessageRead(int id, RootItem::ReadStatus read);
void markMessageImportant(int id, RootItem::Importance important);
void setMessageLabelIds(int id, const QStringList& ids);
private:
void createConnections();

View File

@ -14,6 +14,7 @@
#include <QFontDialog>
#include <QLocale>
#include <QMetaEnum>
#include <QStringList>
SettingsFeedsMessages::SettingsFeedsMessages(Settings* settings, QWidget* parent)
@ -65,7 +66,9 @@ SettingsFeedsMessages::SettingsFeedsMessages(Settings* settings, QWidget* parent
connect(m_ui->m_cbHideCountsIfNoUnread, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings);
connect(m_ui->m_checkAutoUpdate, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings);
connect(m_ui->m_checkAutoUpdateOnlyUnfocused, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings);
connect(m_ui->m_checkDisplayFeedIcons, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings);
connect(m_ui->m_cmbUnreadIconType, &QComboBox::currentIndexChanged, this, &SettingsFeedsMessages::dirtifySettings);
connect(m_ui->m_cmbUnreadIconType, &QComboBox::currentIndexChanged, this, &SettingsFeedsMessages::requireRestart);
connect(m_ui->m_checkKeppMessagesInTheMiddle, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings);
connect(m_ui->m_cbArticleViewerAlwaysVisible, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings);
connect(m_ui->m_checkMessagesDateTimeFormat, &QCheckBox::toggled, this, &SettingsFeedsMessages::dirtifySettings);
@ -181,6 +184,14 @@ SettingsFeedsMessages::SettingsFeedsMessages(Settings* settings, QWidget* parent
}
m_ui->m_spinRelativeArticleTime->setValue(-1);
QMetaEnum enumer = QMetaEnum::fromType<MessagesModel::MessageUnreadIcon>();
for (int i = 0; i < enumer.keyCount(); i++) {
auto en = MessagesModel::MessageUnreadIcon(enumer.value(i));
m_ui->m_cmbUnreadIconType->addItem(MessagesModel::descriptionOfUnreadIcon(en), int(en));
}
}
SettingsFeedsMessages::~SettingsFeedsMessages() {
@ -227,8 +238,9 @@ void SettingsFeedsMessages::loadSettings() {
->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::OnlyBasicShortcutsInLists)).toBool());
m_ui->m_cbHideCountsIfNoUnread
->setChecked(settings()->value(GROUP(Feeds), SETTING(Feeds::HideCountsIfNoUnread)).toBool());
m_ui->m_checkDisplayFeedIcons
->setChecked(settings()->value(GROUP(Messages), SETTING(Messages::DisplayFeedIconsInList)).toBool());
m_ui->m_cmbUnreadIconType
->setCurrentIndex(m_ui->m_cmbUnreadIconType
->findData(settings()->value(GROUP(Messages), SETTING(Messages::UnreadIconType)).toInt()));
m_ui->m_checkBringToForegroundAfterMsgOpened
->setChecked(settings()
->value(GROUP(Messages), SETTING(Messages::BringAppToFrontAfterMessageOpenedExternally))
@ -316,7 +328,7 @@ void SettingsFeedsMessages::saveSettings() {
settings()->setValue(GROUP(Feeds), Feeds::OnlyBasicShortcutsInLists, m_ui->m_cbListsRestrictedShortcuts->isChecked());
settings()->setValue(GROUP(Feeds), Feeds::HideCountsIfNoUnread, m_ui->m_cbHideCountsIfNoUnread->isChecked());
settings()->setValue(GROUP(Messages), Messages::DisplayFeedIconsInList, m_ui->m_checkDisplayFeedIcons->isChecked());
settings()->setValue(GROUP(Messages), Messages::UnreadIconType, m_ui->m_cmbUnreadIconType->currentData().toInt());
settings()->setValue(GROUP(Messages),
Messages::BringAppToFrontAfterMessageOpenedExternally,
m_ui->m_checkBringToForegroundAfterMsgOpened->isChecked());
@ -368,7 +380,6 @@ void SettingsFeedsMessages::saveSettings() {
qApp->feedReader()->feedsModel()->reloadWholeLayout();
qApp->feedReader()->messagesModel()->updateDateFormat();
qApp->feedReader()->messagesModel()->updateFeedIconsDisplay();
qApp->feedReader()->messagesModel()->reloadWholeLayout();
onEndSaveSettings();

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>495</width>
<width>558</width>
<height>531</height>
</rect>
</property>
@ -411,13 +411,6 @@
<string>Articles list</string>
</attribute>
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="m_checkDisplayFeedIcons">
<property name="text">
<string>Display real icons of feeds instead of read/unread icons</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QCheckBox" name="m_checkKeppMessagesInTheMiddle">
<property name="text">
@ -601,6 +594,19 @@
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="m_cmbUnreadIconType"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Unread article icon type</string>
</property>
<property name="buddy">
<cstring>m_cmbUnreadIconType</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
@ -642,7 +648,7 @@
<tabstop>m_cbShowEnclosuresDirectly</tabstop>
<tabstop>m_spinHeightImageAttachments</tabstop>
<tabstop>m_btnChangeMessagesFont</tabstop>
<tabstop>m_checkDisplayFeedIcons</tabstop>
<tabstop>m_cmbUnreadIconType</tabstop>
<tabstop>m_checkKeppMessagesInTheMiddle</tabstop>
<tabstop>m_checkMultilineArticleList</tabstop>
<tabstop>m_spinHeightRowsMessages</tabstop>

View File

@ -175,8 +175,8 @@ DVALUE(bool) Messages::ClearReadOnExitDef = false;
DKEY Messages::IgnoreContentsChanges = "ignore_contents_changes";
DVALUE(bool) Messages::IgnoreContentsChangesDef = true;
DKEY Messages::DisplayFeedIconsInList = "display_feed_icons_in_message_list";
DVALUE(bool) Messages::DisplayFeedIconsInListDef = false;
DKEY Messages::UnreadIconType = "unread_icons_in_message_list";
DVALUE(int) Messages::UnreadIconTypeDef = 1; /* MessagesModel::MessageUnreadIcon::Dot */
DKEY Messages::BringAppToFrontAfterMessageOpenedExternally = "bring_app_to_front_after_msg_opened";
DVALUE(bool) Messages::BringAppToFrontAfterMessageOpenedExternallyDef = false;

View File

@ -174,8 +174,8 @@ namespace Messages {
KEY IgnoreContentsChanges;
VALUE(bool) IgnoreContentsChangesDef;
KEY DisplayFeedIconsInList;
VALUE(bool) DisplayFeedIconsInListDef;
KEY UnreadIconType;
VALUE(int) UnreadIconTypeDef;
KEY BringAppToFrontAfterMessageOpenedExternally;
VALUE(bool) BringAppToFrontAfterMessageOpenedExternallyDef;

View File

@ -77,6 +77,8 @@ bool Label::deleteViaGui() {
void Label::updateCounts(bool including_total_count) {
QSqlDatabase database = qApp->database()->driver()->threadSafeConnection(metaObject()->className());
int account_id = getParentServiceRoot()->accountId();
// TODO: slow
auto ac = DatabaseQueries::getMessageCountsForLabel(database, this, account_id);
if (including_total_count) {