0.2.0 finalization

This commit is contained in:
Blue 2022-01-09 17:32:23 +03:00
parent 8a2658e4fc
commit 4d3ba6b11f
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
12 changed files with 194 additions and 179 deletions

View File

@ -1,19 +1,28 @@
# Changelog # Changelog
## Squawk 0.2.0 (Unreleased) ## Squawk 0.2.0 (Jan 10, 2022)
### Bug fixes ### Bug fixes
- carbon copies switches on again after reconnection - carbon copies switches on again after reconnection
- requesting the history of the current chat after reconnection - requesting the history of the current chat after reconnection
- global availability (in drop down list) gets restored 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 - 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 - 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 ### Improvements
- slightly reduced the traffic on the startup by not requesting history of all MUCs - 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 - 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 - 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 - 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) ## Squawk 0.1.5 (Jul 29, 2020)
### Bug fixes ### Bug fixes

View File

@ -4,13 +4,13 @@
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/) [![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) [![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 ### Prerequisites
- QT 5.12 *(lower versions might work but it wasn't tested)* - QT 5.12 *(lower versions might work but it wasn't tested)*
- lmdb - lmdb
- CMake 3.0 or higher - CMake 3.3 or higher
- qxmpp 1.1.0 or higher - qxmpp 1.1.0 or higher
- KDE Frameworks: kwallet (optional) - KDE Frameworks: kwallet (optional)
- KDE Frameworks: KIO (optional) - KDE Frameworks: KIO (optional)

View File

@ -54,7 +54,7 @@ int main(int argc, char *argv[])
#endif #endif
QApplication::setApplicationName("squawk"); QApplication::setApplicationName("squawk");
QApplication::setApplicationDisplayName("Squawk"); QApplication::setApplicationDisplayName("Squawk");
QApplication::setApplicationVersion("0.1.5"); QApplication::setApplicationVersion("0.2.0");
QTranslator qtTranslator; QTranslator qtTranslator;
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));

View File

@ -1,6 +1,6 @@
# Maintainer: Yury Gubich <blue@macaw.me> # Maintainer: Yury Gubich <blue@macaw.me>
pkgname=squawk pkgname=squawk
pkgver=0.1.5 pkgver=0.2.0
pkgrel=1 pkgrel=1
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)" pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
arch=('i686' 'x86_64') arch=('i686' 'x86_64')
@ -11,7 +11,7 @@ makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools')
optdepends=('kwallet: secure password storage (requires rebuild)') optdepends=('kwallet: secure password storage (requires rebuild)')
source=("$pkgname-$pkgver.tar.gz") source=("$pkgname-$pkgver.tar.gz")
sha256sums=('e1a4c88be9f0481d2aa21078faf42fd0e9d66b490b6d8af82827d441cb58df25') sha256sums=('8e93d3dbe1fc35cfecb7783af409c6a264244d11609b2241d4fe77d43d068419')
build() { build() {
cd "$srcdir/squawk" cd "$srcdir/squawk"
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release

View File

@ -144,7 +144,7 @@ Shared::Global::FileInfo Shared::Global::getFileInfo(const QString& path)
size = defaultIconFileInfoHeight; 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; return itr->second;

View File

@ -55,6 +55,7 @@ namespace Shared {
animation animation
}; };
QString path;
QString name; QString name;
QSize size; QSize size;
QMimeType mime; QMimeType mime;

View File

@ -43,6 +43,7 @@ FeedView::FeedView(QWidget* parent):
QAbstractItemView(parent), QAbstractItemView(parent),
hints(), hints(),
vo(0), vo(0),
elementMargin(0),
specialDelegate(false), specialDelegate(false),
specialModel(false), specialModel(false),
clearWidgetsMode(false), clearWidgetsMode(false),
@ -106,7 +107,7 @@ QRect FeedView::visualRect(const QModelIndex& index) const
} else { } else {
const Hint& hint = hints.at(row); const Hint& hint = hints.at(row);
const QWidget* vp = viewport(); 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()); option.rect.setWidth(layoutBounds.width());
hints.clear(); hints.clear();
uint32_t previousOffset = 0; uint32_t previousOffset = elementMargin;
QDateTime lastDate; QDateTime lastDate;
for (int i = 0, size = m->rowCount(); i < size; ++i) { for (int i = 0, size = m->rowCount(); i < size; ++i) {
QModelIndex index = m->index(i, 0, rootIndex()); QModelIndex index = m->index(i, 0, rootIndex());
@ -206,19 +207,32 @@ void FeedView::updateGeometries()
if (i > 0) { if (i > 0) {
if (currentDate.daysTo(lastDate) > 0) { if (currentDate.daysTo(lastDate) > 0) {
previousOffset += dividerMetrics.height() + dateDeviderMargin * 2; previousOffset += dividerMetrics.height() + dateDeviderMargin * 2;
} else {
previousOffset += elementMargin;
} }
} }
lastDate = currentDate; 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({ hints.emplace_back(Hint({
false, false,
previousOffset, 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) { if (modelState != Models::MessageFeed::complete) {
totalHeight += progressSize; totalHeight += progressSize;
} }
@ -240,7 +254,7 @@ void FeedView::updateGeometries()
bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight) bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* m, uint32_t totalHeight)
{ {
uint32_t previousOffset = 0; uint32_t previousOffset = elementMargin;
bool success = true; bool success = true;
QDateTime lastDate; QDateTime lastDate;
for (int i = 0, size = m->rowCount(); i < size; ++i) { for (int i = 0, size = m->rowCount(); i < size; ++i) {
@ -249,21 +263,39 @@ bool FeedView::tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewIt
if (i > 0) { if (i > 0) {
if (currentDate.daysTo(lastDate) > 0) { if (currentDate.daysTo(lastDate) > 0) {
previousOffset += dateDeviderMargin * 2 + dividerMetrics.height(); previousOffset += dateDeviderMargin * 2 + dividerMetrics.height();
} else {
previousOffset += elementMargin;
} }
} }
lastDate = currentDate; 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; success = false;
break; 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({ hints.emplace_back(Hint({
false, false,
previousOffset, 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; return success;
@ -336,7 +368,7 @@ void FeedView::paintEvent(QPaintEvent* event)
lastDate = currentDate; lastDate = currentDate;
} }
if (!lastDate.isNull() && inZone) { //if after drawing all messages there is still space 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) { if (clearWidgetsMode && specialDelegate) {
@ -423,10 +455,12 @@ void FeedView::setItemDelegate(QAbstractItemDelegate* delegate)
MessageDelegate* del = dynamic_cast<MessageDelegate*>(delegate); MessageDelegate* del = dynamic_cast<MessageDelegate*>(delegate);
if (del) { if (del) {
specialDelegate = true; specialDelegate = true;
elementMargin = MessageDelegate::margin;
connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed); connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath); connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
} else { } else {
specialDelegate = false; specialDelegate = false;
elementMargin = 0;
} }
} }

View File

@ -80,9 +80,12 @@ private:
bool dirty; bool dirty;
uint32_t offset; uint32_t offset;
uint32_t height; uint32_t height;
uint32_t width;
uint32_t x;
}; };
std::deque<Hint> hints; std::deque<Hint> hints;
int vo; int vo;
int elementMargin;
bool specialDelegate; bool specialDelegate;
bool specialModel; bool specialModel;
bool clearWidgetsMode; bool clearWidgetsMode;

View File

@ -26,8 +26,8 @@
#include "messagedelegate.h" #include "messagedelegate.h"
#include "messagefeed.h" #include "messagefeed.h"
constexpr int avatarHeight = 50; int MessageDelegate::avatarHeight(50);
constexpr int margin = 6; int MessageDelegate::margin(6);
constexpr int textMargin = 2; constexpr int textMargin = 2;
constexpr int statusIconSize = 16; constexpr int statusIconSize = 16;
constexpr float nickFontMultiplier = 1.1; constexpr float nickFontMultiplier = 1.1;
@ -45,6 +45,7 @@ MessageDelegate::MessageDelegate(QObject* parent):
nickMetrics(nickFont), nickMetrics(nickFont),
dateMetrics(dateFont), dateMetrics(dateFont),
buttonHeight(0), buttonHeight(0),
buttonWidth(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*>()),
@ -55,8 +56,9 @@ MessageDelegate::MessageDelegate(QObject* parent):
idsToKeep(new std::set<QString>()), idsToKeep(new std::set<QString>()),
clearingWidgets(false) clearingWidgets(false)
{ {
QPushButton btn; QPushButton btn(QCoreApplication::translate("MessageLine", "Download"));
buttonHeight = btn.sizeHint().height(); buttonHeight = btn.sizeHint().height();
buttonWidth = btn.sizeHint().width();
QProgressBar bar; QProgressBar bar;
barHeight = bar.sizeHint().height(); barHeight = bar.sizeHint().height();
@ -107,141 +109,79 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
painter->save(); painter->save();
painter->setRenderHint(QPainter::Antialiasing, true); painter->setRenderHint(QPainter::Antialiasing, true);
// if (option.state & QStyle::State_MouseOver) { paintBubble(data, painter, option);
// painter->fillRect(option.rect, option.palette.brush(QPalette::Inactive, QPalette::Highlight));
// }
bool ntds = needToDrawSender(index, data); bool ntds = needToDrawSender(index, data);
if (ntds || option.rect.y() < 1) { if (ntds || option.rect.y() < 1) {
paintAvatar(data, index, option, painter); paintAvatar(data, index, option, painter);
} }
QStyleOptionViewItem opt = option; 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) { if (!data.sentByMe) {
opt.displayAlignment = Qt::AlignLeft | Qt::AlignTop; opt.displayAlignment = Qt::AlignLeft | Qt::AlignTop;
messageRect.adjust(avatarHeight + margin, 0, avatarHeight + margin, 0);
} else { } else {
opt.displayAlignment = Qt::AlignRight | Qt::AlignTop; 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); QSize bodySize(0, 0);
if (data.text.size() > 0) { if (data.text.size() > 0) {
messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size(); bodySize = bodyMetrics.boundingRect(opt.rect, 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);
} }
QRect rect; QRect rect;
if (ntds) { if (ntds) {
painter->setFont(nickFont); painter->setFont(nickFont);
int storedY2 = opt.rect.y();
opt.rect.setY(storedY);
painter->drawText(opt.rect, opt.displayAlignment, data.sender, &rect); 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()); QWidget* vp = static_cast<QWidget*>(painter->device());
if (data.text.size() > 0) { if (data.text.size() > 0) {
QLabel* body = getBody(data); QLabel* body = getBody(data);
body->setParent(vp); body->setParent(vp);
body->setMaximumWidth(bodySize.width()); body->setMinimumSize(bodySize);
body->setMinimumWidth(bodySize.width()); body->setMaximumSize(bodySize);
body->setMinimumHeight(bodySize.height()); body->move(opt.rect.left(), opt.rect.y());
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->show(); body->show();
opt.rect.adjust(0, bodySize.height() + textMargin, 0, 0); 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();
QString dateString = data.date.toLocalTime().toString("hh:mm");
q.setAlpha(180); q.setAlpha(180);
painter->setPen(q); painter->setPen(q);
painter->drawText(opt.rect, opt.displayAlignment, dateString, &rect); painter->drawText(opt.rect, opt.displayAlignment, dateString, &rect);
@ -250,7 +190,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
QLabel* statusIcon = getStatusIcon(data); QLabel* statusIcon = getStatusIcon(data);
statusIcon->setParent(vp); statusIcon->setParent(vp);
statusIcon->move(opt.rect.topRight().x() - messageSize.width(), currentY); statusIcon->move(opt.rect.left(), currentY);
statusIcon->show(); statusIcon->show();
opt.rect.adjust(0, statusIconSize + textMargin, 0, 0); opt.rect.adjust(0, statusIconSize + textMargin, 0, 0);
@ -261,9 +201,9 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
pencilIcon->setParent(vp); pencilIcon->setParent(vp);
if (data.sentByMe) { if (data.sentByMe) {
pencilIcon->move(opt.rect.topRight().x() - messageSize.width() + statusIconSize + margin, currentY); pencilIcon->move(opt.rect.left() + statusIconSize + margin, currentY);
} else { } else {
pencilIcon->move(messageRight - statusIconSize - margin, currentY); pencilIcon->move(opt.rect.right() - statusIconSize - margin, currentY);
} }
pencilIcon->show(); pencilIcon->show();
} else { } 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(); painter->save();
if (data.sentByMe) { if (data.sentByMe) {
bubbleBegin.setX(option.rect.topRight().x() - messageSize.width() - bubbleMargin);
painter->setBrush(option.palette.brush(QPalette::Inactive, QPalette::Highlight)); painter->setBrush(option.palette.brush(QPalette::Inactive, QPalette::Highlight));
} else { } else {
painter->setBrush(option.palette.brush(QPalette::Window)); painter->setBrush(option.palette.brush(QPalette::Window));
} }
QSize bubbleAddition(2 * bubbleMargin, 1.5 * bubbleMargin);
QRect bubble(bubbleBegin, messageSize + bubbleAddition);
painter->setPen(Qt::NoPen); painter->setPen(Qt::NoPen);
painter->drawRoundedRect(bubble, bubbleBorderRadius, bubbleBorderRadius); painter->drawRoundedRect(option.rect, bubbleBorderRadius, bubbleBorderRadius);
painter->restore(); painter->restore();
} }
@ -330,7 +267,7 @@ void MessageDelegate::paintAvatar(const Models::FeedItem& data, const QModelInde
int ax; int ax;
if (data.sentByMe) { if (data.sentByMe) {
ax = option.rect.width() - avatarHeight - margin; ax = option.rect.x() + option.rect.width() + margin;
} else { } else {
ax = margin; ax = margin;
} }
@ -381,36 +318,51 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
[[fallthrough]]; [[fallthrough]];
case Models::downloading: case Models::downloading:
messageSize.rheight() += barHeight + textMargin; messageSize.rheight() += barHeight + textMargin;
messageSize.setWidth(messageRect.width());
break; break;
case Models::remote: case Models::remote:
messageSize.rheight() += buttonHeight + textMargin; messageSize.rheight() += buttonHeight + textMargin;
messageSize.setWidth(std::max(messageSize.width(), buttonWidth));
break; break;
case Models::ready: case Models::ready:
case Models::local: case Models::local: {
messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin; QSize aSize = Preview::calculateAttachSize(data.attach.localPath, messageRect);
messageSize.rheight() += aSize.height() + textMargin;
messageSize.setWidth(std::max(messageSize.width(), aSize.width()));
}
break; break;
case Models::errorDownload: case Models::errorDownload: {
messageSize.rheight() += buttonHeight + textMargin; QSize commentSize = dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size();
messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size().height() + textMargin; messageSize.rheight() += commentSize.height() + buttonHeight + textMargin * 2;
messageSize.setWidth(std::max(messageSize.width(), std::max(commentSize.width(), buttonWidth)));
}
break; break;
case Models::errorUpload: case Models::errorUpload: {
messageSize.rheight() += Preview::calculateAttachSize(data.attach.localPath, messageRect).height() + textMargin; QSize aSize = Preview::calculateAttachSize(data.attach.localPath, messageRect);
messageSize.rheight() += dateMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.attach.error).size().height() + textMargin; 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; break;
} }
if (needToDrawSender(index, data)) { 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; QString dateString = data.date.toLocalTime().toString("hh:mm");
messageSize.rheight() += dateMetrics.height() > statusIconSize ? dateMetrics.height() : statusIconSize; QSize dateSize = dateMetrics.boundingRect(messageRect, 0, dateString).size();
messageSize.rheight() += bubbleMargin * 1.5;
// if (messageSize.height() < avatarHeight) { messageSize.rheight() += dateSize.height() > statusIconSize ? dateSize.height() : statusIconSize;
// messageSize.setHeight(avatarHeight);
// } int statusWidth = dateSize.width() + statusIconSize + margin;
if (data.correction.corrected) {
messageSize.rheight() += margin; statusWidth += statusIconSize + margin;
}
messageSize.setWidth(std::max(statusWidth, messageSize.width()));
messageSize.rwidth() += 2 * bubbleMargin;
return messageSize; return messageSize;
} }
@ -508,7 +460,7 @@ int MessageDelegate::paintPreview(const Models::FeedItem& data, QPainter* painte
preview->actualize(data.attach.localPath, size, option.rect.topLeft()); preview->actualize(data.attach.localPath, size, option.rect.topLeft());
} else { } else {
QWidget* vp = static_cast<QWidget*>(painter->device()); 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)); previews->insert(std::make_pair(data.id, preview));
} }

View File

@ -55,6 +55,9 @@ public:
bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override; bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override;
void endClearWidgets(); void endClearWidgets();
void beginClearWidgets(); void beginClearWidgets();
static int avatarHeight;
static int margin;
signals: signals:
void buttonPushed(const QString& messageId) const; void buttonPushed(const QString& messageId) const;
@ -66,7 +69,7 @@ protected:
int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const; int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
int paintComment(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 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; 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;
@ -94,6 +97,7 @@ private:
QFontMetrics dateMetrics; QFontMetrics dateMetrics;
int buttonHeight; int buttonHeight;
int buttonWidth;
int barHeight; int barHeight;
std::map<QString, FeedButton*>* buttons; std::map<QString, FeedButton*>* buttons;

View File

@ -25,9 +25,8 @@ constexpr int maxAttachmentHeight = 500;
QFont Preview::font; QFont Preview::font;
QFontMetrics Preview::metrics(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)), info(Shared::Global::getFileInfo(pPath)),
path(pPath),
maxSize(pMaxSize), maxSize(pMaxSize),
actualSize(constrainAttachSize(info.size, maxSize)), actualSize(constrainAttachSize(info.size, maxSize)),
cachedLabelSize(0, 0), cachedLabelSize(0, 0),
@ -37,8 +36,7 @@ Preview::Preview(const QString& pPath, const QSize& pMaxSize, const QPoint& pos,
parent(pParent), parent(pParent),
movie(0), movie(0),
fileReachable(true), fileReachable(true),
actualPreview(false), actualPreview(false)
right(pRight)
{ {
initializeElements(); initializeElements();
@ -104,9 +102,6 @@ void Preview::actualize(const QString& newPath, const QSize& newSize, const QPoi
} }
} else if (maxSizeChanged) { } else if (maxSizeChanged) {
applyNewMaxSize(); applyNewMaxSize();
if (right) {
positionChanged = true;
}
} }
if (positionChanged || !actualPreview) { if (positionChanged || !actualPreview) {
positionElements(); positionElements();
@ -135,9 +130,6 @@ void Preview::setSize(const QSize& newSize)
} }
if (maxSizeChanged || !actualPreview) { if (maxSizeChanged || !actualPreview) {
applyNewMaxSize(); applyNewMaxSize();
if (right) {
positionElements();
}
} }
} }
} }
@ -146,7 +138,7 @@ void Preview::applyNewSize()
{ {
switch (info.preview) { switch (info.preview) {
case Shared::Global::FileInfo::Preview::picture: { case Shared::Global::FileInfo::Preview::picture: {
QImageReader img(path); QImageReader img(info.path);
if (!img.canRead()) { if (!img.canRead()) {
delete widget; delete widget;
fileReachable = false; fileReachable = false;
@ -216,9 +208,8 @@ void Preview::setPosition(const QPoint& newPoint)
bool Preview::setPath(const QString& newPath) bool Preview::setPath(const QString& newPath)
{ {
if (path != newPath) { if (info.path != newPath) {
path = newPath; info = Shared::Global::getFileInfo(newPath);
info = Shared::Global::getFileInfo(path);
actualSize = constrainAttachSize(info.size, maxSize); actualSize = constrainAttachSize(info.size, maxSize);
clean(); clean();
initializeElements(); initializeElements();
@ -235,7 +226,7 @@ void Preview::initializeElements()
{ {
switch (info.preview) { switch (info.preview) {
case Shared::Global::FileInfo::Preview::picture: { case Shared::Global::FileInfo::Preview::picture: {
QImageReader img(path); QImageReader img(info.path);
if (!img.canRead()) { if (!img.canRead()) {
fileReachable = false; fileReachable = false;
} else { } else {
@ -248,7 +239,7 @@ void Preview::initializeElements()
} }
break; break;
case Shared::Global::FileInfo::Preview::animation:{ case Shared::Global::FileInfo::Preview::animation:{
movie = new QMovie(path); movie = new QMovie(info.path);
QObject::connect(movie, &QMovie::error, QObject::connect(movie, &QMovie::error,
std::bind(&Preview::handleQMovieError, this, std::placeholders::_1) std::bind(&Preview::handleQMovieError, this, std::placeholders::_1)
); );
@ -289,9 +280,6 @@ void Preview::initializeElements()
void Preview::positionElements() void Preview::positionElements()
{ {
int start = position.x(); int start = position.x();
if (right) {
start += maxSize.width() - size().width();
}
widget->move(start, position.y()); widget->move(start, position.y());
if (!actualPreview) { if (!actualPreview) {
int x = start + actualSize.width() + margin; 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) QSize Preview::calculateAttachSize(const QString& path, const QRect& bounds)
{ {
Shared::Global::FileInfo info = Shared::Global::getFileInfo(path); Shared::Global::FileInfo info = Shared::Global::getFileInfo(path);
QSize constrained = constrainAttachSize(info.size, bounds.size());
return 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) QSize Preview::constrainAttachSize(QSize src, QSize bounds)

View File

@ -38,7 +38,7 @@
*/ */
class Preview { class Preview {
public: 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(); ~Preview();
void actualize(const QString& newPath, const QSize& newSize, const QPoint& newPoint); void actualize(const QString& newPath, const QSize& newSize, const QPoint& newPoint);
@ -51,6 +51,7 @@ public:
static void initializeFont(const QFont& newFont); static void initializeFont(const QFont& newFont);
static QSize constrainAttachSize(QSize src, QSize bounds); static QSize constrainAttachSize(QSize src, QSize bounds);
static QSize calculateAttachSize(const QString& path, const QRect& bounds); static QSize calculateAttachSize(const QString& path, const QRect& bounds);
static bool canVisualize(const Shared::Global::FileInfo& info);
static QFont font; static QFont font;
static QFontMetrics metrics; static QFontMetrics metrics;
@ -64,7 +65,6 @@ private:
private: private:
Shared::Global::FileInfo info; Shared::Global::FileInfo info;
QString path;
QSize maxSize; QSize maxSize;
QSize actualSize; QSize actualSize;
QSize cachedLabelSize; QSize cachedLabelSize;
@ -75,7 +75,6 @@ private:
QMovie* movie; QMovie* movie;
bool fileReachable; bool fileReachable;
bool actualPreview; bool actualPreview;
bool right;
}; };
#endif // PREVIEW_H #endif // PREVIEW_H