text inside of message is selectable again, links are clickable, some refactor, some bugfixes

This commit is contained in:
Blue 2021-05-03 14:23:41 +03:00
parent 05d6761baa
commit f34289399e
14 changed files with 115 additions and 292 deletions

View File

@ -377,6 +377,7 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
} }
if (added == 0 && wasEmpty) { if (added == 0 && wasEmpty) {
archiveState = empty; archiveState = empty;
nextRequest();
break; break;
} }
if (requestedCount != -1) { if (requestedCount != -1) {
@ -397,7 +398,7 @@ void Core::RosterItem::flushMessagesToArchive(bool finished, const QString& firs
} catch (const Archive::Empty& e) { } catch (const Archive::Empty& e) {
} }
if (!found || requestedCount > responseCache.size()) { if (!found || requestedCount > int(responseCache.size())) {
if (archiveState == complete) { if (archiveState == complete) {
nextRequest(); nextRequest();
} else { } else {

View File

@ -14,6 +14,7 @@ if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})
endif() endif()
add_subdirectory(utils)
add_subdirectory(widgets) add_subdirectory(widgets)
set(squawkUI_SRC set(squawkUI_SRC
@ -31,19 +32,6 @@ set(squawkUI_SRC
models/reference.cpp models/reference.cpp
models/messagefeed.cpp models/messagefeed.cpp
models/element.cpp models/element.cpp
utils/messageline.cpp
utils/message.cpp
utils/resizer.cpp
utils/image.cpp
utils/flowlayout.cpp
utils/badge.cpp
utils/progress.cpp
utils/comboboxdelegate.cpp
utils/dropshadoweffect.cpp
utils/feedview.cpp
utils/messagedelegate.cpp
utils/eb.cpp
utils/shadowoverlay.cpp
) )
# Tell CMake to create the helloworld executable # Tell CMake to create the helloworld executable
@ -51,5 +39,6 @@ add_library(squawkUI ${squawkUI_SRC})
# Use the Widgets module from Qt 5. # Use the Widgets module from Qt 5.
target_link_libraries(squawkUI squawkWidgets) target_link_libraries(squawkUI squawkWidgets)
target_link_libraries(squawkUI squawkUIUtils)
target_link_libraries(squawkUI Qt5::Widgets) target_link_libraries(squawkUI Qt5::Widgets)
target_link_libraries(squawkUI Qt5::DBus) target_link_libraries(squawkUI Qt5::DBus)

32
ui/utils/CMakeLists.txt Normal file
View File

@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.3)
project(squawkUIUtils)
# Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON)
# Instruct CMake to create code from Qt designer ui files
set(CMAKE_AUTOUIC ON)
# Find the QtWidgets library
find_package(Qt5 CONFIG REQUIRED COMPONENTS Widgets Core)
set(squawkUIUtils_SRC
# messageline.cpp
# message.cpp
resizer.cpp
# image.cpp
flowlayout.cpp
badge.cpp
progress.cpp
comboboxdelegate.cpp
feedview.cpp
messagedelegate.cpp
exponentialblur.cpp
shadowoverlay.cpp
)
# Tell CMake to create the helloworld executable
add_library(squawkUIUtils ${squawkUIUtils_SRC})
# Use the Widgets module from Qt 5.
target_link_libraries(squawkUIUtils squawkWidgets)
target_link_libraries(squawkUIUtils Qt5::Widgets)

View File

@ -1,145 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dropshadoweffect.h"
PixmapFilter::PixmapFilter(QObject* parent):QObject(parent) {}
PixmapFilter::~PixmapFilter(){}
QRectF PixmapFilter::boundingRectFor(const QRectF &rect) const {return rect;}
PixmapDropShadowFilter::PixmapDropShadowFilter(QObject *parent):
PixmapFilter(parent),
mColor(63, 63, 63, 180),
mRadius(1),
mThickness(2),
top(true),
right(true),
bottom(true),
left(true){}
PixmapDropShadowFilter::~PixmapDropShadowFilter() {}
qreal PixmapDropShadowFilter::blurRadius() const {return mRadius;}
void PixmapDropShadowFilter::setBlurRadius(qreal radius) {mRadius = radius;}
QColor PixmapDropShadowFilter::color() const {return mColor;}
void PixmapDropShadowFilter::setColor(const QColor &color) {mColor = color;}
qreal PixmapDropShadowFilter::thickness() const {return mThickness;}
void PixmapDropShadowFilter::setThickness(qreal thickness) {mThickness = thickness;}
void PixmapDropShadowFilter::setFrame(bool ptop, bool pright, bool pbottom, bool pleft)
{
top = ptop;
right = pright;
bottom = pbottom;
left = pleft;
}
void DropShadowEffect::setThickness(qreal thickness)
{
if (filter.thickness() == thickness)
return;
filter.setThickness(thickness);
update();
}
void PixmapDropShadowFilter::draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src) const
{
if (px.isNull())
return;
QImage tmp({px.width(), px.height() + int(mThickness)}, QImage::Format_ARGB32_Premultiplied);
tmp.setDevicePixelRatio(px.devicePixelRatioF());
tmp.fill(0);
QPainter tmpPainter(&tmp);
tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
if (top) {
QRectF shadow(0, 0, px.width(), mThickness);
tmpPainter.fillRect(shadow, mColor);
}
if (right) {
QRectF shadow(px.width() - mThickness, 0, mThickness, px.height());
tmpPainter.fillRect(shadow, mColor);
}
if (bottom) {
QRectF shadow(0, px.height() - mThickness, px.width(), mThickness * 2); //i have no idea why, but it leaves some unpainted stripe without some spare space
tmpPainter.fillRect(shadow, mColor);
}
if (left) {
QRectF shadow(0, 0, mThickness, px.height());
tmpPainter.fillRect(shadow, mColor);
}
Utils::exponentialblur(tmp, mRadius, false, 0);
tmpPainter.end();
// Draw the actual pixmap...
p->drawPixmap(pos, px, src);
// draw the blurred drop shadow...
p->drawImage(pos, tmp);
}
qreal DropShadowEffect::blurRadius() const {return filter.blurRadius();}
void DropShadowEffect::setBlurRadius(qreal blurRadius)
{
if (qFuzzyCompare(filter.blurRadius(), blurRadius))
return;
filter.setBlurRadius(blurRadius);
updateBoundingRect();
emit blurRadiusChanged(blurRadius);
}
void DropShadowEffect::setFrame(bool top, bool right, bool bottom, bool left)
{
filter.setFrame(top, right, bottom, left);
update();
}
QColor DropShadowEffect::color() const {return filter.color();}
void DropShadowEffect::setColor(const QColor &color)
{
if (filter.color() == color)
return;
filter.setColor(color);
update();
emit colorChanged(color);
}
void DropShadowEffect::draw(QPainter* painter)
{
if (filter.blurRadius() <= 0 && filter.thickness() == 0) {
drawSource(painter);
return;
}
PixmapPadMode mode = PadToEffectiveBoundingRect;
// Draw pixmap in device coordinates to avoid pixmap scaling.
QPoint offset;
const QPixmap pixmap = sourcePixmap(Qt::DeviceCoordinates, &offset, mode);
if (pixmap.isNull())
return;
QTransform restoreTransform = painter->worldTransform();
painter->setWorldTransform(QTransform());
filter.draw(painter, offset, pixmap);
painter->setWorldTransform(restoreTransform);
}

View File

@ -1,95 +0,0 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DROPSHADOWEFFECT_H
#define DROPSHADOWEFFECT_H
#include <QGraphicsEffect>
#include <QPainter>
#include <QPointF>
#include <QColor>
#include "eb.h"
class PixmapFilter : public QObject
{
Q_OBJECT
public:
PixmapFilter(QObject *parent = nullptr);
virtual ~PixmapFilter() = 0;
virtual QRectF boundingRectFor(const QRectF &rect) const;
virtual void draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &srcRect = QRectF()) const = 0;
};
class PixmapDropShadowFilter : public PixmapFilter
{
Q_OBJECT
public:
PixmapDropShadowFilter(QObject *parent = nullptr);
~PixmapDropShadowFilter();
void draw(QPainter *p, const QPointF &pos, const QPixmap &px, const QRectF &src = QRectF()) const override;
qreal blurRadius() const;
void setBlurRadius(qreal radius);
QColor color() const;
void setColor(const QColor &color);
qreal thickness() const;
void setThickness(qreal thickness);
void setFrame(bool top, bool right, bool bottom, bool left);
protected:
QColor mColor;
qreal mRadius;
qreal mThickness;
bool top;
bool right;
bool bottom;
bool left;
};
class DropShadowEffect : public QGraphicsEffect
{
Q_OBJECT
public:
qreal blurRadius() const;
QColor color() const;
void setFrame(bool top, bool right, bool bottom, bool left);
void setThickness(qreal thickness);
signals:
void blurRadiusChanged(qreal blurRadius);
void colorChanged(const QColor &color);
public slots:
void setBlurRadius(qreal blurRadius);
void setColor(const QColor &color);
protected:
void draw(QPainter * painter) override;
protected:
PixmapDropShadowFilter filter;
};
#endif // DROPSHADOWEFFECT_H

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "eb.h" #include "exponentialblur.h"
static const int tileSize = 32; static const int tileSize = 32;
template <class T> template <class T>

View File

@ -16,8 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef EB_H #ifndef EXPONENTIALBLUR_H
#define EB_H #define EXPONENTIALBLUR_H
#include <QObject> #include <QObject>
#include <QImage> #include <QImage>
@ -31,4 +31,4 @@ namespace Utils {
void exponentialblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0); void exponentialblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0);
}; };
#endif // EB_H #endif // EXPONENTIALBLUR_H

View File

@ -25,7 +25,6 @@
#include "messagedelegate.h" #include "messagedelegate.h"
#include "ui/models/messagefeed.h" #include "ui/models/messagefeed.h"
#include "eb.h"
constexpr int maxMessageHeight = 10000; constexpr int maxMessageHeight = 10000;
constexpr int approximateSingleMessageHeight = 20; constexpr int approximateSingleMessageHeight = 20;
@ -339,6 +338,7 @@ void FeedView::positionProgress()
progressPosition -= hint.offset + hint.height; progressPosition -= hint.offset + hint.height;
} }
progressPosition += vo; progressPosition += vo;
progressPosition = qMin(progressPosition, 0);
progress.move((width() - progressSize) / 2, progressPosition); progress.move((width() - progressSize) / 2, progressPosition);
} }

View File

@ -31,20 +31,21 @@ constexpr int statusIconSize = 16;
constexpr int maxAttachmentHeight = 500; constexpr int maxAttachmentHeight = 500;
MessageDelegate::MessageDelegate(QObject* parent): MessageDelegate::MessageDelegate(QObject* parent):
QStyledItemDelegate(parent), QStyledItemDelegate(parent),
bodyFont(), bodyFont(),
nickFont(), nickFont(),
dateFont(), dateFont(),
bodyMetrics(bodyFont), bodyMetrics(bodyFont),
nickMetrics(nickFont), nickMetrics(nickFont),
dateMetrics(dateFont), dateMetrics(dateFont),
buttonHeight(0), buttonHeight(0),
barHeight(0), barHeight(0),
buttons(new std::map<QString, FeedButton*>()), buttons(new std::map<QString, FeedButton*>()),
bars(new std::map<QString, QProgressBar*>()), bars(new std::map<QString, QProgressBar*>()),
statusIcons(new std::map<QString, QLabel*>()), statusIcons(new std::map<QString, QLabel*>()),
idsToKeep(new std::set<QString>()), bodies(new std::map<QString, QLabel*>()),
clearingWidgets(false) idsToKeep(new std::set<QString>()),
clearingWidgets(false)
{ {
QPushButton btn; QPushButton btn;
buttonHeight = btn.sizeHint().height(); buttonHeight = btn.sizeHint().height();
@ -67,9 +68,14 @@ MessageDelegate::~MessageDelegate()
delete pair.second; delete pair.second;
} }
for (const std::pair<const QString, QLabel*>& pair: *bodies){
delete pair.second;
}
delete idsToKeep; delete idsToKeep;
delete buttons; delete buttons;
delete bars; delete bars;
delete bodies;
} }
void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
@ -105,8 +111,10 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
opt.rect = messageRect; opt.rect = messageRect;
QSize messageSize(0, 0); QSize messageSize(0, 0);
QSize bodySize(0, 0);
if (data.text.size() > 0) { if (data.text.size() > 0) {
messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size(); messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size();
bodySize = messageSize;
} }
messageSize.rheight() += nickMetrics.lineSpacing(); messageSize.rheight() += nickMetrics.lineSpacing();
messageSize.rheight() += dateMetrics.height(); messageSize.rheight() += dateMetrics.height();
@ -146,12 +154,21 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
} }
painter->restore(); painter->restore();
int messageLeft = 10000; //TODO int messageLeft = INT16_MAX;
QWidget* vp = static_cast<QWidget*>(painter->device());
if (data.text.size() > 0) { if (data.text.size() > 0) {
painter->setFont(bodyFont); QLabel* body = getBody(data);
painter->drawText(opt.rect, opt.displayAlignment | Qt::TextWordWrap, data.text, &rect); body->setParent(vp);
opt.rect.adjust(0, rect.height() + textMargin, 0, 0); body->setMaximumWidth(bodySize.width());
messageLeft = rect.x(); body->setMinimumWidth(bodySize.width());
body->setAlignment(opt.displayAlignment);
messageLeft = opt.rect.x();
if (data.sentByMe) {
messageLeft = opt.rect.topRight().x() - bodySize.width();
}
body->move(messageLeft, opt.rect.y());
body->show();
opt.rect.adjust(0, bodySize.height() + textMargin, 0, 0);
} }
painter->setFont(dateFont); painter->setFont(dateFont);
QColor q = painter->pen().color(); QColor q = painter->pen().color();
@ -164,7 +181,6 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
} }
QLabel* statusIcon = getStatusIcon(data); QLabel* statusIcon = getStatusIcon(data);
QWidget* vp = static_cast<QWidget*>(painter->device());
statusIcon->setParent(vp); statusIcon->setParent(vp);
statusIcon->move(messageLeft, opt.rect.y()); statusIcon->move(messageLeft, opt.rect.y());
statusIcon->show(); statusIcon->show();
@ -280,15 +296,7 @@ void MessageDelegate::paintButton(QPushButton* btn, QPainter* painter, bool sent
void MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const void MessageDelegate::paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const
{ {
QPoint start = option.rect.topLeft(); QPoint start = option.rect.topLeft();
//QWidget* vp = static_cast<QWidget*>(painter->device());
// if (bar->parent() != vp) {
// bar->setParent(vp);
// }
// bar->move(start);
bar->resize(option.rect.width(), barHeight); bar->resize(option.rect.width(), barHeight);
// bar->show();
painter->translate(start); painter->translate(start);
bar->render(painter, QPoint(), QRegion(), QWidget::DrawChildren); bar->render(painter, QPoint(), QRegion(), QWidget::DrawChildren);
@ -410,6 +418,27 @@ QLabel * MessageDelegate::getStatusIcon(const Models::FeedItem& data) const
return result; return result;
} }
QLabel * MessageDelegate::getBody(const Models::FeedItem& data) const
{
std::map<QString, QLabel*>::const_iterator itr = bodies->find(data.id);
QLabel* result = 0;
if (itr != bodies->end()) {
result = itr->second;
} else {
result = new QLabel();
result->setFont(bodyFont);
result->setWordWrap(true);
result->setOpenExternalLinks(true);
result->setTextInteractionFlags(result->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
bodies->insert(std::make_pair(data.id, result));
}
result->setText(Shared::processMessageBody(data.text));
return result;
}
void MessageDelegate::beginClearWidgets() void MessageDelegate::beginClearWidgets()
{ {
idsToKeep->clear(); idsToKeep->clear();
@ -422,6 +451,7 @@ void MessageDelegate::endClearWidgets()
std::set<QString> toRemoveButtons; std::set<QString> toRemoveButtons;
std::set<QString> toRemoveBars; std::set<QString> toRemoveBars;
std::set<QString> toRemoveIcons; std::set<QString> toRemoveIcons;
std::set<QString> toRemoveBodies;
for (const std::pair<const QString, FeedButton*>& pair: *buttons) { for (const std::pair<const QString, FeedButton*>& pair: *buttons) {
if (idsToKeep->find(pair.first) == idsToKeep->end()) { if (idsToKeep->find(pair.first) == idsToKeep->end()) {
delete pair.second; delete pair.second;
@ -440,6 +470,12 @@ void MessageDelegate::endClearWidgets()
toRemoveIcons.insert(pair.first); toRemoveIcons.insert(pair.first);
} }
} }
for (const std::pair<const QString, QLabel*>& pair: *bodies) {
if (idsToKeep->find(pair.first) == idsToKeep->end()) {
delete pair.second;
toRemoveBodies.insert(pair.first);
}
}
for (const QString& key : toRemoveButtons) { for (const QString& key : toRemoveButtons) {
buttons->erase(key); buttons->erase(key);
@ -450,6 +486,9 @@ void MessageDelegate::endClearWidgets()
for (const QString& key : toRemoveIcons) { for (const QString& key : toRemoveIcons) {
statusIcons->erase(key); statusIcons->erase(key);
} }
for (const QString& key : toRemoveBodies) {
bodies->erase(key);
}
idsToKeep->clear(); idsToKeep->clear();
clearingWidgets = false; clearingWidgets = false;

View File

@ -32,6 +32,7 @@
#include "shared/icons.h" #include "shared/icons.h"
#include "shared/global.h" #include "shared/global.h"
#include "shared/utils.h"
namespace Models { namespace Models {
struct FeedItem; struct FeedItem;
@ -64,6 +65,7 @@ protected:
QPushButton* getButton(const Models::FeedItem& data) const; QPushButton* getButton(const Models::FeedItem& data) const;
QProgressBar* getBar(const Models::FeedItem& data) const; QProgressBar* getBar(const Models::FeedItem& data) const;
QLabel* getStatusIcon(const Models::FeedItem& data) const; QLabel* getStatusIcon(const Models::FeedItem& data) const;
QLabel* getBody(const Models::FeedItem& data) const;
void clearHelperWidget(const Models::FeedItem& data) const; void clearHelperWidget(const Models::FeedItem& data) const;
QSize calculateAttachSize(const QString& path, const QRect& bounds) const; QSize calculateAttachSize(const QString& path, const QRect& bounds) const;
QSize constrainAttachSize(QSize src, QSize bounds) const; QSize constrainAttachSize(QSize src, QSize bounds) const;
@ -91,6 +93,7 @@ private:
std::map<QString, FeedButton*>* buttons; std::map<QString, FeedButton*>* buttons;
std::map<QString, QProgressBar*>* bars; std::map<QString, QProgressBar*>* bars;
std::map<QString, QLabel*>* statusIcons; std::map<QString, QLabel*>* statusIcons;
std::map<QString, QLabel*>* bodies;
std::set<QString>* idsToKeep; std::set<QString>* idsToKeep;
bool clearingWidgets; bool clearingWidgets;

View File

@ -26,7 +26,7 @@
#include <QPaintEvent> #include <QPaintEvent>
#include <QResizeEvent> #include <QResizeEvent>
#include <ui/utils/eb.h> #include <ui/utils/exponentialblur.h>
/** /**
* @todo write docs * @todo write docs

View File

@ -25,7 +25,7 @@ add_library(squawkWidgets ${squawkWidgets_SRC})
# Use the Widgets module from Qt 5. # Use the Widgets module from Qt 5.
target_link_libraries(squawkWidgets vCardUI) target_link_libraries(squawkWidgets vCardUI)
target_link_libraries(squawkWidgets squawkUIUtils)
target_link_libraries(squawkWidgets Qt5::Widgets) target_link_libraries(squawkWidgets Qt5::Widgets)
target_link_libraries(squawkWidgets squawkUI)
qt5_use_modules(squawkWidgets Core Widgets) qt5_use_modules(squawkWidgets Core Widgets)

View File

@ -18,7 +18,6 @@
#include "conversation.h" #include "conversation.h"
#include "ui_conversation.h" #include "ui_conversation.h"
#include "ui/utils/dropshadoweffect.h"
#include <QDebug> #include <QDebug>
#include <QScrollBar> #include <QScrollBar>

View File

@ -24,12 +24,12 @@
#include <QMap> #include <QMap>
#include <QMimeData> #include <QMimeData>
#include <QFileInfo> #include <QFileInfo>
#include <QGraphicsOpacityEffect>
#include "shared/message.h" #include "shared/message.h"
#include "order.h" #include "order.h"
#include "ui/models/account.h" #include "ui/models/account.h"
#include "ui/models/roster.h" #include "ui/models/roster.h"
#include "ui/utils/messageline.h"
#include "ui/utils/flowlayout.h" #include "ui/utils/flowlayout.h"
#include "ui/utils/badge.h" #include "ui/utils/badge.h"
#include "ui/utils/feedview.h" #include "ui/utils/feedview.h"