fixed text selection

This commit is contained in:
fourtf
2018-01-16 00:26:04 +01:00
parent 1133b33318
commit a190eda075
20 changed files with 278 additions and 1052 deletions
+1 -1
View File
@@ -160,7 +160,7 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection
this->bufferValid = false;
}
if (!this->bufferValid) {
if (!this->bufferValid || !selection.isEmpty()) {
this->updateBuffer(pixmap, messageIndex, selection);
}
+183 -8
View File
@@ -29,6 +29,7 @@ int MessageLayoutContainer::getHeight() const
void MessageLayoutContainer::clear()
{
this->elements.clear();
this->lines.clear();
this->height = 0;
this->line = 0;
@@ -36,6 +37,7 @@ void MessageLayoutContainer::clear()
this->currentY = 0;
this->lineStart = 0;
this->lineHeight = 0;
this->charIndex = 0;
}
void MessageLayoutContainer::addElement(MessageLayoutElement *element)
@@ -106,7 +108,16 @@ void MessageLayoutContainer::breakLine()
element->getRect().y() + this->lineHeight + yExtra));
}
this->lines.push_back({(int)lineStart, QRect(0, this->currentY, this->width, lineHeight)});
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 = 0;
@@ -132,8 +143,10 @@ void MessageLayoutContainer::finish()
}
if (this->lines.size() != 0) {
this->lines[0].rect.setTop(0);
this->lines.back().rect.setBottom(this->height);
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;
}
}
@@ -166,6 +179,166 @@ void MessageLayoutContainer::paintAnimatedElements(QPainter &painter, int yOffse
void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex,
Selection &selection)
{
// 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;
rect.setTop(std::max(0, rect.top()));
rect.setBottom(std::min(this->height, rect.bottom()));
rect.setLeft(this->elements[line.startIndex]->getRect().left());
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
painter.fillRect(rect, QColor(255, 255, 0, 127));
}
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;
rect.setTop(std::max(0, rect.top()));
rect.setBottom(std::min(this->height, rect.bottom()));
rect.setLeft(this->elements[line.startIndex]->getRect().left());
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
painter.fillRect(rect, QColor(255, 255, 0, 127));
}
returnAfter = true;
} else {
lineIndex++;
breakAfter = true;
}
break;
}
index += c;
}
QRect rect = line.rect;
rect.setTop(std::max(0, rect.top()));
rect.setBottom(std::min(this->height, rect.bottom()));
rect.setLeft(x);
rect.setRight(r);
painter.fillRect(rect, QColor(255, 255, 0, 127));
if (returnAfter) {
return;
}
if (breakAfter) {
break;
}
}
}
// start in this message
for (; lineIndex < this->lines.size(); lineIndex++) {
Line &line = this->lines[lineIndex];
index = line.startCharIndex;
bool breakAfter = false;
// just draw the garbage
if (line.endCharIndex < /*=*/selection.max.charIndex) {
QRect rect = line.rect;
rect.setTop(std::max(0, rect.top()));
rect.setBottom(std::min(this->height, rect.bottom()));
rect.setLeft(this->elements[line.startIndex]->getRect().left());
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
painter.fillRect(rect, QColor(255, 255, 0, 127));
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;
rect.setTop(std::max(0, rect.top()));
rect.setBottom(std::min(this->height, rect.bottom()));
rect.setLeft(this->elements[line.startIndex]->getRect().left());
rect.setRight(r);
painter.fillRect(rect, QColor(255, 255, 0, 127));
break;
}
}
void MessageLayoutContainer::_paintLine(QPainter &painter, Line &line, int x, int y)
{
QRect rect = line.rect;
rect.setTop(std::max(0, rect.top()));
rect.setBottom(std::min(this->height, rect.bottom()));
rect.setLeft(this->elements[line.startIndex]->getRect().left());
rect.setRight(this->elements[line.endIndex - 1]->getRect().right());
painter.fillRect(rect, QColor(255, 255, 0, 127));
}
// selection
@@ -183,10 +356,10 @@ int MessageLayoutContainer::getSelectionIndex(QPoint point)
}
}
assert(line != this->lines.end());
int lineStart = line->startIndex;
line++;
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;
@@ -204,10 +377,12 @@ int MessageLayoutContainer::getSelectionIndex(QPoint point)
}
// this is the word
if (point.x() > this->elements[i]->getRect().left()) {
if (point.x() < this->elements[i]->getRect().right()) {
index += this->elements[i]->getMouseOverIndex(point);
break;
}
index += this->elements[i]->getSelectionIndexCount();
}
qDebug() << index;
@@ -6,11 +6,12 @@
#include <QPoint>
#include <QRect>
#include "messages/selection.hpp"
class QPainter;
namespace chatterino {
namespace messages {
class Selection;
namespace layouts {
class MessageLayoutElement;
@@ -72,23 +73,28 @@ public:
int getSelectionIndex(QPoint point);
private:
struct Line {
int startIndex;
int endIndex;
int startCharIndex;
int endCharIndex;
QRect rect;
};
// helpers
void _addElement(MessageLayoutElement *element);
void _paintLine(QPainter &painter, Line &line, int x, int y);
// variables
int line;
int height;
int currentX, currentY;
int charIndex = 0;
size_t lineStart = 0;
int lineHeight = 0;
int spaceWidth = 4;
std::vector<std::unique_ptr<MessageLayoutElement>> elements;
struct Line {
int startIndex;
QRect rect;
};
std::vector<Line> lines;
};
} // namespace layouts
+51 -1
View File
@@ -1,6 +1,7 @@
#include "messages/layouts/messagelayoutelement.hpp"
#include "messages/messageelement.hpp"
#include <QDebug>
#include <QPainter>
namespace chatterino {
@@ -88,6 +89,18 @@ int ImageLayoutElement::getMouseOverIndex(const QPoint &abs)
return 0;
}
int ImageLayoutElement::getXFromIndex(int index)
{
if (index <= 0) {
return this->getRect().left();
} else if (index == 1) {
// fourtf: remove space width
return this->getRect().right();
} else {
return this->getRect().right();
}
}
//
// TEXT
//
@@ -127,7 +140,44 @@ void TextLayoutElement::paintAnimated(QPainter &, int)
int TextLayoutElement::getMouseOverIndex(const QPoint &abs)
{
return 0;
if (abs.x() < this->getRect().left()) {
return 0;
}
QFontMetrics &metrics =
singletons::FontManager::getInstance().getFontMetrics(this->style, this->scale);
int x = this->getRect().left();
for (int i = 0; i < this->text.size(); i++) {
int w = metrics.width(this->text[i]);
if (x + w > abs.x()) {
return i;
}
x += w;
}
return this->getSelectionIndexCount();
}
int TextLayoutElement::getXFromIndex(int index)
{
QFontMetrics &metrics =
singletons::FontManager::getInstance().getFontMetrics(this->style, this->scale);
if (index <= 0) {
return this->getRect().left();
} else if (index < this->text.size()) {
int x = 0;
for (int i = 0; i < index; i++) {
x += metrics.width(this->text[i]);
}
return x + this->getRect().left();
} else {
return this->getRect().right();
}
}
} // namespace layouts
} // namespace messages
@@ -36,6 +36,7 @@ public:
virtual void paint(QPainter &painter) = 0;
virtual void paintAnimated(QPainter &painter, int yOffset) = 0;
virtual int getMouseOverIndex(const QPoint &abs) = 0;
virtual int getXFromIndex(int index) = 0;
protected:
bool trailingSpace = true;
@@ -58,6 +59,7 @@ protected:
virtual void paint(QPainter &painter) override;
virtual void paintAnimated(QPainter &painter, int yOffset) override;
virtual int getMouseOverIndex(const QPoint &abs) override;
virtual int getXFromIndex(int index) override;
private:
Image &image;
@@ -76,6 +78,7 @@ protected:
virtual void paint(QPainter &painter) override;
virtual void paintAnimated(QPainter &painter, int yOffset) override;
virtual int getMouseOverIndex(const QPoint &abs) override;
virtual int getXFromIndex(int index) override;
private:
QString text;
+1 -1
View File
@@ -11,7 +11,7 @@
#include <QTime>
#include <boost/noncopyable.hpp>
#include <util/emotemap.hpp>
#include "util/emotemap.hpp"
namespace chatterino {
class Channel;
+22 -10
View File
@@ -8,24 +8,36 @@ struct SelectionItem {
SelectionItem()
{
messageIndex = charIndex = 0;
this->messageIndex = 0;
this->charIndex = 0;
}
SelectionItem(int _messageIndex, int _charIndex)
{
this->messageIndex = _messageIndex;
this->charIndex = _charIndex;
}
bool isSmallerThan(const SelectionItem &other) const
bool operator<(const SelectionItem &b) const
{
return this->messageIndex < other.messageIndex ||
(this->messageIndex == other.messageIndex && this->charIndex < other.charIndex);
if (this->messageIndex < b.messageIndex) {
return true;
}
if (this->messageIndex == b.messageIndex && this->charIndex < b.charIndex) {
return true;
}
return false;
}
bool equals(const SelectionItem &other) const
bool operator>(const SelectionItem &b) const
{
return this->messageIndex == other.messageIndex && this->charIndex == other.charIndex;
return b.operator<(*this);
}
bool operator==(const SelectionItem &b) const
{
return this->messageIndex == b.messageIndex && this->charIndex == b.charIndex;
}
};
@@ -45,14 +57,14 @@ struct Selection {
, min(start)
, max(end)
{
if (max.isSmallerThan(min)) {
if (min > max) {
std::swap(this->min, this->max);
}
}
bool isEmpty() const
{
return this->start.equals(this->end);
return this->start == this->end;
}
bool isSingleMessage() const
@@ -60,5 +72,5 @@ struct Selection {
return this->min.messageIndex == this->max.messageIndex;
}
};
}
}
} // namespace messages
} // namespace chatterino