diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e78c401..cf2f2724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## Unversioned +- Major: We now support image thumbnails coming from the link resolver. This feature is off by default and can be enabled in the settings with the "Show link thumbnail" setting. This feature also requires the "Show link info when hovering" setting to be enabled (#1664) - Major: Added image upload functionality to i.nuuls.com. This works by dragging and dropping an image into a split, or pasting an image into the text edit field. (#1332) - Minor: Emotes in the emote popup are now sorted in the same order as the tab completion (#1549) - Minor: Removed "Online Logs" functionality as services are shut down (#1640) diff --git a/src/messages/MessageBuilder.cpp b/src/messages/MessageBuilder.cpp index c215a60a..e877161c 100644 --- a/src/messages/MessageBuilder.cpp +++ b/src/messages/MessageBuilder.cpp @@ -419,7 +419,8 @@ void MessageBuilder::addLink(const QString &origLink, LinkResolver::getLinkInfo( matchedLink, nullptr, [weakMessage = this->weakOf(), linkMELowercase, linkMEOriginal, - matchedLink](QString tooltipText, Link originalLink) { + matchedLink](QString tooltipText, Link originalLink, + ImagePtr thumbnail) { auto shared = weakMessage.lock(); if (!shared) { @@ -436,6 +437,12 @@ void MessageBuilder::addLink(const QString &origLink, linkMELowercase->setLink(originalLink)->updateLink(); linkMEOriginal->setLink(originalLink)->updateLink(); } + linkMELowercase->setThumbnail(thumbnail); + linkMELowercase->setThumbnailType( + MessageElement::ThumbnailType::Link_Thumbnail); + linkMEOriginal->setThumbnail(thumbnail); + linkMEOriginal->setThumbnailType( + MessageElement::ThumbnailType::Link_Thumbnail); }); } diff --git a/src/messages/MessageElement.cpp b/src/messages/MessageElement.cpp index d49ca4ad..89c0b1c4 100644 --- a/src/messages/MessageElement.cpp +++ b/src/messages/MessageElement.cpp @@ -40,6 +40,18 @@ MessageElement *MessageElement::setTooltip(const QString &tooltip) return this; } +MessageElement *MessageElement::setThumbnail(const ImagePtr &thumbnail) +{ + this->thumbnail_ = thumbnail; + return this; +} + +MessageElement *MessageElement::setThumbnailType(const ThumbnailType type) +{ + this->thumbnailType_ = type; + return this; +} + MessageElement *MessageElement::setTrailingSpace(bool value) { this->trailingSpace = value; @@ -51,6 +63,16 @@ const QString &MessageElement::getTooltip() const return this->tooltip_; } +const ImagePtr &MessageElement::getThumbnail() const +{ + return this->thumbnail_; +} + +const MessageElement::ThumbnailType &MessageElement::getThumbnailType() const +{ + return this->thumbnailType_; +} + const Link &MessageElement::getLink() const { return this->link_; diff --git a/src/messages/MessageElement.hpp b/src/messages/MessageElement.hpp index 110cf917..f5f546df 100644 --- a/src/messages/MessageElement.hpp +++ b/src/messages/MessageElement.hpp @@ -122,14 +122,23 @@ public: Update_Images = 4, Update_All = Update_Text | Update_Emotes | Update_Images }; + enum ThumbnailType : char { + Link_Thumbnail = 1, + }; virtual ~MessageElement(); MessageElement *setLink(const Link &link); MessageElement *setText(const QString &text); MessageElement *setTooltip(const QString &tooltip); + MessageElement *setThumbnailType(const ThumbnailType type); + MessageElement *setThumbnail(const ImagePtr &thumbnail); + MessageElement *setTrailingSpace(bool value); const QString &getTooltip() const; + const ImagePtr &getThumbnail() const; + const ThumbnailType &getThumbnailType() const; + const Link &getLink() const; bool hasTrailingSpace() const; MessageElementFlags getFlags() const; @@ -148,6 +157,8 @@ private: QString text_; Link link_; QString tooltip_; + ImagePtr thumbnail_; + ThumbnailType thumbnailType_; MessageElementFlags flags_; }; diff --git a/src/providers/LinkResolver.cpp b/src/providers/LinkResolver.cpp index 50f6c971..41f511eb 100644 --- a/src/providers/LinkResolver.cpp +++ b/src/providers/LinkResolver.cpp @@ -3,6 +3,7 @@ #include "common/Common.hpp" #include "common/Env.hpp" #include "common/NetworkRequest.hpp" +#include "messages/Image.hpp" #include "messages/Link.hpp" #include "singletons/Settings.hpp" @@ -12,11 +13,11 @@ namespace chatterino { void LinkResolver::getLinkInfo( const QString url, QObject *caller, - std::function successCallback) + std::function successCallback) { if (!getSettings()->linkInfoTooltip) { - successCallback("No link info loaded", Link(Link::Url, url)); + successCallback("No link info loaded", Link(Link::Url, url), nullptr); return; } // Uncomment to test crashes @@ -25,30 +26,35 @@ void LinkResolver::getLinkInfo( QUrl::toPercentEncoding(url, "", "/:")))) .caller(caller) .timeout(30000) - .onSuccess([successCallback, url](auto result) mutable -> Outcome { - auto root = result.parseJson(); - auto statusCode = root.value("status").toInt(); - QString response = QString(); - QString linkString = url; - if (statusCode == 200) - { - response = root.value("tooltip").toString(); - if (getSettings()->unshortLinks) + .onSuccess( + [successCallback, url](NetworkResult result) mutable -> Outcome { + auto root = result.parseJson(); + auto statusCode = root.value("status").toInt(); + QString response = QString(); + QString linkString = url; + ImagePtr thumbnail = nullptr; + if (statusCode == 200) { - linkString = root.value("link").toString(); + response = root.value("tooltip").toString(); + thumbnail = + Image::fromUrl({root.value("thumbnail").toString()}); + if (getSettings()->unshortLinks) + { + linkString = root.value("link").toString(); + } } - } - else - { - response = root.value("message").toString(); - } - successCallback(QUrl::fromPercentEncoding(response.toUtf8()), - Link(Link::Url, linkString)); + else + { + response = root.value("message").toString(); + } + successCallback(QUrl::fromPercentEncoding(response.toUtf8()), + Link(Link::Url, linkString), thumbnail); - return Success; - }) + return Success; + }) .onError([successCallback, url](auto /*result*/) { - successCallback("No link info found", Link(Link::Url, url)); + successCallback("No link info found", Link(Link::Url, url), + nullptr); }) .execute(); // }); diff --git a/src/providers/LinkResolver.hpp b/src/providers/LinkResolver.hpp index d4e9804f..d1b24d69 100644 --- a/src/providers/LinkResolver.hpp +++ b/src/providers/LinkResolver.hpp @@ -3,6 +3,7 @@ #include #include +#include "messages/Image.hpp" #include "messages/Link.hpp" namespace chatterino { @@ -10,8 +11,9 @@ namespace chatterino { class LinkResolver { public: - static void getLinkInfo(const QString url, QObject *caller, - std::function callback); + static void getLinkInfo( + const QString url, QObject *caller, + std::function callback); }; } // namespace chatterino diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 11927120..95f957fd 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -161,6 +161,7 @@ public: /// Links BoolSetting linksDoubleClickOnly = {"/links/doubleClickToOpen", false}; BoolSetting linkInfoTooltip = {"/links/linkInfoTooltip", false}; + IntSetting thumbnailSize = {"/appearance/thumbnailSize", 0}; BoolSetting unshortLinks = {"/links/unshortLinks", false}; BoolSetting lowercaseDomains = {"/links/linkLowercase", true}; diff --git a/src/singletons/TooltipPreviewImage.cpp b/src/singletons/TooltipPreviewImage.cpp index ebb0fe6a..2885d934 100644 --- a/src/singletons/TooltipPreviewImage.cpp +++ b/src/singletons/TooltipPreviewImage.cpp @@ -38,6 +38,14 @@ void TooltipPreviewImage::setImage(ImagePtr image) this->refreshTooltipWidgetPixmap(); } +void TooltipPreviewImage::setImageScale(int w, int h) +{ + this->imageWidth_ = w; + this->imageHeight_ = h; + + this->refreshTooltipWidgetPixmap(); +} + void TooltipPreviewImage::refreshTooltipWidgetPixmap() { auto tooltipWidget = TooltipWidget::instance(); @@ -46,7 +54,17 @@ void TooltipPreviewImage::refreshTooltipWidgetPixmap() { if (auto pixmap = this->image_->pixmapOrLoad()) { - tooltipWidget->setImage(*pixmap); + if (this->imageWidth_ != 0 && this->imageHeight_) + { + tooltipWidget->setImage(pixmap->scaled(this->imageWidth_, + this->imageHeight_, + Qt::KeepAspectRatio)); + } + else + { + tooltipWidget->setImage(*pixmap); + } + this->attemptRefresh = false; } else diff --git a/src/singletons/TooltipPreviewImage.hpp b/src/singletons/TooltipPreviewImage.hpp index 0d1f6f07..aeccdf9d 100644 --- a/src/singletons/TooltipPreviewImage.hpp +++ b/src/singletons/TooltipPreviewImage.hpp @@ -9,6 +9,7 @@ class TooltipPreviewImage public: static TooltipPreviewImage &instance(); void setImage(ImagePtr image); + void setImageScale(int w, int h); TooltipPreviewImage(const TooltipPreviewImage &) = delete; @@ -17,6 +18,9 @@ private: private: ImagePtr image_ = nullptr; + int imageWidth_ = 0; + int imageHeight_ = 0; + std::vector connections_; // attemptRefresh is set to true in case we want to preview an image that has not loaded yet (if pixmapOrLoad fails) diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index 3a5ad065..ab25a236 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -1259,6 +1259,7 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) else { auto &tooltipPreviewImage = TooltipPreviewImage::instance(); + tooltipPreviewImage.setImageScale(0, 0); auto emoteElement = dynamic_cast( &hoverLayoutElement->getCreator()); auto badgeElement = dynamic_cast( @@ -1288,7 +1289,22 @@ void ChannelView::mouseMoveEvent(QMouseEvent *event) } else { - tooltipPreviewImage.setImage(nullptr); + auto element = &hoverLayoutElement->getCreator(); + auto thumbnailSize = getSettings()->thumbnailSize; + if (thumbnailSize == 0) + { + tooltipPreviewImage.setImage(nullptr); + } + else + { + tooltipPreviewImage.setImage(element->getThumbnail()); + if (element->getThumbnailType() == + MessageElement::ThumbnailType::Link_Thumbnail) + { + tooltipPreviewImage.setImageScale(thumbnailSize, + thumbnailSize); + } + } } tooltipWidget->moveTo(this, event->globalPos()); diff --git a/src/widgets/settingspages/GeneralPage.cpp b/src/widgets/settingspages/GeneralPage.cpp index fb8fd152..6353b760 100644 --- a/src/widgets/settingspages/GeneralPage.cpp +++ b/src/widgets/settingspages/GeneralPage.cpp @@ -521,6 +521,31 @@ void GeneralPage::initLayout(SettingsLayout &layout) }, [](auto args) { return fuzzyToFloat(args.value, 63.f); }); layout.addCheckbox("Show link info when hovering", s.linkInfoTooltip); + layout.addDropdown( + "Show link thumbnail", {"Off", "Small", "Medium", "Large"}, + s.thumbnailSize, + [](auto val) { + if (val == 0) + return QString("Off"); + else if (val == 100) + return QString("Small"); + else if (val == 200) + return QString("Medium"); + else if (val == 300) + return QString("Large"); + else + return QString::number(val); + }, + [](auto args) { + if (args.value == "Small") + return 100; + else if (args.value == "Medium") + return 200; + else if (args.value == "Large") + return 300; + + return fuzzyToInt(args.value, 0); + }); layout.addCheckbox("Double click to open links and other elements in chat", s.linksDoubleClickOnly); layout.addCheckbox("Unshorten links", s.unshortLinks);