diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index 1e1b4c1c..c936bf94 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -1,183 +1,184 @@ set(ruqola_userfeedback_SRCS) if (TARGET KUserFeedbackWidgets) set(ruqola_userfeedback_SRCS ${ruqola_userfeedback_SRCS} userfeedback/userfeedbackmanager.cpp ) endif() set(Ruqola_widgets_SRCS ruqolamainwindow.cpp ruqolacentralwidget.cpp ruqolaloginwidget.cpp ruqolamainwidget.cpp ) set(Ruqola_widgets_dialog_SRCS dialogs/serverinfowidget.cpp dialogs/serverinfodialog.cpp dialogs/uploadfiledialog.cpp dialogs/uploadfilewidget.cpp dialogs/channelinfowidget.cpp dialogs/channelinfodialog.cpp dialogs/searchchanneldialog.cpp dialogs/searchchannelwidget.cpp dialogs/modifystatusdialog.cpp dialogs/modifystatuswidget.cpp dialogs/createnewchanneldialog.cpp dialogs/createnewchannelwidget.cpp dialogs/showlistmessagebasedialog.cpp dialogs/showlistmessagebasewidget.cpp dialogs/createnewaccountdialog.cpp dialogs/createnewaccountwidget.cpp dialogs/showpinnedmessagesdialog.cpp dialogs/showstarredmessagesdialog.cpp dialogs/showmentionsmessagesdialog.cpp dialogs/showsnipperedmessagesdialog.cpp dialogs/configurenotificationdialog.cpp dialogs/configurenotificationwidget.cpp dialogs/searchmessagedialog.cpp dialogs/searchmessagewidget.cpp dialogs/reportmessagedialog.cpp dialogs/reportmessagewidget.cpp dialogs/showimagedialog.cpp dialogs/showimagewidget.cpp dialogs/showattachmentdialog.cpp dialogs/showattachmentwidget.cpp dialogs/directchannelinfodialog.cpp dialogs/directchannelinfowidget.cpp dialogs/attachment/listattachmentdelegate.cpp dialogs/showdiscussionsdialog.cpp dialogs/showdiscussionswidget.cpp dialogs/discussion/listdiscussiondelegate.cpp dialogs/createnewdiscussiondialog.cpp dialogs/createnewdiscussionwidget.cpp dialogs/channelpassworddialog.cpp dialogs/channelpasswordwidget.cpp dialogs/addusersinroomdialog.cpp dialogs/addusersinroomwidget.cpp dialogs/adduserscompletionlineedit.cpp dialogs/showthreadswidget.cpp dialogs/showthreadsdialog.cpp dialogs/thread/listthreadsdelegate.cpp dialogs/searchchannel/searchchanneldelegate.cpp dialogs/takevideomessagedialog.cpp dialogs/takevideomessagewidget.cpp dialogs/inviteusersdialog.cpp dialogs/inviteuserswidget.cpp ) set(Ruqola_configure_SRCS configuredialog/configuresettingsdialog.cpp configuredialog/configureaccountwidget.cpp configuredialog/accountserverlistwidget.cpp configuredialog/configureaccountserverwidget.cpp configuredialog/configurespellcheckingwidget.cpp configuredialog/configureuserfeedbackwidget.cpp ) ki18n_wrap_ui(Ruqola_configure_SRCS configuredialog/configureaccountserverwidget.ui ) set(Ruqola_channellist_SRCS channellist/channellistwidget.cpp channellist/statuscombobox.cpp channellist/channellistview.cpp channellist/channellistdelegate.cpp ) set(Ruqola_roomwidget_SRCS room/roomwidget.cpp room/roomheaderwidget.cpp room/messagelistview.cpp room/messagetextedit.cpp room/delegate/messagelistdelegate.cpp room/delegate/messagedelegatehelperbase.cpp room/delegate/messagedelegatehelpertext.cpp room/delegate/messagedelegatehelperimage.cpp room/delegate/messagedelegatehelperfile.cpp room/delegate/messagedelegatehelperreactions.cpp + room/delegate/pixmapcache.cpp room/messagelinewidget.cpp room/readonlylineeditwidget.cpp room/usersinroomflowwidget.cpp room/usersinroomlabel.cpp ) set(Ruqola_misc_widget_SRCS misc/accountmenu.cpp misc/accountsoverviewwidget.cpp misc/emoticonmenuwidget.cpp misc/emoticonselectorwidget.cpp misc/adduserswidget.cpp misc/clickableuserwidget.cpp ) set(Ruqola_common_SRCS common/completionlineedit.cpp common/completiontextedit.cpp common/completionlistview.cpp common/authenticationcombobox.cpp common/flowlayout.cpp ) ecm_qt_declare_logging_category(Ruqola_widgets_SRCS HEADER ruqolawidgets_debug.h IDENTIFIER RUQOLAWIDGETS_LOG CATEGORY_NAME org.kde.ruqola.widgets) qt5_add_resources(Ruqola_widgets_SRCS ruqolawidget.qrc) add_library(libruqolawidgets ${ruqola_userfeedback_SRCS} ${Ruqola_common_SRCS} ${Ruqola_widgets_SRCS} ${Ruqola_widgets_dialog_SRCS} ${Ruqola_channellist_SRCS} ${Ruqola_roomwidget_SRCS} ${Ruqola_configure_SRCS} ${Ruqola_misc_widget_SRCS} ) generate_export_header(libruqolawidgets BASE_NAME libruqolawidgets) set(ruqola_userfeedback_LIB) if (TARGET KUserFeedbackWidgets) set(ruqola_userfeedback_LIB KUserFeedbackWidgets) endif() target_link_libraries(libruqolawidgets Qt5::Gui Qt5::Widgets KF5::I18n KF5::ConfigCore KF5::XmlGui KF5::KIOWidgets KF5::WidgetsAddons KF5::SonnetUi KF5::TextWidgets librocketchatrestapi-qt5 libruqolacore ${ruqola_userfeedback_LIB} ) if (NOT WIN32) target_link_libraries(libruqolawidgets Qt5::DBus) endif() set_target_properties(libruqolawidgets PROPERTIES OUTPUT_NAME ruqolawidgets VERSION ${RUQOLA_LIB_VERSION} SOVERSION ${RUQOLA_LIB_SOVERSION} ) if (BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) add_subdirectory(channellist/autotests) add_subdirectory(room/autotests) add_subdirectory(dialogs/autotests) add_subdirectory(misc/autotests) add_subdirectory(configuredialog/autotests) add_subdirectory(common/autotests) endif() install(TARGETS libruqolawidgets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY NAMELINK_SKIP) diff --git a/src/widgets/room/autotests/CMakeLists.txt b/src/widgets/room/autotests/CMakeLists.txt index 032e9a01..24a3f871 100644 --- a/src/widgets/room/autotests/CMakeLists.txt +++ b/src/widgets/room/autotests/CMakeLists.txt @@ -1,19 +1,20 @@ macro(add_ruqolaroom_test _source) set( _sources ${_source} ${ARGN} ) get_filename_component( _name ${_source} NAME_WE ) add_executable( ${_name} ${_sources} ) add_test(NAME ${_name} COMMAND ${_name} ) ecm_mark_as_test(${_name}) target_link_libraries( ${_name} Qt5::Test libruqolacore libruqolawidgets) endmacro() add_ruqolaroom_test(roomwidgettest.cpp) add_ruqolaroom_test(roomheaderwidgettest.cpp) add_ruqolaroom_test(messagetextedittest.cpp) add_ruqolaroom_test(messagelinewidgettest.cpp) add_ruqolaroom_test(messagelistdelegatetest.cpp testdata.cpp) add_ruqolaroom_test(messagedelegatehelperimagetest.cpp testdata.cpp) add_ruqolaroom_test(messagedelegatehelperfiletest.cpp) add_ruqolaroom_test(readonlylineeditwidgettest.cpp) add_ruqolaroom_test(usersinroomflowwidgettest.cpp) add_ruqolaroom_test(usersinroomlabeltest.cpp) +add_ruqolaroom_test(pixmapcachetest.cpp) diff --git a/src/widgets/room/autotests/messagedelegatehelperimagetest.cpp b/src/widgets/room/autotests/messagedelegatehelperimagetest.cpp index 78c3edf0..a75db816 100644 --- a/src/widgets/room/autotests/messagedelegatehelperimagetest.cpp +++ b/src/widgets/room/autotests/messagedelegatehelperimagetest.cpp @@ -1,71 +1,57 @@ /* Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "messagedelegatehelperimagetest.h" #include "room/delegate/messagedelegatehelperimage.h" #include "managerdatapaths.h" #include "ruqola.h" #include "rocketchataccount.h" #include "messages/message.h" #include "messages/messageattachment.h" #include "testdata.h" #include #include #include QTEST_MAIN(MessageDelegateHelperImageTest) MessageDelegateHelperImageTest::MessageDelegateHelperImageTest(QObject *parent) : QObject(parent) { QStandardPaths::setTestModeEnabled(true); Ruqola::self()->rocketChatAccount()->setAccountName(QStringLiteral("accountName")); } void MessageDelegateHelperImageTest::shouldExtractMessageData() { MessageDelegateHelperImage helper; QStyleOptionViewItem option; QWidget fakeWidget; option.widget = &fakeWidget; Message message; const MessageAttachment msgAttach = testAttachment(); message.setAttachements({msgAttach}); const MessageDelegateHelperImage::ImageLayout layout = helper.layoutImage(&message, option); QCOMPARE(layout.title, msgAttach.title()); QCOMPARE(layout.description, msgAttach.description()); QVERIFY(layout.isShown); } - -void MessageDelegateHelperImageTest::shouldCacheLastFivePixmaps() -{ - MessageDelegateHelperImage helper; - for (int i = 1; i < 7; ++i) { - const QString link = QStringLiteral("link") + QString::number(i); - const QPixmap pix(i * 10, i * 10); - helper.insertCachedPixmap(link, pix); - QCOMPARE(helper.findCachedPixmap(link).height(), i * 10); - QCOMPARE(helper.findCachedPixmap(QStringLiteral("link1")).height(), 10); // we keep using that one - } - QCOMPARE(helper.findCachedPixmap(QStringLiteral("link4")).height(), 40); - QVERIFY(helper.findCachedPixmap(QStringLiteral("link2")).isNull()); // oldest one got evicted -} diff --git a/src/widgets/room/autotests/messagedelegatehelperimagetest.h b/src/widgets/room/autotests/messagedelegatehelperimagetest.h index b5c9d056..76c1cdd4 100644 --- a/src/widgets/room/autotests/messagedelegatehelperimagetest.h +++ b/src/widgets/room/autotests/messagedelegatehelperimagetest.h @@ -1,42 +1,39 @@ /* Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MESSAGEDELEGATEHELPERIMAGETEST_H #define MESSAGEDELEGATEHELPERIMAGETEST_H #include -#include - class MessageDelegateHelperImageTest : public QObject { Q_OBJECT public: explicit MessageDelegateHelperImageTest(QObject *parent = nullptr); ~MessageDelegateHelperImageTest() override = default; private Q_SLOTS: void shouldExtractMessageData(); - void shouldCacheLastFivePixmaps(); private: }; #endif // MESSAGEDELEGATEHELPERIMAGETEST_H diff --git a/src/widgets/room/autotests/messagedelegatehelperimagetest.h b/src/widgets/room/autotests/pixmapcachetest.cpp similarity index 50% copy from src/widgets/room/autotests/messagedelegatehelperimagetest.h copy to src/widgets/room/autotests/pixmapcachetest.cpp index b5c9d056..bfb6e07f 100644 --- a/src/widgets/room/autotests/messagedelegatehelperimagetest.h +++ b/src/widgets/room/autotests/pixmapcachetest.cpp @@ -1,42 +1,48 @@ /* Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef MESSAGEDELEGATEHELPERIMAGETEST_H -#define MESSAGEDELEGATEHELPERIMAGETEST_H +#include "pixmapcachetest.h" +#include "room/delegate/pixmapcache.h" -#include +#include +#include +#include -#include +QTEST_MAIN(PixmapCacheTest) -class MessageDelegateHelperImageTest : public QObject +PixmapCacheTest::PixmapCacheTest(QObject *parent) + : QObject(parent) { - Q_OBJECT -public: - explicit MessageDelegateHelperImageTest(QObject *parent = nullptr); - ~MessageDelegateHelperImageTest() override = default; + QStandardPaths::setTestModeEnabled(true); +} -private Q_SLOTS: - void shouldExtractMessageData(); - void shouldCacheLastFivePixmaps(); - -private: -}; - -#endif // MESSAGEDELEGATEHELPERIMAGETEST_H +void PixmapCacheTest::shouldCacheLastFivePixmaps() +{ + PixmapCache cache; + for (int i = 1; i < 7; ++i) { + const QString link = QStringLiteral("link") + QString::number(i); + const QPixmap pix(i * 10, i * 10); + cache.insertCachedPixmap(link, pix); + QCOMPARE(cache.findCachedPixmap(link).height(), i * 10); + QCOMPARE(cache.findCachedPixmap(QStringLiteral("link1")).height(), 10); // we keep using that one + } + QCOMPARE(cache.findCachedPixmap(QStringLiteral("link4")).height(), 40); + QVERIFY(cache.findCachedPixmap(QStringLiteral("link2")).isNull()); // oldest one got evicted +} diff --git a/src/widgets/room/autotests/messagedelegatehelperimagetest.h b/src/widgets/room/autotests/pixmapcachetest.h similarity index 72% copy from src/widgets/room/autotests/messagedelegatehelperimagetest.h copy to src/widgets/room/autotests/pixmapcachetest.h index b5c9d056..2953e449 100644 --- a/src/widgets/room/autotests/messagedelegatehelperimagetest.h +++ b/src/widgets/room/autotests/pixmapcachetest.h @@ -1,42 +1,37 @@ /* Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef MESSAGEDELEGATEHELPERIMAGETEST_H -#define MESSAGEDELEGATEHELPERIMAGETEST_H +#ifndef PIXMAPCACHETEST_H +#define PIXMAPCACHETEST_H #include -#include - -class MessageDelegateHelperImageTest : public QObject +class PixmapCacheTest : public QObject { Q_OBJECT public: - explicit MessageDelegateHelperImageTest(QObject *parent = nullptr); - ~MessageDelegateHelperImageTest() override = default; + explicit PixmapCacheTest(QObject *parent = nullptr); + ~PixmapCacheTest() override = default; private Q_SLOTS: - void shouldExtractMessageData(); void shouldCacheLastFivePixmaps(); - -private: }; -#endif // MESSAGEDELEGATEHELPERIMAGETEST_H +#endif // PIXMAPCACHETEST_H diff --git a/src/widgets/room/delegate/messagedelegatehelperimage.cpp b/src/widgets/room/delegate/messagedelegatehelperimage.cpp index 17058aa0..12e84377 100644 --- a/src/widgets/room/delegate/messagedelegatehelperimage.cpp +++ b/src/widgets/room/delegate/messagedelegatehelperimage.cpp @@ -1,191 +1,155 @@ /* Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "messagedelegatehelperimage.h" #include "ruqolawidgets_debug.h" #include "ruqola.h" #include "rocketchataccount.h" #include "dialogs/showimagedialog.h" #include #include #include #include #include #include #include static const int margin = 8; // vertical margin between title and pixmap, and between pixmap and description (if any) void MessageDelegateHelperImage::draw(QPainter *painter, const QRect &messageRect, const QModelIndex &index, const QStyleOptionViewItem &option) const { const Message *message = index.data(MessageModel::MessagePointer).value(); ImageLayout layout = layoutImage(message, option); if (!layout.pixmap.isNull()) { // Draw title and buttons painter->drawText(messageRect.x(), messageRect.y() + option.fontMetrics.ascent(), layout.title); const QIcon hideShowIcon = QIcon::fromTheme(layout.isShown ? QStringLiteral("visibility") : QStringLiteral("hint")); hideShowIcon.paint(painter, layout.hideShowButtonRect.translated(messageRect.topLeft())); const QIcon downloadIcon = QIcon::fromTheme(QStringLiteral("cloud-download")); downloadIcon.paint(painter, layout.downloadButtonRect.translated(messageRect.topLeft())); // Draw main pixmap (if shown) int nextY = messageRect.y() + layout.titleSize.height() + margin; if (layout.isShown) { const int imageMaxWidth = messageRect.width(); int imageMaxHeight = messageRect.bottom() - nextY - margin; if (!layout.description.isEmpty()) { imageMaxHeight -= layout.descriptionSize.height() + margin; } const QPixmap scaledPixmap = layout.pixmap.scaled(imageMaxWidth, imageMaxHeight, Qt::KeepAspectRatio); painter->drawPixmap(messageRect.x(), nextY, scaledPixmap); nextY += scaledPixmap.height() + margin; } // Draw description (if any) if (!layout.description.isEmpty()) { painter->drawText(messageRect.x(), nextY + option.fontMetrics.ascent(), layout.description); } } } QSize MessageDelegateHelperImage::sizeHint(const QModelIndex &index, int maxWidth, const QStyleOptionViewItem &option) const { const Message *message = index.data(MessageModel::MessagePointer).value(); const ImageLayout layout = layoutImage(message, option); int height = layout.titleSize.height() + margin; int pixmapWidth = 0; if (layout.isShown) { pixmapWidth = qMin(layout.pixmap.width(), maxWidth); height += qMin(layout.pixmap.height(), 200) + margin; } int descriptionWidth = 0; if (!layout.description.isEmpty()) { descriptionWidth = layout.descriptionSize.width(); height += layout.descriptionSize.height() + margin; } return QSize(qMax(qMax(pixmapWidth, layout.titleSize.width()), descriptionWidth), height); } bool MessageDelegateHelperImage::handleMouseEvent(QMouseEvent *mouseEvent, const QRect &attachmentsRect, const QStyleOptionViewItem &option, const QModelIndex &index) { if (mouseEvent->type() == QEvent::MouseButtonRelease) { const Message *message = index.data(MessageModel::MessagePointer).value(); const QPoint pos = mouseEvent->pos(); ImageLayout layout = layoutImage(message, option); if (layout.hideShowButtonRect.translated(attachmentsRect.topLeft()).contains(pos)) { auto *model = const_cast(index.model()); model->setData(index, !layout.isShown, MessageModel::DisplayAttachment); return true; } else if (layout.downloadButtonRect.translated(attachmentsRect.topLeft()).contains(pos)) { const QString file = QFileDialog::getSaveFileName(const_cast(option.widget), i18n("Save Image")); if (!file.isEmpty()) { layout.pixmap.save(file); } return true; } else if (!layout.pixmap.isNull()) { const int imageY = attachmentsRect.y() + layout.titleSize.height() + margin; int imageMaxHeight = attachmentsRect.bottom() - imageY - margin; if (!layout.description.isEmpty()) { imageMaxHeight -= layout.descriptionSize.height() + margin; } // ## the width is often less than that, due to aspect ratio const QRect imageRect(attachmentsRect.x(), imageY, attachmentsRect.width(), imageMaxHeight); if (imageRect.contains(pos)) { QPointer dlg = new ShowImageDialog(); dlg->setImage(layout.pixmap); dlg->exec(); delete dlg; } return true; } } return false; } -QPixmap MessageDelegateHelperImage::findCachedPixmap(const QString &link) const -{ - auto matchesLink = [&](const CachedImage &cached) { - return cached.link == link; - }; - auto it = std::find_if(mCachedImages.begin(), mCachedImages.end(), matchesLink); - if (it == mCachedImages.end()) { - return QPixmap(); - } - QPixmap result = it->pixmap; // grab pixmap before 'it' gets invalidated - // Move it to the front - if (it != mCachedImages.begin()) { - const auto idx = std::distance(mCachedImages.begin(), it); - mCachedImages.move(idx, 0); - } - return result; -} - -void MessageDelegateHelperImage::insertCachedPixmap(const QString &link, const QPixmap &pixmap) const -{ - mCachedImages.prepend(CachedImage{link, pixmap}); - static const int s_maxCacheSize = 5; - if (mCachedImages.size() > s_maxCacheSize) { - mCachedImages.resize(s_maxCacheSize); - } -} - MessageDelegateHelperImage::ImageLayout MessageDelegateHelperImage::layoutImage(const Message *message, const QStyleOptionViewItem &option) const { ImageLayout layout; if (message->attachements().isEmpty()) { qCWarning(RUQOLAWIDGETS_LOG) << "No attachments in Image message"; return layout; } if (message->attachements().count() > 1) { qCWarning(RUQOLAWIDGETS_LOG) << "Multiple attachments in Image message? Can this happen?"; } const MessageAttachment &msgAttach = message->attachements().at(0); const QUrl url = Ruqola::self()->rocketChatAccount()->attachmentUrl(msgAttach.link()); if (url.isLocalFile()) { const QString path = url.toLocalFile(); - // QPixmapCache is too small for this, let's have our own LRU cache - QPixmap pixmap = findCachedPixmap(path); - if (pixmap.isNull()) { - if (pixmap.load(path)) { - insertCachedPixmap(path, pixmap); - } else { - qCWarning(RUQOLAWIDGETS_LOG) << "Could not load" << path; - } - } - layout.pixmap = pixmap; + layout.pixmap = mPixmapCache.pixmapForLocalFile(path); //or we could do layout.attachment = msgAttach; if we need many fields from it layout.title = msgAttach.title(); layout.description = msgAttach.description(); layout.isShown = message->showAttachment(); layout.titleSize = option.fontMetrics.size(Qt::TextSingleLine, layout.title); layout.descriptionSize = layout.description.isEmpty() ? QSize(0, 0) : option.fontMetrics.size(Qt::TextSingleLine, layout.description); const int buttonMargin = 8; const int iconSize = option.widget->style()->pixelMetric(QStyle::PM_ButtonIconSize); layout.hideShowButtonRect = QRect(layout.titleSize.width() + buttonMargin, 0, iconSize, iconSize); layout.downloadButtonRect = layout.hideShowButtonRect.translated(iconSize + buttonMargin, 0); } return layout; } diff --git a/src/widgets/room/delegate/messagedelegatehelperimage.h b/src/widgets/room/delegate/messagedelegatehelperimage.h index ab7a0cda..eac1a9d9 100644 --- a/src/widgets/room/delegate/messagedelegatehelperimage.h +++ b/src/widgets/room/delegate/messagedelegatehelperimage.h @@ -1,60 +1,55 @@ /* Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MESSAGEDELEGATEHELPERIMAGE_H #define MESSAGEDELEGATEHELPERIMAGE_H #include "messagedelegatehelperbase.h" +#include "pixmapcache.h" #include #include class LIBRUQOLAWIDGETS_TESTS_EXPORT MessageDelegateHelperImage : public MessageDelegateHelperBase { public: void draw(QPainter *painter, const QRect &messageRect, const QModelIndex &index, const QStyleOptionViewItem &option) const override; QSize sizeHint(const QModelIndex &index, int maxWidth, const QStyleOptionViewItem &option) const override; bool handleMouseEvent(QMouseEvent *mouseEvent, const QRect &attachmentsRect, const QStyleOptionViewItem &option, const QModelIndex &index) override; private: friend class MessageDelegateHelperImageTest; - QPixmap findCachedPixmap(const QString &link) const; - void insertCachedPixmap(const QString &link, const QPixmap &pixmap) const; struct ImageLayout { QPixmap pixmap; QString title; QString description; QSize titleSize; QSize descriptionSize; QRect hideShowButtonRect; QRect downloadButtonRect; bool isShown; }; ImageLayout layoutImage(const Message *message, const QStyleOptionViewItem &option) const; - struct CachedImage { - QString link; - QPixmap pixmap; - }; - mutable QVector mCachedImages; // most recent first + mutable PixmapCache mPixmapCache; }; #endif // MESSAGEDELEGATEHELPERIMAGE_H diff --git a/src/widgets/room/delegate/pixmapcache.cpp b/src/widgets/room/delegate/pixmapcache.cpp new file mode 100644 index 00000000..36f0f3f2 --- /dev/null +++ b/src/widgets/room/delegate/pixmapcache.cpp @@ -0,0 +1,62 @@ +/* + Copyright (c) 2020 David Faure + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Library General Public License as published + by the Free Software Foundation; either version 2 of the License or + ( at your option ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "pixmapcache.h" +#include "ruqolawidgets_debug.h" + +QPixmap PixmapCache::pixmapForLocalFile(const QString &path) +{ + QPixmap pixmap = findCachedPixmap(path); + if (pixmap.isNull()) { + if (pixmap.load(path)) { + insertCachedPixmap(path, pixmap); + } else { + qCWarning(RUQOLAWIDGETS_LOG) << "Could not load" << path; + } + } + return pixmap; +} + +QPixmap PixmapCache::findCachedPixmap(const QString &link) +{ + auto matchesLink = [&](const CachedImage &cached) { + return cached.link == link; + }; + auto it = std::find_if(mCachedImages.begin(), mCachedImages.end(), matchesLink); + if (it == mCachedImages.end()) { + return QPixmap(); + } + QPixmap result = it->pixmap; // grab pixmap before 'it' gets invalidated + // Move it to the front + if (it != mCachedImages.begin()) { + const auto idx = std::distance(mCachedImages.begin(), it); + mCachedImages.move(idx, 0); + } + return result; +} + +void PixmapCache::insertCachedPixmap(const QString &link, const QPixmap &pixmap) +{ + mCachedImages.prepend(CachedImage{link, pixmap}); + static const int s_maxCacheSize = 5; + if (mCachedImages.size() > s_maxCacheSize) { + mCachedImages.resize(s_maxCacheSize); + } +} diff --git a/src/widgets/room/autotests/messagedelegatehelperimagetest.h b/src/widgets/room/delegate/pixmapcache.h similarity index 60% copy from src/widgets/room/autotests/messagedelegatehelperimagetest.h copy to src/widgets/room/delegate/pixmapcache.h index b5c9d056..50541a36 100644 --- a/src/widgets/room/autotests/messagedelegatehelperimagetest.h +++ b/src/widgets/room/delegate/pixmapcache.h @@ -1,42 +1,47 @@ /* Copyright (c) 2020 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef MESSAGEDELEGATEHELPERIMAGETEST_H -#define MESSAGEDELEGATEHELPERIMAGETEST_H +#ifndef PIXMAPCACHE_H +#define PIXMAPCACHE_H -#include +#include "libruqolawidgets_private_export.h" -#include +#include +#include -class MessageDelegateHelperImageTest : public QObject +// QPixmapCache is too small for the big images in messages, let's have our own LRU cache +class LIBRUQOLAWIDGETS_TESTS_EXPORT PixmapCache { - Q_OBJECT public: - explicit MessageDelegateHelperImageTest(QObject *parent = nullptr); - ~MessageDelegateHelperImageTest() override = default; - -private Q_SLOTS: - void shouldExtractMessageData(); - void shouldCacheLastFivePixmaps(); + QPixmap pixmapForLocalFile(const QString &path); private: + friend class PixmapCacheTest; + QPixmap findCachedPixmap(const QString &link); + void insertCachedPixmap(const QString &link, const QPixmap &pixmap); + + struct CachedImage { + QString link; + QPixmap pixmap; + }; + QVector mCachedImages; // most recent first }; -#endif // MESSAGEDELEGATEHELPERIMAGETEST_H +#endif // PIXMAPCACHE_H