2017-02-02 22:15:09 +01:00
|
|
|
#include "messageref.h"
|
2017-04-12 17:46:44 +02:00
|
|
|
#include "emotemanager.h"
|
|
|
|
#include "settingsmanager.h"
|
2017-02-02 22:15:09 +01:00
|
|
|
|
2017-02-03 19:31:51 +01:00
|
|
|
#include <QDebug>
|
|
|
|
|
2017-02-02 22:15:09 +01:00
|
|
|
#define MARGIN_LEFT 8
|
|
|
|
#define MARGIN_RIGHT 8
|
|
|
|
#define MARGIN_TOP 8
|
|
|
|
#define MARGIN_BOTTOM 8
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
using namespace chatterino::messages;
|
|
|
|
|
2017-02-02 22:15:09 +01:00
|
|
|
namespace chatterino {
|
|
|
|
namespace messages {
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
MessageRef::MessageRef(SharedMessage message)
|
|
|
|
: _message(message)
|
|
|
|
, _wordParts()
|
2017-02-03 19:31:51 +01:00
|
|
|
, buffer()
|
2017-02-02 22:15:09 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
Message *MessageRef::getMessage()
|
|
|
|
{
|
|
|
|
return _message.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
int MessageRef::getHeight() const
|
|
|
|
{
|
|
|
|
return _height;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MessageRef::layout(int width, bool enableEmoteMargins)
|
2017-02-02 22:15:09 +01:00
|
|
|
{
|
2017-04-12 17:46:44 +02:00
|
|
|
auto &settings = SettingsManager::getInstance();
|
2017-02-02 22:15:09 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
bool sizeChanged = width != _currentLayoutWidth;
|
|
|
|
bool redraw = width != _currentLayoutWidth;
|
2017-02-02 22:15:09 +01:00
|
|
|
int spaceWidth = 4;
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
int mediumTextLineHeight =
|
|
|
|
FontManager::getInstance().getFontMetrics(FontManager::Medium).height();
|
|
|
|
|
|
|
|
bool recalculateImages = _emoteGeneration != EmoteManager::getInstance().getGeneration();
|
|
|
|
bool recalculateText = _fontGeneration != FontManager::getInstance().getGeneration();
|
|
|
|
bool newWordTypes = _currentWordTypes != SettingsManager::getInstance().getWordTypeMask();
|
|
|
|
|
|
|
|
qreal emoteScale = settings.emoteScale.get();
|
|
|
|
bool scaleEmotesByLineHeight = settings.scaleEmotesByLineHeight.get();
|
|
|
|
|
|
|
|
// calculate word sizes
|
|
|
|
if (!redraw && !recalculateImages && !recalculateText && !newWordTypes) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
_emoteGeneration = EmoteManager::getInstance().getGeneration();
|
|
|
|
_fontGeneration = FontManager::getInstance().getGeneration();
|
|
|
|
|
|
|
|
for (auto &word : _message->getWords()) {
|
|
|
|
if (word.isImage()) {
|
|
|
|
if (!recalculateImages) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto &image = word.getImage();
|
|
|
|
|
|
|
|
qreal w = image.getWidth();
|
|
|
|
qreal h = image.getHeight();
|
|
|
|
|
|
|
|
if (scaleEmotesByLineHeight) {
|
|
|
|
word.setSize(w * mediumTextLineHeight / h * emoteScale,
|
|
|
|
mediumTextLineHeight * emoteScale);
|
|
|
|
} else {
|
|
|
|
word.setSize(w * image.getScale() * emoteScale, h * image.getScale() * emoteScale);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!recalculateText) {
|
|
|
|
continue;
|
2017-02-02 22:15:09 +01:00
|
|
|
}
|
2017-02-17 23:51:35 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
QFontMetrics &metrics = word.getFontMetrics();
|
|
|
|
word.setSize(metrics.width(word.getText()), metrics.height());
|
2017-02-17 23:51:35 +01:00
|
|
|
}
|
2017-02-02 22:15:09 +01:00
|
|
|
}
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
if (newWordTypes) {
|
|
|
|
_currentWordTypes = SettingsManager::getInstance().getWordTypeMask();
|
2017-02-02 22:15:09 +01:00
|
|
|
}
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
// layout
|
|
|
|
_currentLayoutWidth = width;
|
2017-02-03 19:31:51 +01:00
|
|
|
|
2017-02-02 22:15:09 +01:00
|
|
|
int x = MARGIN_LEFT;
|
|
|
|
int y = MARGIN_TOP;
|
|
|
|
|
|
|
|
int right = width - MARGIN_RIGHT - MARGIN_LEFT;
|
|
|
|
|
2017-02-17 23:51:35 +01:00
|
|
|
int lineNumber = 0;
|
2017-02-02 22:15:09 +01:00
|
|
|
int lineStart = 0;
|
|
|
|
int lineHeight = 0;
|
|
|
|
bool first = true;
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
_wordParts.clear();
|
2017-02-02 22:15:09 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
uint32_t flags = SettingsManager::getInstance().getWordTypeMask();
|
2017-02-02 22:15:09 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
for (auto it = _message->getWords().begin(); it != _message->getWords().end(); ++it) {
|
2017-02-02 22:15:09 +01:00
|
|
|
Word &word = *it;
|
|
|
|
|
|
|
|
if ((word.getType() & flags) == Word::None) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int xOffset = 0, yOffset = 0;
|
|
|
|
|
|
|
|
if (enableEmoteMargins) {
|
|
|
|
if (word.isImage() && word.getImage().getIsHat()) {
|
|
|
|
xOffset = -word.getWidth() + 2;
|
|
|
|
} else {
|
|
|
|
xOffset = word.getXOffset();
|
|
|
|
yOffset = word.getYOffset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// word wrapping
|
|
|
|
if (word.isText() && word.getWidth() + MARGIN_LEFT > right) {
|
2017-04-12 17:46:44 +02:00
|
|
|
alignWordParts(lineStart, lineHeight);
|
2017-02-02 22:15:09 +01:00
|
|
|
|
|
|
|
y += lineHeight;
|
|
|
|
|
|
|
|
const QString &text = word.getText();
|
|
|
|
|
|
|
|
int start = 0;
|
|
|
|
QFontMetrics &metrics = word.getFontMetrics();
|
|
|
|
|
|
|
|
int width = 0;
|
|
|
|
|
|
|
|
std::vector<short> &charWidths = word.getCharacterWidthCache();
|
|
|
|
|
|
|
|
if (charWidths.size() == 0) {
|
|
|
|
for (int i = 0; i < text.length(); i++) {
|
|
|
|
charWidths.push_back(metrics.charWidth(text, i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 2; i <= text.length(); i++) {
|
|
|
|
if ((width = width + charWidths[i - 1]) + MARGIN_LEFT > right) {
|
|
|
|
QString mid = text.mid(start, i - start - 1);
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
_wordParts.push_back(WordPart(word, MARGIN_LEFT, y, width, word.getHeight(),
|
|
|
|
lineNumber, mid, mid));
|
2017-02-02 22:15:09 +01:00
|
|
|
|
|
|
|
y += metrics.height();
|
|
|
|
|
|
|
|
start = i - 1;
|
|
|
|
|
|
|
|
width = 0;
|
2017-02-17 23:51:35 +01:00
|
|
|
lineNumber++;
|
2017-02-02 22:15:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QString mid(text.mid(start));
|
|
|
|
width = metrics.width(mid);
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
_wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(), width,
|
|
|
|
word.getHeight(), lineNumber, mid, mid));
|
2017-02-02 22:15:09 +01:00
|
|
|
x = width + MARGIN_LEFT + spaceWidth;
|
|
|
|
|
|
|
|
lineHeight = word.getHeight();
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
lineStart = _wordParts.size() - 1;
|
2017-02-02 22:15:09 +01:00
|
|
|
|
|
|
|
first = false;
|
|
|
|
} else if (first || x + word.getWidth() + xOffset <= right) {
|
|
|
|
// fits in the line
|
2017-04-12 17:46:44 +02:00
|
|
|
_wordParts.push_back(
|
|
|
|
WordPart(word, x, y - word.getHeight(), lineNumber, word.getCopyText()));
|
2017-02-02 22:15:09 +01:00
|
|
|
|
|
|
|
x += word.getWidth() + xOffset;
|
|
|
|
x += spaceWidth;
|
|
|
|
|
|
|
|
lineHeight = std::max(word.getHeight(), lineHeight);
|
|
|
|
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
// doesn't fit in the line
|
2017-04-12 17:46:44 +02:00
|
|
|
alignWordParts(lineStart, lineHeight);
|
2017-02-02 22:15:09 +01:00
|
|
|
|
|
|
|
y += lineHeight;
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
_wordParts.push_back(
|
|
|
|
WordPart(word, MARGIN_LEFT, y - word.getHeight(), lineNumber, word.getCopyText()));
|
2017-02-02 22:15:09 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
lineStart = _wordParts.size() - 1;
|
2017-02-02 22:15:09 +01:00
|
|
|
|
|
|
|
lineHeight = word.getHeight();
|
|
|
|
|
|
|
|
x = word.getWidth() + MARGIN_LEFT;
|
|
|
|
x += spaceWidth;
|
2017-02-17 23:51:35 +01:00
|
|
|
|
|
|
|
lineNumber++;
|
2017-02-02 22:15:09 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
alignWordParts(lineStart, lineHeight);
|
2017-02-02 22:15:09 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
if (_height != y + lineHeight) {
|
2017-02-03 19:31:51 +01:00
|
|
|
sizeChanged = true;
|
2017-04-12 17:46:44 +02:00
|
|
|
_height = y + lineHeight;
|
2017-02-03 19:31:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sizeChanged) {
|
2017-04-12 17:46:44 +02:00
|
|
|
buffer = nullptr;
|
2017-02-03 19:31:51 +01:00
|
|
|
}
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
updateBuffer = true;
|
2017-02-02 22:15:09 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
const std::vector<WordPart> &MessageRef::getWordParts() const
|
|
|
|
{
|
|
|
|
return _wordParts;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MessageRef::alignWordParts(int lineStart, int lineHeight)
|
2017-02-02 22:15:09 +01:00
|
|
|
{
|
2017-04-12 17:46:44 +02:00
|
|
|
for (size_t i = lineStart; i < _wordParts.size(); i++) {
|
|
|
|
WordPart &wordPart2 = _wordParts.at(i);
|
2017-02-02 22:15:09 +01:00
|
|
|
|
|
|
|
wordPart2.setY(wordPart2.getY() + lineHeight);
|
|
|
|
}
|
|
|
|
}
|
2017-02-17 23:51:35 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
bool MessageRef::tryGetWordPart(QPoint point, Word &word)
|
2017-02-17 23:51:35 +01:00
|
|
|
{
|
2017-04-12 17:46:44 +02:00
|
|
|
// go through all words and return the first one that contains the point.
|
|
|
|
for (WordPart &wordPart : _wordParts) {
|
2017-02-17 23:51:35 +01:00
|
|
|
if (wordPart.getRect().contains(point)) {
|
|
|
|
word = wordPart.getWord();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
int MessageRef::getSelectionIndex(QPoint position)
|
2017-02-17 23:51:35 +01:00
|
|
|
{
|
2017-04-12 17:46:44 +02:00
|
|
|
if (_wordParts.size() == 0) {
|
2017-02-17 23:51:35 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find out in which line the cursor is
|
|
|
|
int lineNumber = 0, lineStart = 0, lineEnd = 0;
|
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
for (int i = 0; i < _wordParts.size(); i++) {
|
|
|
|
WordPart &part = _wordParts[i];
|
2017-02-17 23:51:35 +01:00
|
|
|
|
|
|
|
// return if curser under the word
|
|
|
|
if (position.y() >= part.getBottom()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (part.getLineNumber() != lineNumber) {
|
|
|
|
lineStart = i;
|
|
|
|
lineNumber = part.getLineNumber();
|
|
|
|
}
|
|
|
|
|
|
|
|
lineEnd = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
// count up to the cursor
|
|
|
|
int index = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < lineStart; i++) {
|
2017-04-12 17:46:44 +02:00
|
|
|
WordPart &part = _wordParts[i];
|
2017-02-17 23:51:35 +01:00
|
|
|
|
|
|
|
index += part.getWord().isImage() ? 2 : part.getText().length() + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = lineStart; i < lineEnd; i++) {
|
2017-04-12 17:46:44 +02:00
|
|
|
WordPart &part = _wordParts[i];
|
2017-02-17 23:51:35 +01:00
|
|
|
|
|
|
|
// curser is left of the word part
|
|
|
|
if (position.x() < part.getX()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// cursor is right of the word part
|
|
|
|
if (position.x() > part.getX()) {
|
|
|
|
index += part.getWord().isImage() ? 2 : part.getText().length() + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// cursor is over the word part
|
|
|
|
if (part.getWord().isImage()) {
|
|
|
|
index++;
|
|
|
|
} else {
|
|
|
|
auto text = part.getWord().getText();
|
|
|
|
|
|
|
|
int x = part.getX();
|
|
|
|
|
|
|
|
for (int j = 0; j < text.length(); j++) {
|
|
|
|
if (x > position.x()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
index++;
|
2017-04-12 17:46:44 +02:00
|
|
|
x = part.getX() + part.getWord().getFontMetrics().width(text, j + 1);
|
2017-02-17 23:51:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return index;
|
|
|
|
|
|
|
|
// go through all the wordparts
|
2017-04-12 17:46:44 +02:00
|
|
|
// for (int i = 0; i < wordParts; i < wordParts.size()) {
|
2017-02-17 23:51:35 +01:00
|
|
|
|
2017-04-12 17:46:44 +02:00
|
|
|
// WordPart &part = wordParts[i];
|
2017-02-17 23:51:35 +01:00
|
|
|
|
|
|
|
// // return if curser under the word
|
|
|
|
// if (position.y() >= part.getBottom()) {
|
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // increment index and continue if the curser x is bigger than the
|
|
|
|
// words
|
|
|
|
// // right edge
|
|
|
|
// if (position.x() > part.getRight()) {
|
|
|
|
// index += part.getWord().isImage() ? 2 +
|
|
|
|
// part.getText().length() + 1;
|
|
|
|
// continue;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
}
|
2017-02-02 22:15:09 +01:00
|
|
|
}
|
|
|
|
}
|