rename #2
This commit is contained in:
@@ -0,0 +1,502 @@
|
||||
#include "messagelayoutcontainer.hpp"
|
||||
|
||||
#include "application.hpp"
|
||||
#include "messagelayoutelement.hpp"
|
||||
#include "messages/selection.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
|
||||
#define COMPACT_EMOTES_OFFSET 6
|
||||
#define MAX_UNCOLLAPSED_LINES (getApp()->settings->collpseMessagesMinLines.getValue())
|
||||
|
||||
namespace chatterino {
|
||||
namespace messages {
|
||||
namespace layouts {
|
||||
|
||||
int MessageLayoutContainer::getHeight() const
|
||||
{
|
||||
return this->height;
|
||||
}
|
||||
|
||||
int MessageLayoutContainer::getWidth() const
|
||||
{
|
||||
return this->width;
|
||||
}
|
||||
|
||||
float MessageLayoutContainer::getScale() const
|
||||
{
|
||||
return this->scale;
|
||||
}
|
||||
|
||||
// methods
|
||||
void MessageLayoutContainer::begin(int _width, float _scale, Message::MessageFlags _flags)
|
||||
{
|
||||
this->clear();
|
||||
this->width = _width;
|
||||
this->scale = _scale;
|
||||
this->flags = _flags;
|
||||
auto mediumFontMetrics = getApp()->fonts->getFontMetrics(FontStyle::ChatMedium, _scale);
|
||||
this->textLineHeight = mediumFontMetrics.height();
|
||||
this->spaceWidth = mediumFontMetrics.width(' ');
|
||||
this->dotdotdotWidth = mediumFontMetrics.width("...");
|
||||
this->_canAddMessages = true;
|
||||
this->_isCollapsed = false;
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::clear()
|
||||
{
|
||||
this->elements.clear();
|
||||
this->lines.clear();
|
||||
|
||||
this->height = 0;
|
||||
this->line = 0;
|
||||
this->currentX = 0;
|
||||
this->currentY = 0;
|
||||
this->lineStart = 0;
|
||||
this->lineHeight = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
bool MessageLayoutContainer::canAddElements()
|
||||
{
|
||||
return this->_canAddMessages;
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::_addElement(MessageLayoutElement *element, bool forceAdd)
|
||||
{
|
||||
if (!this->canAddElements() && !forceAdd) {
|
||||
delete element;
|
||||
return;
|
||||
}
|
||||
|
||||
// top margin
|
||||
if (this->elements.size() == 0) {
|
||||
this->currentY = this->margin.top * this->scale;
|
||||
}
|
||||
|
||||
int newLineHeight = element->getRect().height();
|
||||
|
||||
// compact emote offset
|
||||
bool isCompactEmote = !(this->flags & Message::DisableCompactEmotes) &&
|
||||
element->getCreator().getFlags() & MessageElement::EmoteImages;
|
||||
|
||||
if (isCompactEmote) {
|
||||
newLineHeight -= COMPACT_EMOTES_OFFSET * this->scale;
|
||||
}
|
||||
|
||||
// update line height
|
||||
this->lineHeight = std::max(this->lineHeight, newLineHeight);
|
||||
|
||||
// set move element
|
||||
element->setPosition(QPoint(this->currentX, this->currentY - element->getRect().height()));
|
||||
|
||||
// add element
|
||||
this->elements.push_back(std::unique_ptr<MessageLayoutElement>(element));
|
||||
|
||||
// set current x
|
||||
this->currentX += element->getRect().width();
|
||||
|
||||
if (element->hasTrailingSpace()) {
|
||||
this->currentX += this->spaceWidth;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::breakLine()
|
||||
{
|
||||
int xOffset = 0;
|
||||
|
||||
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();
|
||||
|
||||
bool isCompactEmote = !(this->flags & Message::DisableCompactEmotes) &&
|
||||
element->getCreator().getFlags() & MessageElement::EmoteImages;
|
||||
|
||||
int yExtra = 0;
|
||||
if (isCompactEmote) {
|
||||
yExtra = (COMPACT_EMOTES_OFFSET / 2) * this->scale;
|
||||
}
|
||||
|
||||
// if (element->getCreator().getFlags() & MessageElement::Badges) {
|
||||
if (element->getRect().height() < this->textLineHeight) {
|
||||
yExtra -= (this->textLineHeight - element->getRect().height()) / 2;
|
||||
}
|
||||
|
||||
element->setPosition(QPoint(element->getRect().x() + xOffset + this->margin.left,
|
||||
element->getRect().y() + this->lineHeight + yExtra));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
this->lineStart = this->elements.size();
|
||||
// this->currentX = (int)(this->scale * 8);
|
||||
|
||||
if (this->canCollapse() && line + 1 >= MAX_UNCOLLAPSED_LINES) {
|
||||
this->_canAddMessages = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this->currentX = 0;
|
||||
this->currentY += this->lineHeight;
|
||||
this->height = this->currentY + (this->margin.bottom * this->scale);
|
||||
this->lineHeight = 0;
|
||||
this->line++;
|
||||
}
|
||||
|
||||
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 -
|
||||
(this->line + 1 == MAX_UNCOLLAPSED_LINES ? this->dotdotdotWidth : 0));
|
||||
}
|
||||
|
||||
void MessageLayoutContainer::end()
|
||||
{
|
||||
if (!this->canAddElements()) {
|
||||
static TextElement dotdotdot("...", MessageElement::Collapsed, MessageColor::Link);
|
||||
static QString dotdotdotText("...");
|
||||
|
||||
auto *element = new TextLayoutElement(
|
||||
dotdotdot, dotdotdotText, QSize(this->dotdotdotWidth, this->textLineHeight),
|
||||
QColor("#00D80A"), FontStyle::ChatMediumBold, this->scale);
|
||||
|
||||
// getApp()->themes->messages.textColors.system
|
||||
this->_addElement(element, true);
|
||||
this->_isCollapsed = true;
|
||||
}
|
||||
|
||||
if (!this->atStartOfLine()) {
|
||||
this->breakLine();
|
||||
}
|
||||
|
||||
this->height += this->lineHeight;
|
||||
|
||||
if (this->lines.size() != 0) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageLayoutContainer::canCollapse()
|
||||
{
|
||||
return getApp()->settings->collpseMessagesMinLines.getValue() > 0 &&
|
||||
this->flags & Message::MessageFlags::Collapsed;
|
||||
}
|
||||
|
||||
bool MessageLayoutContainer::isCollapsed()
|
||||
{
|
||||
return this->_isCollapsed;
|
||||
}
|
||||
|
||||
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) {
|
||||
#ifdef FOURTF
|
||||
painter.setPen(QColor(0, 255, 0));
|
||||
painter.drawRect(element->getRect());
|
||||
#endif
|
||||
|
||||
element->paint(painter);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
Selection &selection, int yOffset)
|
||||
{
|
||||
auto app = getApp();
|
||||
QColor selectionColor = app->themes->messages.selection;
|
||||
|
||||
// don't draw anything
|
||||
if (selection.selectionMin.messageIndex > messageIndex ||
|
||||
selection.selectionMax.messageIndex < messageIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fully selected
|
||||
if (selection.selectionMin.messageIndex < messageIndex &&
|
||||
selection.selectionMax.messageIndex > messageIndex) {
|
||||
for (Line &line : this->lines) {
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()) + yOffset);
|
||||
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
|
||||
rect.setLeft(this->elements[line.startIndex]->getRect().left());
|
||||
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
|
||||
|
||||
painter.fillRect(rect, selectionColor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int lineIndex = 0;
|
||||
int index = 0;
|
||||
|
||||
// start in this message
|
||||
if (selection.selectionMin.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.selectionMin.charIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = line.startIndex; i < line.endIndex; i++) {
|
||||
int c = this->elements[i]->getSelectionIndexCount();
|
||||
|
||||
if (index + c > selection.selectionMin.charIndex) {
|
||||
x = this->elements[i]->getXFromIndex(selection.selectionMin.charIndex - index);
|
||||
|
||||
// ends in same line
|
||||
if (selection.selectionMax.messageIndex == messageIndex &&
|
||||
line.endCharIndex > /*=*/selection.selectionMax.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.selectionMax.charIndex) {
|
||||
r = this->elements[i]->getXFromIndex(
|
||||
selection.selectionMax.charIndex - index);
|
||||
break;
|
||||
}
|
||||
index += c;
|
||||
}
|
||||
}
|
||||
// ends in same line end
|
||||
|
||||
if (selection.selectionMax.messageIndex != messageIndex) {
|
||||
int lineIndex2 = lineIndex + 1;
|
||||
for (; lineIndex2 < this->lines.size(); lineIndex2++) {
|
||||
Line &line = this->lines[lineIndex2];
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()) + yOffset);
|
||||
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
|
||||
rect.setLeft(this->elements[line.startIndex]->getRect().left());
|
||||
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
|
||||
|
||||
painter.fillRect(rect, selectionColor);
|
||||
}
|
||||
returnAfter = true;
|
||||
} else {
|
||||
lineIndex++;
|
||||
breakAfter = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
index += c;
|
||||
}
|
||||
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()) + yOffset);
|
||||
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
|
||||
rect.setLeft(x);
|
||||
rect.setRight(r);
|
||||
|
||||
painter.fillRect(rect, selectionColor);
|
||||
|
||||
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.selectionMax.charIndex) {
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()) + yOffset);
|
||||
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
|
||||
rect.setLeft(this->elements[line.startIndex]->getRect().left());
|
||||
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
|
||||
|
||||
painter.fillRect(rect, selectionColor);
|
||||
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.selectionMax.charIndex) {
|
||||
r = this->elements[i]->getXFromIndex(selection.selectionMax.charIndex - index);
|
||||
break;
|
||||
}
|
||||
|
||||
index += c;
|
||||
}
|
||||
|
||||
QRect rect = line.rect;
|
||||
|
||||
rect.setTop(std::max(0, rect.top()) + yOffset);
|
||||
rect.setBottom(std::min(this->height, rect.bottom()) + yOffset);
|
||||
rect.setLeft(this->elements[line.startIndex]->getRect().left());
|
||||
rect.setRight(r);
|
||||
|
||||
painter.fillRect(rect, selectionColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
int lineStart = line == this->lines.end() ? this->lines.back().startIndex : line->startIndex;
|
||||
if (line != this->lines.end()) {
|
||||
line++;
|
||||
}
|
||||
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
|
||||
if (point.x() < this->elements[i]->getRect().right()) {
|
||||
index += this->elements[i]->getMouseOverIndex(point);
|
||||
break;
|
||||
}
|
||||
|
||||
index += this->elements[i]->getSelectionIndexCount();
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
// 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;
|
||||
bool first = true;
|
||||
|
||||
for (std::unique_ptr<MessageLayoutElement> &ele : this->elements) {
|
||||
int c = ele->getSelectionIndexCount();
|
||||
|
||||
if (first) {
|
||||
if (index + c > from) {
|
||||
ele->addCopyTextToString(str, from - index, to - index);
|
||||
first = false;
|
||||
|
||||
if (index + c > to) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (index + c > to) {
|
||||
ele->addCopyTextToString(str, 0, to - index);
|
||||
break;
|
||||
} else {
|
||||
ele->addCopyTextToString(str);
|
||||
}
|
||||
}
|
||||
|
||||
index += c;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace layouts
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
Reference in New Issue
Block a user