mirror-chatterino2/src/messages/layouts/messagelayoutcontainer.cpp

445 lines
13 KiB
C++
Raw Normal View History

#include "messagelayoutcontainer.hpp"
#include "messagelayoutelement.hpp"
#include "messages/selection.hpp"
#include "singletons/settingsmanager.hpp"
2018-01-15 04:08:48 +01:00
#include <QDebug>
#include <QPainter>
#define COMPACT_EMOTES_OFFSET 6
namespace chatterino {
namespace messages {
namespace layouts {
int MessageLayoutContainer::getHeight() const
{
return this->height;
}
2018-01-28 16:29:47 +01:00
int MessageLayoutContainer::getWidth() const
{
return this->width;
}
float MessageLayoutContainer::getScale() const
{
return this->scale;
}
// methods
2018-04-01 16:43:30 +02:00
void MessageLayoutContainer::begin(int _width, float _scale, Message::MessageFlags _flags)
2018-01-28 16:29:47 +01:00
{
this->clear();
2018-04-01 16:43:30 +02:00
this->width = _width;
this->scale = _scale;
2018-01-28 16:29:47 +01:00
this->flags = _flags;
}
void MessageLayoutContainer::clear()
{
this->elements.clear();
2018-01-16 00:26:04 +01:00
this->lines.clear();
this->height = 0;
this->line = 0;
this->currentX = 0;
this->currentY = 0;
this->lineStart = 0;
this->lineHeight = 0;
2018-01-16 00:26:04 +01:00
this->charIndex = 0;
}
void MessageLayoutContainer::addElement(MessageLayoutElement *element)
{
if (!this->fitsInLine(element->getRect().width())) {
this->breakLine();
}
this->_addElement(element);
}
void MessageLayoutContainer::addElementNoLineBreak(MessageLayoutElement *element)
{
this->_addElement(element);
}
void MessageLayoutContainer::_addElement(MessageLayoutElement *element)
{
2018-01-28 16:29:47 +01:00
// top margin
if (this->elements.size() == 0) {
this->currentY = this->margin.top * this->scale;
}
int newLineHeight = element->getRect().height();
2018-01-28 16:29:47 +01:00
// compact emote offset
bool isCompactEmote = !(this->flags & Message::DisableCompactEmotes) &&
element->getCreator().getFlags() & MessageElement::EmoteImages;
2018-01-28 16:29:47 +01:00
if (isCompactEmote) {
newLineHeight -= COMPACT_EMOTES_OFFSET * this->scale;
}
// update line height
this->lineHeight = std::max(this->lineHeight, newLineHeight);
2018-01-28 16:29:47 +01:00
// set move element
element->setPosition(QPoint(this->currentX, this->currentY - element->getRect().height()));
2018-01-28 16:29:47 +01:00
// add element
this->elements.push_back(std::unique_ptr<MessageLayoutElement>(element));
2018-01-28 16:29:47 +01:00
// set current x
this->currentX += element->getRect().width();
if (element->hasTrailingSpace()) {
this->currentX += this->spaceWidth;
}
}
void MessageLayoutContainer::breakLine()
{
int xOffset = 0;
2018-01-28 16:29:47 +01:00
if (this->flags & Message::Centered && this->elements.size() > 0) {
xOffset = (width - this->elements.at(this->elements.size() - 1)->getRect().right()) / 2;
}
for (size_t i = lineStart; i < this->elements.size(); i++) {
MessageLayoutElement *element = this->elements.at(i).get();
2018-04-06 18:05:30 +02:00
bool isCompactEmote = !(this->flags & Message::DisableCompactEmotes) &&
element->getCreator().getFlags() & MessageElement::EmoteImages;
int yExtra = 0;
if (isCompactEmote) {
yExtra = (COMPACT_EMOTES_OFFSET / 2) * this->scale;
}
element->setPosition(QPoint(element->getRect().x() + xOffset + this->margin.left,
element->getRect().y() + this->lineHeight + yExtra));
}
2018-01-16 00:26:04 +01:00
if (this->lines.size() != 0) {
this->lines.back().endIndex = this->lineStart;
this->lines.back().endCharIndex = this->charIndex;
}
this->lines.push_back({(int)lineStart, 0, this->charIndex, 0,
QRect(-100000, this->currentY, 200000, lineHeight)});
for (int i = this->lineStart; i < this->elements.size(); i++) {
this->charIndex += this->elements[i]->getSelectionIndexCount();
}
2018-01-15 04:08:48 +01:00
this->lineStart = this->elements.size();
2018-04-10 15:48:56 +02:00
// this->currentX = (int)(this->scale * 8);
this->currentX = 0;
this->currentY += this->lineHeight;
this->height = this->currentY + (this->margin.bottom * this->scale);
this->lineHeight = 0;
}
bool MessageLayoutContainer::atStartOfLine()
{
return this->lineStart == this->elements.size();
}
bool MessageLayoutContainer::fitsInLine(int _width)
{
return this->currentX + _width <= this->width - this->margin.left - this->margin.right;
}
2018-04-01 16:41:32 +02:00
void MessageLayoutContainer::end()
{
if (!this->atStartOfLine()) {
this->breakLine();
}
2018-01-15 04:08:48 +01:00
if (this->lines.size() != 0) {
2018-01-16 00:26:04 +01:00
this->lines[0].rect.setTop(-100000);
this->lines.back().rect.setBottom(100000);
this->lines.back().endIndex = this->elements.size();
this->lines.back().endCharIndex = this->charIndex;
2018-01-15 04:08:48 +01:00
}
}
MessageLayoutElement *MessageLayoutContainer::getElementAt(QPoint point)
{
for (std::unique_ptr<MessageLayoutElement> &element : this->elements) {
if (element->getRect().contains(point)) {
return element.get();
}
}
return nullptr;
}
// painting
void MessageLayoutContainer::paintElements(QPainter &painter)
{
for (const std::unique_ptr<MessageLayoutElement> &element : this->elements) {
2018-04-10 02:42:41 +02:00
#ifdef OHHEYITSFOURTF
painter.setPen(QColor(0, 255, 0));
painter.drawRect(element->getRect());
#endif
element->paint(painter);
}
}
2018-01-13 02:13:59 +01:00
void MessageLayoutContainer::paintAnimatedElements(QPainter &painter, int yOffset)
{
for (const std::unique_ptr<MessageLayoutElement> &element : this->elements) {
element->paintAnimated(painter, yOffset);
}
}
void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex,
2018-04-10 03:29:00 +02:00
Selection &selection, int yOffset)
{
2018-01-16 00:34:32 +01:00
singletons::ThemeManager &themeManager = singletons::ThemeManager::getInstance();
QColor selectionColor = themeManager.messages.selection;
2018-01-16 00:26:04 +01:00
// don't draw anything
if (selection.min.messageIndex > messageIndex || selection.max.messageIndex < messageIndex) {
return;
}
// fully selected
if (selection.min.messageIndex < messageIndex && selection.max.messageIndex > messageIndex) {
for (Line &line : this->lines) {
QRect rect = line.rect;
2018-04-10 03:29:00 +02:00
rect.setTop(std::max(0, rect.top()) + yOffset);
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
2018-01-16 00:26:04 +01:00
rect.setLeft(this->elements[line.startIndex]->getRect().left());
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
2018-01-16 00:34:32 +01:00
painter.fillRect(rect, selectionColor);
2018-01-16 00:26:04 +01:00
}
return;
}
int lineIndex = 0;
int index = 0;
// start in this message
if (selection.min.messageIndex == messageIndex) {
for (; lineIndex < this->lines.size(); lineIndex++) {
Line &line = this->lines[lineIndex];
index = line.startCharIndex;
bool returnAfter = false;
bool breakAfter = false;
int x = this->elements[line.startIndex]->getRect().left();
int r = this->elements[line.endIndex - 1]->getRect().right();
if (line.endCharIndex < selection.min.charIndex) {
continue;
}
for (int i = line.startIndex; i < line.endIndex; i++) {
int c = this->elements[i]->getSelectionIndexCount();
if (index + c > selection.min.charIndex) {
x = this->elements[i]->getXFromIndex(selection.min.charIndex - index);
// ends in same line
if (selection.max.messageIndex == messageIndex &&
line.endCharIndex > /*=*/selection.max.charIndex) //
{
returnAfter = true;
index = line.startCharIndex;
for (int i = line.startIndex; i < line.endIndex; i++) {
int c = this->elements[i]->getSelectionIndexCount();
if (index + c > selection.max.charIndex) {
r = this->elements[i]->getXFromIndex(selection.max.charIndex -
index);
break;
}
index += c;
}
}
// ends in same line end
if (selection.max.messageIndex != messageIndex) {
int lineIndex2 = lineIndex + 1;
for (; lineIndex2 < this->lines.size(); lineIndex2++) {
Line &line = this->lines[lineIndex2];
QRect rect = line.rect;
2018-04-10 03:29:00 +02:00
rect.setTop(std::max(0, rect.top()) + yOffset);
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
2018-01-16 00:26:04 +01:00
rect.setLeft(this->elements[line.startIndex]->getRect().left());
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
2018-01-16 00:34:32 +01:00
painter.fillRect(rect, selectionColor);
2018-01-16 00:26:04 +01:00
}
returnAfter = true;
} else {
lineIndex++;
breakAfter = true;
}
break;
}
index += c;
}
QRect rect = line.rect;
2018-04-10 03:29:00 +02:00
rect.setTop(std::max(0, rect.top()) + yOffset);
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
2018-01-16 00:26:04 +01:00
rect.setLeft(x);
rect.setRight(r);
2018-01-16 00:34:32 +01:00
painter.fillRect(rect, selectionColor);
2018-01-16 00:26:04 +01:00
if (returnAfter) {
return;
}
if (breakAfter) {
break;
}
}
}
// start in this message
for (; lineIndex < this->lines.size(); lineIndex++) {
Line &line = this->lines[lineIndex];
index = line.startCharIndex;
// just draw the garbage
if (line.endCharIndex < /*=*/selection.max.charIndex) {
QRect rect = line.rect;
2018-04-10 03:29:00 +02:00
rect.setTop(std::max(0, rect.top()) + yOffset);
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
2018-01-16 00:26:04 +01:00
rect.setLeft(this->elements[line.startIndex]->getRect().left());
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
2018-01-16 00:34:32 +01:00
painter.fillRect(rect, selectionColor);
2018-01-16 00:26:04 +01:00
continue;
}
int r = this->elements[line.endIndex - 1]->getRect().right();
for (int i = line.startIndex; i < line.endIndex; i++) {
int c = this->elements[i]->getSelectionIndexCount();
if (index + c > selection.max.charIndex) {
r = this->elements[i]->getXFromIndex(selection.max.charIndex - index);
break;
}
index += c;
}
QRect rect = line.rect;
2018-04-10 03:29:00 +02:00
rect.setTop(std::max(0, rect.top()) + yOffset);
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
2018-01-16 00:26:04 +01:00
rect.setLeft(this->elements[line.startIndex]->getRect().left());
rect.setRight(r);
2018-01-16 00:34:32 +01:00
painter.fillRect(rect, selectionColor);
2018-01-16 00:26:04 +01:00
break;
}
}
2018-01-15 04:08:48 +01:00
// selection
int MessageLayoutContainer::getSelectionIndex(QPoint point)
{
if (this->elements.size() == 0) {
return 0;
}
auto line = this->lines.begin();
for (; line != this->lines.end(); line++) {
if (line->rect.contains(point)) {
break;
}
}
2018-01-16 00:26:04 +01:00
int lineStart = line == this->lines.end() ? this->lines.back().startIndex : line->startIndex;
if (line != this->lines.end()) {
line++;
}
2018-01-15 04:08:48 +01:00
int lineEnd = line == this->lines.end() ? this->elements.size() : line->startIndex;
int index = 0;
for (int i = 0; i < lineEnd; i++) {
// end of line
if (i == lineEnd) {
break;
}
// before line
if (i < lineStart) {
index += this->elements[i]->getSelectionIndexCount();
continue;
}
// this is the word
2018-01-16 00:26:04 +01:00
if (point.x() < this->elements[i]->getRect().right()) {
2018-01-15 04:08:48 +01:00
index += this->elements[i]->getMouseOverIndex(point);
break;
}
2018-01-16 00:26:04 +01:00
index += this->elements[i]->getSelectionIndexCount();
2018-01-15 04:08:48 +01:00
}
return index;
}
2018-01-16 02:39:31 +01:00
// fourtf: no idea if this is acurate LOL
int MessageLayoutContainer::getLastCharacterIndex() const
{
if (this->lines.size() == 0) {
return 0;
}
return this->lines.back().endCharIndex;
}
void MessageLayoutContainer::addSelectionText(QString &str, int from, int to)
{
int index = 0;
2018-04-10 15:48:56 +02:00
bool first = true;
2018-01-16 02:39:31 +01:00
for (std::unique_ptr<MessageLayoutElement> &ele : this->elements) {
int c = ele->getSelectionIndexCount();
2018-04-10 15:48:56 +02:00
qDebug() << c;
if (first) {
2018-01-16 02:39:31 +01:00
if (index + c > from) {
ele->addCopyTextToString(str, from - index, to - index);
2018-04-10 15:48:56 +02:00
first = false;
if (index + c > to) {
break;
}
2018-01-16 02:39:31 +01:00
}
} else {
2018-04-10 15:48:56 +02:00
if (index + c > to) {
ele->addCopyTextToString(str, 0, to - index);
2018-01-16 02:39:31 +01:00
break;
} else {
ele->addCopyTextToString(str);
}
}
index += c;
}
}
2018-04-01 16:43:30 +02:00
} // namespace layouts
2018-01-13 02:13:59 +01:00
} // namespace messages
} // namespace chatterino