forked from blue/squawk
0.2.0 finalization
This commit is contained in:
parent
8a2658e4fc
commit
4d3ba6b11f
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,19 +1,28 @@
|
||||
# Changelog
|
||||
|
||||
## Squawk 0.2.0 (Unreleased)
|
||||
## Squawk 0.2.0 (Jan 10, 2022)
|
||||
### Bug fixes
|
||||
- carbon copies switches on again after reconnection
|
||||
- requesting the history of the current chat after reconnection
|
||||
- global availability (in drop down list) gets restored after reconnection
|
||||
- status icon in active chat changes when presence of the pen pal changes
|
||||
- infinite progress when open the dialogue with something that has no history to show
|
||||
- fallback icons for buttons, when no supported theme is installed (shunf4)
|
||||
- better handling messages with no id (shunf4)
|
||||
- removed dependency: uuid, now it's on Qt (shunf4)
|
||||
- better requesting latest history (shunf4)
|
||||
|
||||
### Improvements
|
||||
- slightly reduced the traffic on the startup by not requesting history of all MUCs
|
||||
- completely rewritten message feed, now it works way faster
|
||||
- completely rewritten message feed, now it works way faster and looks cooler
|
||||
- OPTIONAL RUNTIME dependency: "KIO Widgets" that is supposed to allow you to open a file in your default file manager
|
||||
- show in folder now is supposed to try it's best to show file in folder, even you don't have KIO installed
|
||||
- once uploaded local files don't get second time uploaded - the remote URL is reused
|
||||
- way better compilation time (vae)
|
||||
|
||||
### New features
|
||||
- pasting images from clipboard to attachment (shunf4)
|
||||
- possible compilation for windows and macOS (shunf4)
|
||||
|
||||
## Squawk 0.1.5 (Jul 29, 2020)
|
||||
### Bug fixes
|
||||
|
@ -4,13 +4,13 @@
|
||||
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
|
||||
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
||||
|
||||
![Squawk screenshot](https://macaw.me/images/squawk/0.1.4.png)
|
||||
![Squawk screenshot](https://macaw.me/images/squawk/0.2.0.png)
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- QT 5.12 *(lower versions might work but it wasn't tested)*
|
||||
- lmdb
|
||||
- CMake 3.0 or higher
|
||||
- CMake 3.3 or higher
|
||||
- qxmpp 1.1.0 or higher
|
||||
- KDE Frameworks: kwallet (optional)
|
||||
- KDE Frameworks: KIO (optional)
|
||||
|
@ -54,7 +54,7 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
QApplication::setApplicationName("squawk");
|
||||
QApplication::setApplicationDisplayName("Squawk");
|
||||
QApplication::setApplicationVersion("0.1.5");
|
||||
QApplication::setApplicationVersion("0.2.0");
|
||||
|
||||
QTranslator qtTranslator;
|
||||
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Maintainer: Yury Gubich <blue@macaw.me>
|
||||
pkgname=squawk
|
||||
pkgver=0.1.5
|
||||
pkgver=0.2.0
|
||||
pkgrel=1
|
||||
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
|
||||
arch=('i686' 'x86_64')
|
||||
@ -11,7 +11,7 @@ makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools')
|
||||
optdepends=('kwallet: secure password storage (requires rebuild)')
|
||||
|
||||
source=("$pkgname-$pkgver.tar.gz")
|
||||
sha256sums=('e1a4c88be9f0481d2aa21078faf42fd0e9d66b490b6d8af82827d441cb58df25')
|
||||
sha256sums=('8e93d3dbe1fc35cfecb7783af409c6a264244d11609b2241d4fe77d43d068419')
|
||||
build() {
|
||||
cd "$srcdir/squawk"
|
||||
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
||||
|
@ -144,7 +144,7 @@ Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path)
|
||||
size = defaultIconFileInfoHeight;
|
||||
}
|
||||
|
||||
itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.fileName(), size, type, p}))).first;
|
||||
itr = instance->fileCache.insert(std::make_pair(path, FileInfo({info.absoluteFilePath(), info.fileName(), size, type, p}))).first;
|
||||
}
|
||||
|
||||
return itr->second;
|
||||
|
@ -55,6 +55,7 @@ namespace Shared {
|
||||
animation
|
||||
};
|
||||
|
||||
QString path;
|
||||
QString name;
|
||||
QSize size;
|
||||
QMimeType mime;
|
||||
|
@ -43,6 +43,7 @@ FeedView::FeedView(QWidget* parent):
|
||||
QAbstractItemView(parent),
|
||||
hints(),
|
||||
vo(0),
|
||||
elementMargin(0),
|
||||
specialDelegate(false),
|
||||
specialModel(false),
|
||||
clearWidgetsMode(false),
|
||||
@ -106,7 +107,7 @@ QRect FeedView::visualRect(const QModelIndex& index) const
|
||||
} else {
|
||||
const Hint& hint = hints.at(row);
|
||||
const QWidget* vp = viewport();
|
||||
return QRect(0, vp->height() - hint.height - hint.offset + vo, vp->width(), hint.height);
|
||||
return QRect(hint.x, vp->height() - hint.height - hint.offset + vo, hint.width, hint.height);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,7 +199,7 @@ void FeedView::updateGeometries()
|
||||
option.rect.setWidth(layoutBounds.width());
|
||||
|
||||
hints.clear();
|
||||
uint32_t previousOffset = 0;
|
||||
uint32_t previousOffset = elementMargin;
|
||||
QDateTime lastDate;
|
||||
for (int i = 0, size = m->rowCount(); i < size; ++i) {
|
||||
QModelIndex index = m->index(i, 0, rootIndex());
|
||||
@ -206,19 +207,32 @@ void FeedView::updateGeometries()
|
||||
if (i > 0) {
|
||||
if (currentDate.daysTo(lastDate) > 0) {
|
||||
previousOffset += dividerMetrics.height() + dateDeviderMargin * 2;
|
||||
} else {
|
||||
previousOffset += elementMargin;
|
||||
}
|
||||
}
|
||||
lastDate = currentDate;
|
||||
int height = itemDelegate(index)->sizeHint(option, index).height();
|
||||
QSize messageSize = itemDelegate(index)->sizeHint(option, index);
|
||||
uint32_t offsetX(0);
|
||||
if (specialDelegate) {
|
||||
if (index.data(Models::MessageFeed::SentByMe).toBool()) {
|
||||
offsetX = layoutBounds.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2;
|
||||
} else {
|
||||
offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2;
|
||||
}
|
||||
}
|
||||
|
||||
hints.emplace_back(Hint({
|
||||
false,
|
||||
previousOffset,
|
||||
static_cast<uint32_t>(height)
|
||||
static_cast<uint32_t>(messageSize.height()),
|
||||
static_cast<uint32_t>(messageSize.width()),
|
||||
offsetX
|
||||
}));
|
||||
previousOffset += height;
|
||||
previousOffset += messageSize.height();
|
||||
}
|
||||
|
||||
int totalHeight = previousOffset - layoutBounds.height();
|
||||
int totalHeight = previousOffset - layoutBounds.height() + dividerMetrics.height() + dateDeviderMargin * 2;
|
||||
if (modelState != Models::MessageFeed::complete) {
|
||||
totalHeight += progressSize;
|
||||
}
|
||||
@ -240,7 +254,7 @@ void FeedView::updateGeometries()
|
||||
|
||||
bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight)
|
||||
{
|
||||
uint32_t previousOffset = 0;
|
||||
uint32_t previousOffset = elementMargin;
|
||||
bool success = true;
|
||||
QDateTime lastDate;
|
||||
for (int i = 0, size = m->rowCount(); i < size; ++i) {
|
||||
@ -249,21 +263,39 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt
|
||||
if (i > 0) {
|
||||
if (currentDate.daysTo(lastDate) > 0) {
|
||||
previousOffset += dateDeviderMargin * 2 + dividerMetrics.height();
|
||||
} else {
|
||||
previousOffset += elementMargin;
|
||||
}
|
||||
}
|
||||
lastDate = currentDate;
|
||||
int height = itemDelegate(index)->sizeHint(option, index).height();
|
||||
QSize messageSize = itemDelegate(index)->sizeHint(option, index);
|
||||
|
||||
if (previousOffset + height > totalHeight) {
|
||||
if (previousOffset + messageSize.height() + elementMargin > totalHeight) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t offsetX(0);
|
||||
if (specialDelegate) {
|
||||
if (index.data(Models::MessageFeed::SentByMe).toBool()) {
|
||||
offsetX = option.rect.width() - messageSize.width() - MessageDelegate::avatarHeight - MessageDelegate::margin * 2;
|
||||
} else {
|
||||
offsetX = MessageDelegate::avatarHeight + MessageDelegate::margin * 2;
|
||||
}
|
||||
}
|
||||
hints.emplace_back(Hint({
|
||||
false,
|
||||
previousOffset,
|
||||
static_cast<uint32_t>(height)
|
||||
static_cast<uint32_t>(messageSize.height()),
|
||||
static_cast<uint32_t>(messageSize.width()),
|
||||
offsetX
|
||||
}));
|
||||
previousOffset += height;
|
||||
previousOffset += messageSize.height();
|
||||
}
|
||||
|
||||
previousOffset += dateDeviderMargin * 2 + dividerMetrics.height();
|
||||
if (previousOffset > totalHeight) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
@ -336,7 +368,7 @@ void FeedView::paintEvent(QPaintEvent* event)
|
||||
lastDate = currentDate;
|
||||
}
|
||||
if (!lastDate.isNull() && inZone) { //if after drawing all messages there is still space
|
||||
drawDateDevider(option.rect.bottom(), lastDate, painter);
|
||||
drawDateDevider(option.rect.top() - dateDeviderMargin * 2 - dividerMetrics.height(), lastDate, painter);
|
||||
}
|
||||
|
||||
if (clearWidgetsMode && specialDelegate) {
|
||||
@ -423,10 +455,12 @@ void FeedView::setItemDelegate(QAbstractItemDelegate* delegate)
|
||||
MessageDelegate* del = dynamic_cast<MessageDelegate*>(delegate);
|
||||
if (del) {
|
||||
specialDelegate = true;
|
||||
elementMargin = MessageDelegate::margin;
|
||||
connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
|
||||
connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
|
||||
} else {
|
||||
specialDelegate = false;
|
||||
elementMargin = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,9 +80,12 @@ private:
|
||||
bool dirty;
|
||||
uint32_t offset;
|
||||
uint32_t height;
|
||||
uint32_t width;
|
||||
uint32_t x;
|
||||
};
|
||||
std::deque<Hint> hints;
|
||||
int vo;
|
||||
int elementMargin;
|
||||
bool specialDelegate;
|
||||
bool specialModel;
|
||||
bool clearWidgetsMode;
|
||||
|
@ -26,8 +26,8 @@
|
||||
#include "messagedelegate.h"
|
||||
#include "messagefeed.h"
|
||||
|
||||
constexpr int avatarHeight = 50;
|
||||
constexpr int margin = 6;
|
||||
int MessageDelegate::avatarHeight(50);
|
||||
int MessageDelegate::margin(6);
|
||||
constexpr int textMargin = 2;
|
||||
constexpr int statusIconSize = 16;
|
||||
constexpr float nickFontMultiplier = 1.1;
|
||||
@ -45,6 +45,7 @@ MessageDelegate::MessageDelegate(QObject* parent):
|
||||
nickMetrics(nickFont),
|
||||
dateMetrics(dateFont),
|
||||
buttonHeight(0),
|
||||
buttonWidth(0),
|
||||
barHeight(0),
|
||||
buttons(new std::map<QString, FeedButton*>()),
|
||||
bars(new std::map<QString, QProgressBar*>()),
|
||||
@ -55,8 +56,9 @@ MessageDelegate::MessageDelegate(QObject* parent):
|
||||
idsToKeep(new std::set<QString>()),
|
||||
clearingWidgets(false)
|
||||
{
|
||||
QPushButton btn;
|
||||
QPushButton btn(QCoreApplication::translate("MessageLine", "Download"));
|
||||
buttonHeight = btn.sizeHint().height();
|
||||
buttonWidth = btn.sizeHint().width();
|
||||
|
||||
QProgressBar bar;
|
||||
barHeight = bar.sizeHint().height();
|
||||
@ -107,141 +109,79 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// if (option.state & QStyle::State_MouseOver) {
|
||||
// painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight));
|
||||
// }
|
||||
|
||||
|
||||
paintBubble(data, painter, option);
|
||||
bool ntds = needToDrawSender(index, data);
|
||||
if (ntds || option.rect.y() < 1) {
|
||||
paintAvatar(data, index, option, painter);
|
||||
}
|
||||
|
||||
QStyleOptionViewItem opt = option;
|
||||
QRect messageRect = option.rect.adjusted(margin, margin / 2, -(avatarHeight + 2 * margin), -margin / 2);
|
||||
opt.rect = option.rect.adjusted(bubbleMargin, bubbleMargin, -bubbleMargin, -bubbleMargin / 2);
|
||||
if (!data.sentByMe) {
|
||||
opt.displayAlignment = Qt::AlignLeft | Qt::AlignTop;
|
||||
messageRect.adjust(avatarHeight + margin, 0, avatarHeight + margin, 0);
|
||||
} else {
|
||||
opt.displayAlignment = Qt::AlignRight | Qt::AlignTop;
|
||||
}
|
||||
|
||||
QPoint bubbleBegin = messageRect.topLeft();
|
||||
messageRect.adjust(bubbleMargin, bubbleMargin, -bubbleMargin, -bubbleMargin / 2);
|
||||
opt.rect = messageRect;
|
||||
|
||||
QSize messageSize(0, 0);
|
||||
QSize bodySize(0, 0);
|
||||
if (data.text.size() > 0) {
|
||||
messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size();
|
||||
bodySize = messageSize;
|
||||
}
|
||||
|
||||
messageSize.rheight() += dateMetrics.height();
|
||||
QString dateString = data.date.toLocalTime().toString("hh:mm");
|
||||
|
||||
if (messageSize.width() < opt.rect.width()) {
|
||||
if (ntds) {
|
||||
QSize senderSize = nickMetrics.boundingRect(messageRect, 0, data.sender).size();
|
||||
if (senderSize.width() > messageSize.width()) {
|
||||
messageSize.setWidth(senderSize.width());
|
||||
}
|
||||
}
|
||||
QSize dateSize = dateMetrics.boundingRect(messageRect, 0, dateString).size();
|
||||
int addition = 0;
|
||||
|
||||
if (data.correction.corrected) {
|
||||
addition += margin + statusIconSize;
|
||||
}
|
||||
if (data.sentByMe) {
|
||||
addition += margin + statusIconSize;
|
||||
}
|
||||
if (dateSize.width() + addition > messageSize.width()) {
|
||||
messageSize.setWidth(dateSize.width() + addition);
|
||||
}
|
||||
} else {
|
||||
messageSize.setWidth(opt.rect.width());
|
||||
}
|
||||
|
||||
painter->save();
|
||||
|
||||
int storedY = opt.rect.y();
|
||||
if (ntds) {
|
||||
opt.rect.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0);
|
||||
}
|
||||
|
||||
int attWidth(0);
|
||||
switch (data.attach.state) {
|
||||
case Models::none:
|
||||
clearHelperWidget(data); //i can't imagine the situation where it's gonna be needed
|
||||
break; //but it's a possible performance problem
|
||||
case Models::uploading:
|
||||
attWidth = std::max(paintPreview(data, painter, opt), attWidth);
|
||||
[[fallthrough]];
|
||||
case Models::downloading:
|
||||
messageSize.setWidth(opt.rect.width());
|
||||
messageSize.rheight() += barHeight + textMargin + opt.rect.y() - storedY;
|
||||
paintBubble(data, painter, messageSize, opt, bubbleBegin);
|
||||
attWidth = std::max(paintBar(getBar(data), painter, data.sentByMe, opt), attWidth);
|
||||
break;
|
||||
case Models::remote:
|
||||
attWidth = std::max(paintButton(getButton(data), painter, data.sentByMe, opt), attWidth);
|
||||
break;
|
||||
case Models::ready:
|
||||
case Models::local:
|
||||
clearHelperWidget(data);
|
||||
attWidth = std::max(paintPreview(data, painter, opt), attWidth);
|
||||
break;
|
||||
case Models::errorDownload: {
|
||||
attWidth = std::max(paintButton(getButton(data), painter, data.sentByMe, opt), attWidth);
|
||||
attWidth = std::max(paintComment(data, painter, opt), attWidth);
|
||||
}
|
||||
|
||||
break;
|
||||
case Models::errorUpload:{
|
||||
clearHelperWidget(data);
|
||||
attWidth = std::max(paintPreview(data, painter, opt), attWidth);
|
||||
attWidth = std::max(paintComment(data, painter, opt), attWidth);
|
||||
}
|
||||
break;
|
||||
}
|
||||
painter->restore();
|
||||
if (data.attach.state != Models::uploading && data.attach.state != Models::downloading) {
|
||||
messageSize.rheight() += opt.rect.y() - storedY;
|
||||
messageSize.setWidth(std::max(attWidth, messageSize.width()));
|
||||
paintBubble(data, painter, messageSize, opt, bubbleBegin);
|
||||
bodySize = bodyMetrics.boundingRect(opt.rect, Qt::TextWordWrap, data.text).size();
|
||||
}
|
||||
|
||||
QRect rect;
|
||||
if (ntds) {
|
||||
painter->setFont(nickFont);
|
||||
int storedY2 = opt.rect.y();
|
||||
opt.rect.setY(storedY);
|
||||
painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect);
|
||||
opt.rect.setY(storedY2);
|
||||
opt.rect.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0);
|
||||
}
|
||||
|
||||
painter->save();
|
||||
switch (data.attach.state) {
|
||||
case Models::none:
|
||||
clearHelperWidget(data); //i can't imagine the situation where it's gonna be needed
|
||||
break; //but it's a possible performance problem
|
||||
case Models::uploading:
|
||||
paintPreview(data, painter, opt);
|
||||
[[fallthrough]];
|
||||
case Models::downloading:
|
||||
paintBar(getBar(data), painter, data.sentByMe, opt);
|
||||
break;
|
||||
case Models::remote:
|
||||
paintButton(getButton(data), painter, data.sentByMe, opt);
|
||||
break;
|
||||
case Models::ready:
|
||||
case Models::local:
|
||||
clearHelperWidget(data);
|
||||
paintPreview(data, painter, opt);
|
||||
break;
|
||||
case Models::errorDownload: {
|
||||
paintButton(getButton(data), painter, data.sentByMe, opt);
|
||||
paintComment(data, painter, opt);
|
||||
}
|
||||
|
||||
break;
|
||||
case Models::errorUpload:{
|
||||
clearHelperWidget(data);
|
||||
paintPreview(data, painter, opt);
|
||||
paintComment(data, painter, opt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
painter->restore();
|
||||
|
||||
int messageLeft = INT16_MAX;
|
||||
int messageRight = opt.rect.x() + messageSize.width();
|
||||
QWidget* vp = static_cast<QWidget*>(painter->device());
|
||||
if (data.text.size() > 0) {
|
||||
QLabel* body = getBody(data);
|
||||
body->setParent(vp);
|
||||
body->setMaximumWidth(bodySize.width());
|
||||
body->setMinimumWidth(bodySize.width());
|
||||
body->setMinimumHeight(bodySize.height());
|
||||
body->setMaximumHeight(bodySize.height());
|
||||
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->setMinimumSize(bodySize);
|
||||
body->setMaximumSize(bodySize);
|
||||
body->move(opt.rect.left(), opt.rect.y());
|
||||
body->show();
|
||||
opt.rect.adjust(0, bodySize.height() + textMargin, 0, 0);
|
||||
}
|
||||
painter->setFont(dateFont);
|
||||
QColor q = painter->pen().color();
|
||||
QString dateString = data.date.toLocalTime().toString("hh:mm");
|
||||
q.setAlpha(180);
|
||||
painter->setPen(q);
|
||||
painter->drawText(opt.rect, opt.displayAlignment, dateString, &rect);
|
||||
@ -250,7 +190,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
||||
QLabel* statusIcon = getStatusIcon(data);
|
||||
|
||||
statusIcon->setParent(vp);
|
||||
statusIcon->move(opt.rect.topRight().x() - messageSize.width(), currentY);
|
||||
statusIcon->move(opt.rect.left(), currentY);
|
||||
statusIcon->show();
|
||||
|
||||
opt.rect.adjust(0, statusIconSize + textMargin, 0, 0);
|
||||
@ -261,9 +201,9 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
||||
|
||||
pencilIcon->setParent(vp);
|
||||
if (data.sentByMe) {
|
||||
pencilIcon->move(opt.rect.topRight().x() - messageSize.width() + statusIconSize + margin, currentY);
|
||||
pencilIcon->move(opt.rect.left() + statusIconSize + margin, currentY);
|
||||
} else {
|
||||
pencilIcon->move(messageRight - statusIconSize - margin, currentY);
|
||||
pencilIcon->move(opt.rect.right() - statusIconSize - margin, currentY);
|
||||
}
|
||||
pencilIcon->show();
|
||||
} else {
|
||||
@ -281,19 +221,16 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
||||
}
|
||||
}
|
||||
|
||||
void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QSize& messageSize, QStyleOptionViewItem& option, QPoint bubbleBegin) const
|
||||
void MessageDelegate::paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const
|
||||
{
|
||||
painter->save();
|
||||
if (data.sentByMe) {
|
||||
bubbleBegin.setX(option.rect.topRight().x() - messageSize.width() - bubbleMargin);
|
||||
painter->setBrush(option.palette.brush(QPalette::Inactive, QPalette::Highlight));
|
||||
} else {
|
||||
painter->setBrush(option.palette.brush(QPalette::Window));
|
||||
}
|
||||
QSize bubbleAddition(2 * bubbleMargin, 1.5 * bubbleMargin);
|
||||
QRect bubble(bubbleBegin, messageSize + bubbleAddition);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->drawRoundedRect(bubble, bubbleBorderRadius, bubbleBorderRadius);
|
||||
painter->drawRoundedRect(option.rect, bubbleBorderRadius, bubbleBorderRadius);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
@ -330,7 +267,7 @@ void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelInde
|
||||
int ax;
|
||||
|
||||
if (data.sentByMe) {
|
||||
ax = option.rect.width() - avatarHeight - margin;
|
||||
ax = option.rect.x() + option.rect.width() + margin;
|
||||
} else {
|
||||
ax = margin;
|
||||
}
|
||||
@ -381,36 +318,51 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
|
||||
[[fallthrough]];
|
||||
case Models::downloading:
|
||||
messageSize.rheight() += barHeight + textMargin;
|
||||
messageSize.setWidth(messageRect.width());
|
||||
break;
|
||||
case Models::remote:
|
||||
messageSize.rheight() += buttonHeight + textMargin;
|
||||
messageSize.setWidth(std::max(messageSize.width(), buttonWidth));
|
||||
break;
|
||||
case Models::ready:
|
||||
case Models::local:
|
||||
messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin;
|
||||
case Models::local: {
|
||||
QSize aSize = Preview::calculateAttachSize(data.attach.localPath, messageRect);
|
||||
messageSize.rheight() += aSize.height() + textMargin;
|
||||
messageSize.setWidth(std::max(messageSize.width(), aSize.width()));
|
||||
}
|
||||
break;
|
||||
case Models::errorDownload:
|
||||
messageSize.rheight() += buttonHeight + textMargin;
|
||||
messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size().height() + textMargin;
|
||||
case Models::errorDownload: {
|
||||
QSize commentSize = dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size();
|
||||
messageSize.rheight() += commentSize.height() + buttonHeight + textMargin * 2;
|
||||
messageSize.setWidth(std::max(messageSize.width(), std::max(commentSize.width(), buttonWidth)));
|
||||
}
|
||||
break;
|
||||
case Models::errorUpload:
|
||||
messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin;
|
||||
messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size().height() + textMargin;
|
||||
case Models::errorUpload: {
|
||||
QSize aSize = Preview::calculateAttachSize(data.attach.localPath, messageRect);
|
||||
QSize commentSize = dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size();
|
||||
messageSize.rheight() += aSize.height() + commentSize.height() + textMargin * 2;
|
||||
messageSize.setWidth(std::max(messageSize.width(), std::max(commentSize.width(), aSize.width())));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (needToDrawSender(index, data)) {
|
||||
messageSize.rheight() += nickMetrics.lineSpacing() + textMargin;
|
||||
QSize senderSize = nickMetrics.boundingRect(messageRect, 0, data.sender).size();
|
||||
messageSize.rheight() += senderSize.height() + textMargin;
|
||||
messageSize.setWidth(std::max(senderSize.width(), messageSize.width()));
|
||||
}
|
||||
|
||||
messageSize.rheight() += bubbleMargin + bubbleMargin / 2;
|
||||
messageSize.rheight() += dateMetrics.height() > statusIconSize ? dateMetrics.height() : statusIconSize;
|
||||
|
||||
// if (messageSize.height() < avatarHeight) {
|
||||
// messageSize.setHeight(avatarHeight);
|
||||
// }
|
||||
|
||||
messageSize.rheight() += margin;
|
||||
QString dateString = data.date.toLocalTime().toString("hh:mm");
|
||||
QSize dateSize = dateMetrics.boundingRect(messageRect, 0, dateString).size();
|
||||
messageSize.rheight() += bubbleMargin * 1.5;
|
||||
messageSize.rheight() += dateSize.height() > statusIconSize ? dateSize.height() : statusIconSize;
|
||||
|
||||
int statusWidth = dateSize.width() + statusIconSize + margin;
|
||||
if (data.correction.corrected) {
|
||||
statusWidth += statusIconSize + margin;
|
||||
}
|
||||
messageSize.setWidth(std::max(statusWidth, messageSize.width()));
|
||||
messageSize.rwidth() += 2 * bubbleMargin;
|
||||
|
||||
return messageSize;
|
||||
}
|
||||
@ -508,7 +460,7 @@ int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painte
|
||||
preview->actualize(data.attach.localPath, size, option.rect.topLeft());
|
||||
} else {
|
||||
QWidget* vp = static_cast<QWidget*>(painter->device());
|
||||
preview = new Preview(data.attach.localPath, size, option.rect.topLeft(), data.sentByMe, vp);
|
||||
preview = new Preview(data.attach.localPath, size, option.rect.topLeft(), vp);
|
||||
previews->insert(std::make_pair(data.id, preview));
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,9 @@ public:
|
||||
bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override;
|
||||
void endClearWidgets();
|
||||
void beginClearWidgets();
|
||||
|
||||
static int avatarHeight;
|
||||
static int margin;
|
||||
|
||||
signals:
|
||||
void buttonPushed(const QString& messageId) const;
|
||||
@ -66,7 +69,7 @@ protected:
|
||||
int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||
int paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||
void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const;
|
||||
void paintBubble(const Models::FeedItem& data, QPainter* painter, const QSize& messageSize, QStyleOptionViewItem& option, QPoint bubbleBegin) const;
|
||||
void paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const;
|
||||
QPushButton* getButton(const Models::FeedItem& data) const;
|
||||
QProgressBar* getBar(const Models::FeedItem& data) const;
|
||||
QLabel* getStatusIcon(const Models::FeedItem& data) const;
|
||||
@ -94,6 +97,7 @@ private:
|
||||
QFontMetrics dateMetrics;
|
||||
|
||||
int buttonHeight;
|
||||
int buttonWidth;
|
||||
int barHeight;
|
||||
|
||||
std::map<QString, FeedButton*>* buttons;
|
||||
|
@ -25,9 +25,8 @@ constexpr int maxAttachmentHeight = 500;
|
||||
QFont Preview::font;
|
||||
QFontMetrics Preview::metrics(Preview::font);
|
||||
|
||||
Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, bool pRight, QWidget* pParent):
|
||||
Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, QWidget* pParent):
|
||||
info(Shared::Global::getFileInfo(pPath)),
|
||||
path(pPath),
|
||||
maxSize(pMaxSize),
|
||||
actualSize(constrainAttachSize(info.size, maxSize)),
|
||||
cachedLabelSize(0, 0),
|
||||
@ -37,8 +36,7 @@ Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos,
|
||||
parent(pParent),
|
||||
movie(0),
|
||||
fileReachable(true),
|
||||
actualPreview(false),
|
||||
right(pRight)
|
||||
actualPreview(false)
|
||||
{
|
||||
|
||||
initializeElements();
|
||||
@ -104,9 +102,6 @@ void Preview::actualize(const QString& newPath, const QSize& newSize, const QPoi
|
||||
}
|
||||
} else if (maxSizeChanged) {
|
||||
applyNewMaxSize();
|
||||
if (right) {
|
||||
positionChanged = true;
|
||||
}
|
||||
}
|
||||
if (positionChanged || !actualPreview) {
|
||||
positionElements();
|
||||
@ -135,9 +130,6 @@ void Preview::setSize(const QSize& newSize)
|
||||
}
|
||||
if (maxSizeChanged || !actualPreview) {
|
||||
applyNewMaxSize();
|
||||
if (right) {
|
||||
positionElements();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,7 +138,7 @@ void Preview::applyNewSize()
|
||||
{
|
||||
switch (info.preview) {
|
||||
case Shared::Global::FileInfo::Preview::picture: {
|
||||
QImageReader img(path);
|
||||
QImageReader img(info.path);
|
||||
if (!img.canRead()) {
|
||||
delete widget;
|
||||
fileReachable = false;
|
||||
@ -216,9 +208,8 @@ void Preview::setPosition(const QPoint& newPoint)
|
||||
|
||||
bool Preview::setPath(const QString& newPath)
|
||||
{
|
||||
if (path != newPath) {
|
||||
path = newPath;
|
||||
info = Shared::Global::getFileInfo(path);
|
||||
if (info.path != newPath) {
|
||||
info = Shared::Global::getFileInfo(newPath);
|
||||
actualSize = constrainAttachSize(info.size, maxSize);
|
||||
clean();
|
||||
initializeElements();
|
||||
@ -235,7 +226,7 @@ void Preview::initializeElements()
|
||||
{
|
||||
switch (info.preview) {
|
||||
case Shared::Global::FileInfo::Preview::picture: {
|
||||
QImageReader img(path);
|
||||
QImageReader img(info.path);
|
||||
if (!img.canRead()) {
|
||||
fileReachable = false;
|
||||
} else {
|
||||
@ -248,7 +239,7 @@ void Preview::initializeElements()
|
||||
}
|
||||
break;
|
||||
case Shared::Global::FileInfo::Preview::animation:{
|
||||
movie = new QMovie(path);
|
||||
movie = new QMovie(info.path);
|
||||
QObject::connect(movie, &QMovie::error,
|
||||
std::bind(&Preview::handleQMovieError, this, std::placeholders::_1)
|
||||
);
|
||||
@ -289,9 +280,6 @@ void Preview::initializeElements()
|
||||
void Preview::positionElements()
|
||||
{
|
||||
int start = position.x();
|
||||
if (right) {
|
||||
start += maxSize.width() - size().width();
|
||||
}
|
||||
widget->move(start, position.y());
|
||||
if (!actualPreview) {
|
||||
int x = start + actualSize.width() + margin;
|
||||
@ -300,11 +288,36 @@ void Preview::positionElements()
|
||||
}
|
||||
}
|
||||
|
||||
bool Preview::canVisualize(const Shared::Global::FileInfo& info)
|
||||
{
|
||||
switch (info.preview) {
|
||||
case Shared::Global::FileInfo::Preview::picture: {
|
||||
QImageReader img(info.path);
|
||||
return img.canRead();
|
||||
}
|
||||
break;
|
||||
case Shared::Global::FileInfo::Preview::animation:{
|
||||
QMovie movie(info.path);
|
||||
return movie.isValid();
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QSize Preview::calculateAttachSize(const QString& path, const QRect& bounds)
|
||||
{
|
||||
Shared::Global::FileInfo info = Shared::Global::getFileInfo(path);
|
||||
|
||||
return constrainAttachSize(info.size, bounds.size());
|
||||
QSize constrained = constrainAttachSize(info.size, bounds.size());
|
||||
if (!canVisualize(info)) {
|
||||
int maxLabelWidth = bounds.width() - info.size.width() - margin;
|
||||
QString elidedName = metrics.elidedText(info.name, Qt::ElideMiddle, maxLabelWidth);
|
||||
int labelWidth = metrics.boundingRect(elidedName).size().width();
|
||||
constrained.rwidth() += margin + labelWidth;
|
||||
}
|
||||
return constrained;
|
||||
}
|
||||
|
||||
QSize Preview::constrainAttachSize(QSize src, QSize bounds)
|
||||
|
@ -38,7 +38,7 @@
|
||||
*/
|
||||
class Preview {
|
||||
public:
|
||||
Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, bool pRight, QWidget* parent);
|
||||
Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos, QWidget* parent);
|
||||
~Preview();
|
||||
|
||||
void actualize(const QString& newPath, const QSize& newSize, const QPoint& newPoint);
|
||||
@ -51,6 +51,7 @@ public:
|
||||
static void initializeFont(const QFont& newFont);
|
||||
static QSize constrainAttachSize(QSize src, QSize bounds);
|
||||
static QSize calculateAttachSize(const QString& path, const QRect& bounds);
|
||||
static bool canVisualize(const Shared::Global::FileInfo& info);
|
||||
static QFont font;
|
||||
static QFontMetrics metrics;
|
||||
|
||||
@ -64,7 +65,6 @@ private:
|
||||
|
||||
private:
|
||||
Shared::Global::FileInfo info;
|
||||
QString path;
|
||||
QSize maxSize;
|
||||
QSize actualSize;
|
||||
QSize cachedLabelSize;
|
||||
@ -75,7 +75,6 @@ private:
|
||||
QMovie* movie;
|
||||
bool fileReachable;
|
||||
bool actualPreview;
|
||||
bool right;
|
||||
};
|
||||
|
||||
#endif // PREVIEW_H
|
||||
|
Loading…
Reference in New Issue
Block a user