replaced qt font scaling

remove the qt font scaling and added code that uses the actual scale/dpi value
This commit is contained in:
fourtf 2017-12-23 21:18:13 +01:00
parent c9aa716f58
commit fc81b118c7
21 changed files with 298 additions and 155 deletions

View file

@ -8,6 +8,7 @@ QT += core gui network multimedia
CONFIG += communi CONFIG += communi
COMMUNI += core model util COMMUNI += core model util
CONFIG += c++14 CONFIG += c++14
PRECOMPILED_HEADER = precompiled_header.hpp
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
@ -111,6 +112,7 @@ SOURCES += \
src/widgets/accountswitchpopupwidget.cpp src/widgets/accountswitchpopupwidget.cpp
HEADERS += \ HEADERS += \
src/precompiled_headers.hpp \
src/asyncexec.hpp \ src/asyncexec.hpp \
src/channel.hpp \ src/channel.hpp \
src/colorscheme.hpp \ src/colorscheme.hpp \
@ -183,7 +185,9 @@ HEADERS += \
src/util/helpers.hpp \ src/util/helpers.hpp \
src/widgets/accountswitchwidget.hpp \ src/widgets/accountswitchwidget.hpp \
src/widgets/accountswitchpopupwidget.hpp \ src/widgets/accountswitchpopupwidget.hpp \
src/const.hpp src/const.hpp \
src/precompiled_headers.hpp \
src/messages/wordflags.hpp
PRECOMPILED_HEADER = PRECOMPILED_HEADER =

View file

@ -6,29 +6,33 @@ namespace chatterino {
FontManager::FontManager() FontManager::FontManager()
: currentFontFamily("/appearance/currentFontFamily", "Arial") : currentFontFamily("/appearance/currentFontFamily", "Arial")
, currentFontSize("/appearance/currentFontSize", 14) , currentFontSize("/appearance/currentFontSize", 12)
, currentFont(this->currentFontFamily.getValue().c_str(), currentFontSize.getValue()) // , currentFont(this->currentFontFamily.getValue().c_str(), currentFontSize.getValue())
{ {
this->currentFontFamily.connect([this](const std::string &newValue, auto) { this->currentFontFamily.connect([this](const std::string &newValue, auto) {
this->incGeneration(); this->incGeneration();
this->currentFont.setFamily(newValue.c_str()); // this->currentFont.setFamily(newValue.c_str());
this->currentFontByDpi.clear();
this->fontChanged.invoke(); this->fontChanged.invoke();
}); });
this->currentFontSize.connect([this](const int &newValue, auto) { this->currentFontSize.connect([this](const int &newValue, auto) {
this->incGeneration(); this->incGeneration();
this->currentFont.setSize(newValue); // this->currentFont.setSize(newValue);
this->currentFontByDpi.clear();
this->fontChanged.invoke(); this->fontChanged.invoke();
}); });
} }
QFont &FontManager::getFont(Type type) QFont &FontManager::getFont(Type type, float dpi)
{ {
return this->currentFont.getFont(type); // return this->currentFont.getFont(type);
return this->getCurrentFont(dpi).getFont(type);
} }
QFontMetrics &FontManager::getFontMetrics(Type type) QFontMetrics &FontManager::getFontMetrics(Type type, float dpi)
{ {
return this->currentFont.getFontMetrics(type); // return this->currentFont.getFontMetrics(type);
return this->getCurrentFont(dpi).getFontMetrics(type);
} }
FontManager::FontData &FontManager::Font::getFontData(Type type) FontManager::FontData &FontManager::Font::getFontData(Type type)
@ -62,4 +66,17 @@ QFontMetrics &FontManager::Font::getFontMetrics(Type type)
return this->getFontData(type).metrics; return this->getFontData(type).metrics;
} }
FontManager::Font &FontManager::getCurrentFont(float dpi)
{
for (auto it = this->currentFontByDpi.begin(); it != this->currentFontByDpi.end(); it++) {
if (it->first == dpi) {
return it->second;
}
}
this->currentFontByDpi.push_back(std::make_pair(
dpi,
Font(this->currentFontFamily.getValue().c_str(), this->currentFontSize.getValue() * dpi)));
return this->currentFontByDpi.back().second;
}
} // namespace chatterino } // namespace chatterino

View file

@ -30,8 +30,8 @@ public:
return instance; return instance;
} }
QFont &getFont(Type type); QFont &getFont(Type type, float dpi);
QFontMetrics &getFontMetrics(Type type); QFontMetrics &getFontMetrics(Type type, float dpi);
int getGeneration() const int getGeneration() const
{ {
@ -122,10 +122,12 @@ private:
FontData veryLarge; FontData veryLarge;
}; };
Font &getCurrentFont(float dpi);
// Future plans: // Future plans:
// Could have multiple fonts in here, such as "Menu font", "Application font", "Chat font" // Could have multiple fonts in here, such as "Menu font", "Application font", "Chat font"
Font currentFont; std::list<std::pair<float, Font>> currentFontByDpi;
int generation = 0; int generation = 0;
}; };

View file

@ -44,7 +44,8 @@ inline bool initSettings(bool portable)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// QApplication::setAttribute(Qt::AA_Use96Dpi, true); QApplication::setAttribute(Qt::AA_Use96Dpi, true);
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling, true);
QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL, true); QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL, true);
QApplication a(argc, argv); QApplication a(argc, argv);

View file

@ -83,13 +83,15 @@ void AddCurrentTimestamp(Message *message)
strftime(timeStampBuffer, 69, "%H:%M", localtime(&t)); strftime(timeStampBuffer, 69, "%H:%M", localtime(&t));
QString timestampNoSeconds(timeStampBuffer); QString timestampNoSeconds(timeStampBuffer);
message->getWords().push_back(Word(timestampNoSeconds, Word::TimestampNoSeconds, message->getWords().push_back(Word(timestampNoSeconds, Word::TimestampNoSeconds,
MessageColor(MessageColor::System), QString(), QString())); MessageColor(MessageColor::System), FontManager::Medium,
QString(), QString()));
// Add word for timestamp with seconds // Add word for timestamp with seconds
strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&t)); strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&t));
QString timestampWithSeconds(timeStampBuffer); QString timestampWithSeconds(timeStampBuffer);
message->getWords().push_back(Word(timestampWithSeconds, Word::TimestampWithSeconds, message->getWords().push_back(Word(timestampWithSeconds, Word::TimestampWithSeconds,
MessageColor(MessageColor::System), QString(), QString())); MessageColor(MessageColor::System), FontManager::Medium,
QString(), QString()));
} }
} // namespace } // namespace
@ -101,7 +103,8 @@ Message *Message::createSystemMessage(const QString &text)
AddCurrentTimestamp(message); AddCurrentTimestamp(message);
Word word(text, Word::Type::Default, MessageColor(MessageColor::Type::System), text, QString()); Word word(text, Word::Flags::Default, MessageColor(MessageColor::Type::System),
FontManager::Medium, text, QString());
message->getWords().push_back(word); message->getWords().push_back(word);
@ -142,7 +145,8 @@ Message *Message::createTimeoutMessage(const QString &username, const QString &d
} }
text.append("."); text.append(".");
Word word(text, Word::Type::Default, MessageColor(MessageColor::Type::System), text, QString()); Word word(text, Word::Flags::Default, MessageColor(MessageColor::Type::System),
FontManager::Medium, text, QString());
message->getWords().push_back(word); message->getWords().push_back(word);

View file

@ -42,13 +42,15 @@ void MessageBuilder::appendTimestamp(time_t time)
strftime(timeStampBuffer, 69, "%H:%M", localtime(&time)); strftime(timeStampBuffer, 69, "%H:%M", localtime(&time));
QString timestampNoSeconds(timeStampBuffer); QString timestampNoSeconds(timeStampBuffer);
this->appendWord(Word(timestampNoSeconds, Word::TimestampNoSeconds, this->appendWord(Word(timestampNoSeconds, Word::TimestampNoSeconds,
MessageColor(MessageColor::System), QString(), QString())); MessageColor(MessageColor::System), FontManager::Medium, QString(),
QString()));
// Add word for timestamp with seconds // Add word for timestamp with seconds
strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time)); strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time));
QString timestampWithSeconds(timeStampBuffer); QString timestampWithSeconds(timeStampBuffer);
this->appendWord(Word(timestampWithSeconds, Word::TimestampWithSeconds, this->appendWord(Word(timestampWithSeconds, Word::TimestampWithSeconds,
MessageColor(MessageColor::System), QString(), QString())); MessageColor(MessageColor::System), FontManager::Medium, QString(),
QString()));
} }
QString MessageBuilder::matchLink(const QString &string) QString MessageBuilder::matchLink(const QString &string)

View file

@ -4,10 +4,10 @@
#include <QDebug> #include <QDebug>
#define MARGIN_LEFT (int)(8 * this->dpiMultiplier) #define MARGIN_LEFT (int)(8 * this->scale)
#define MARGIN_RIGHT (int)(8 * this->dpiMultiplier) #define MARGIN_RIGHT (int)(8 * this->scale)
#define MARGIN_TOP (int)(4 * this->dpiMultiplier) #define MARGIN_TOP (int)(4 * this->scale)
#define MARGIN_BOTTOM (int)(4 * this->dpiMultiplier) #define MARGIN_BOTTOM (int)(4 * this->scale)
using namespace chatterino::messages; using namespace chatterino::messages;
@ -32,11 +32,11 @@ int MessageRef::getHeight() const
} }
// return true if redraw is required // return true if redraw is required
bool MessageRef::layout(int width, float dpiMultiplyer) bool MessageRef::layout(int width, float scale)
{ {
auto &emoteManager = EmoteManager::getInstance(); auto &emoteManager = EmoteManager::getInstance();
bool layoutRequired = false; bool rebuildRequired = false, layoutRequired = false;
// check if width changed // check if width changed
bool widthChanged = width != this->currentLayoutWidth; bool widthChanged = width != this->currentLayoutWidth;
@ -60,11 +60,11 @@ bool MessageRef::layout(int width, float dpiMultiplyer)
this->currentWordTypes = SettingsManager::getInstance().getWordTypeMask(); this->currentWordTypes = SettingsManager::getInstance().getWordTypeMask();
// check if dpi changed // check if dpi changed
bool dpiChanged = this->dpiMultiplier != dpiMultiplyer; bool scaleChanged = this->scale != scale;
layoutRequired |= dpiChanged; layoutRequired |= scaleChanged;
this->dpiMultiplier = dpiMultiplyer; this->scale = scale;
imagesChanged |= dpiChanged; imagesChanged |= scaleChanged;
textChanged |= dpiChanged; textChanged |= scaleChanged;
// update word sizes if needed // update word sizes if needed
if (imagesChanged) { if (imagesChanged) {
@ -119,7 +119,7 @@ void MessageRef::actuallyLayout(int width)
Word &word = *it; Word &word = *it;
// Check if given word is supposed to be rendered by comparing it to the current setting // Check if given word is supposed to be rendered by comparing it to the current setting
if ((word.getType() & flags) == Word::None) { if ((word.getFlags() & flags) == Word::None) {
continue; continue;
} }
@ -135,7 +135,7 @@ void MessageRef::actuallyLayout(int width)
/// } /// }
// word wrapping // word wrapping
if (word.isText() && word.getWidth() + MARGIN_LEFT > right) { if (word.isText() && word.getWidth(this->scale) + MARGIN_LEFT > right) {
// align and end the current line // align and end the current line
alignWordParts(lineStart, lineHeight, width, firstLineHeight); alignWordParts(lineStart, lineHeight, width, firstLineHeight);
y += lineHeight; y += lineHeight;
@ -145,17 +145,17 @@ void MessageRef::actuallyLayout(int width)
// go through the text, break text when it doesn't fit in the line anymore // go through the text, break text when it doesn't fit in the line anymore
for (int i = 1; i <= word.getText().length(); i++) { for (int i = 1; i <= word.getText().length(); i++) {
currentLineWidth += word.getCharWidth(i - 1); currentLineWidth += word.getCharWidth(i - 1, this->scale);
if (currentLineWidth + MARGIN_LEFT > right) { if (currentLineWidth + MARGIN_LEFT > right) {
// add the current line // add the current line
QString mid = word.getText().mid(currentPartStart, i - currentPartStart - 1); QString mid = word.getText().mid(currentPartStart, i - currentPartStart - 1);
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y, currentLineWidth, this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y, currentLineWidth,
word.getHeight(), lineNumber, mid, mid, word.getHeight(this->scale), lineNumber, mid,
false, currentPartStart)); mid, false, currentPartStart));
y += word.getFontMetrics().height(); y += word.getFontMetrics(this->scale).height();
currentPartStart = i - 1; currentPartStart = i - 1;
@ -165,27 +165,27 @@ void MessageRef::actuallyLayout(int width)
} }
QString mid(word.getText().mid(currentPartStart)); QString mid(word.getText().mid(currentPartStart));
currentLineWidth = word.getFontMetrics().width(mid); currentLineWidth = word.getFontMetrics(this->scale).width(mid);
this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(), this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(this->scale),
currentLineWidth, word.getHeight(), lineNumber, mid, currentLineWidth, word.getHeight(this->scale),
mid, true, currentPartStart)); lineNumber, mid, mid, true, currentPartStart));
x = currentLineWidth + MARGIN_LEFT + spaceWidth; x = currentLineWidth + MARGIN_LEFT + spaceWidth;
lineHeight = word.getHeight(); lineHeight = word.getHeight(this->scale);
lineStart = this->wordParts.size() - 1; lineStart = this->wordParts.size() - 1;
first = false; first = false;
} }
// fits in the current line // fits in the current line
else if (first || x + word.getWidth() + xOffset <= right) { else if (first || x + word.getWidth(this->scale) + xOffset <= right) {
this->wordParts.push_back( this->wordParts.push_back(WordPart(word, x, y - word.getHeight(this->scale), scale,
WordPart(word, x, y - word.getHeight(), lineNumber, word.getCopyText())); lineNumber, word.getCopyText()));
x += word.getWidth() + xOffset; x += word.getWidth(this->scale) + xOffset;
x += spaceWidth; x += spaceWidth;
lineHeight = std::max(word.getHeight(), lineHeight); lineHeight = std::max(word.getHeight(this->scale), lineHeight);
first = false; first = false;
} }
@ -198,14 +198,14 @@ void MessageRef::actuallyLayout(int width)
lineNumber++; lineNumber++;
this->wordParts.push_back( this->wordParts.push_back(WordPart(word, MARGIN_LEFT, y - word.getHeight(this->scale),
WordPart(word, MARGIN_LEFT, y - word.getHeight(), lineNumber, word.getCopyText())); this->scale, lineNumber, word.getCopyText()));
lineStart = this->wordParts.size() - 1; lineStart = this->wordParts.size() - 1;
lineHeight = word.getHeight(); lineHeight = word.getHeight(this->scale);
x = word.getWidth() + MARGIN_LEFT; x = word.getWidth(this->scale) + MARGIN_LEFT;
x += spaceWidth; x += spaceWidth;
} }
} }
@ -213,7 +213,7 @@ void MessageRef::actuallyLayout(int width)
// align and end the current line // align and end the current line
alignWordParts(lineStart, lineHeight, width, firstLineHeight); alignWordParts(lineStart, lineHeight, width, firstLineHeight);
this->collapsedHeight = firstLineHeight == -1 ? (int)(24 * dpiMultiplier) this->collapsedHeight = firstLineHeight == -1 ? (int)(24 * this->scale)
: firstLineHeight + MARGIN_TOP + MARGIN_BOTTOM; : firstLineHeight + MARGIN_TOP + MARGIN_BOTTOM;
// update height // update height
@ -236,37 +236,21 @@ void MessageRef::actuallyLayout(int width)
void MessageRef::updateTextSizes() void MessageRef::updateTextSizes()
{ {
for (auto &word : this->message->getWords()) { for (auto &word : this->message->getWords()) {
if (!word.isText()) if (!word.isText()) {
continue; continue;
}
QFontMetrics &metrics = word.getFontMetrics(); word.updateSize();
word.setSize((int)(metrics.width(word.getText()) * this->dpiMultiplier),
(int)(metrics.height() * this->dpiMultiplier));
} }
} }
void MessageRef::updateImageSizes() void MessageRef::updateImageSizes()
{ {
const int mediumTextLineHeight =
FontManager::getInstance().getFontMetrics(FontManager::Medium).height();
const qreal emoteScale = SettingsManager::getInstance().emoteScale.get() * this->dpiMultiplier;
const bool scaleEmotesByLineHeight = SettingsManager::getInstance().scaleEmotesByLineHeight;
for (auto &word : this->message->getWords()) { for (auto &word : this->message->getWords()) {
if (!word.isImage()) if (!word.isImage())
continue; continue;
auto &image = word.getImage(); word.updateSize();
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);
}
} }
} }
@ -406,7 +390,7 @@ int MessageRef::getSelectionIndex(QPoint position)
} }
index++; index++;
x = part.getX() + part.getWord().getFontMetrics().width(text, j + 1); x = part.getX() + part.getWord().getFontMetrics(this->scale).width(text, j + 1);
} }
} }

View file

@ -22,7 +22,7 @@ public:
Message *getMessage(); Message *getMessage();
int getHeight() const; int getHeight() const;
bool layout(int width, float dpiMultiplier); bool layout(int width, float scale);
const std::vector<WordPart> &getWordParts() const; const std::vector<WordPart> &getWordParts() const;
@ -36,6 +36,7 @@ public:
bool isCollapsed() const; bool isCollapsed() const;
void setCollapsed(bool value); void setCollapsed(bool value);
int getCollapsedHeight() const; int getCollapsedHeight() const;
int getCollapsedLineCount() const;
private: private:
// variables // variables
@ -47,14 +48,15 @@ private:
int currentLayoutWidth = -1; int currentLayoutWidth = -1;
int fontGeneration = -1; int fontGeneration = -1;
int emoteGeneration = -1; int emoteGeneration = -1;
float dpiMultiplier = -1; float scale = -1;
Word::Type currentWordTypes = Word::None; Word::Flags currentWordTypes = Word::None;
bool collapsed; bool collapsed;
int collapsedHeight = 32; int collapsedHeight = 32;
// methods // methods
void rebuild();
void actuallyLayout(int width); void actuallyLayout(int width);
void alignWordParts(int lineStart, int lineHeight, int width, int &firstLineHeight); void alignWordParts(int lineStart, int lineHeight, int width, int &firstLineHeight);
void updateTextSizes(); void updateTextSizes();

View file

@ -1,11 +1,12 @@
#include "messages/word.hpp" #include "messages/word.hpp"
#include "settingsmanager.hpp"
#include "util/benchmark.hpp" #include "util/benchmark.hpp"
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
// Image word // Image word
Word::Word(LazyLoadedImage *image, Type type, const QString &copytext, const QString &tooltip, Word::Word(LazyLoadedImage *image, Flags type, const QString &copytext, const QString &tooltip,
const Link &link) const Link &link)
: image(image) : image(image)
, _isImage(true) , _isImage(true)
@ -14,15 +15,15 @@ Word::Word(LazyLoadedImage *image, Type type, const QString &copytext, const QSt
, tooltip(tooltip) , tooltip(tooltip)
, link(link) , link(link)
{ {
image->getWidth(); // professional segfault test
} }
// Text word // Text word
Word::Word(const QString &text, Type type, const MessageColor &color, const QString &copytext, Word::Word(const QString &text, Flags type, const MessageColor &color, FontManager::Type font,
const QString &tooltip, const Link &link) const QString &copytext, const QString &tooltip, const Link &link)
: image(nullptr) : image(nullptr)
, text(text) , text(text)
, color(color) , color(color)
, font(font)
, _isImage(false) , _isImage(false)
, type(type) , type(type)
, copyText(copytext) , copyText(copytext)
@ -41,20 +42,54 @@ const QString &Word::getText() const
return this->text; return this->text;
} }
int Word::getWidth() const int Word::getWidth(float scale) const
{ {
return this->width; return this->getSize(scale).width();
} }
int Word::getHeight() const int Word::getHeight(float scale) const
{ {
return this->height; return this->getSize(scale).height();
} }
void Word::setSize(int width, int height) QSize Word::getSize(float scale) const
{ {
this->width = width; auto &data = this->getDataByScale(scale);
this->height = height;
if (data.size.isEmpty()) {
// no size found
if (this->isText()) {
QFontMetrics &metrics = this->getFontMetrics(scale);
data.size.setWidth((int)(metrics.width(this->getText())));
data.size.setHeight((int)(metrics.height()));
} else {
const int mediumTextLineHeight =
FontManager::getInstance().getFontMetrics(this->font, scale).height();
const qreal emoteScale = SettingsManager::getInstance().emoteScale.get() * scale;
const bool scaleEmotesByLineHeight =
SettingsManager::getInstance().scaleEmotesByLineHeight;
auto &image = this->getImage();
qreal w = image.getWidth();
qreal h = image.getHeight();
if (scaleEmotesByLineHeight) {
data.size.setWidth(w * mediumTextLineHeight / h * emoteScale);
data.size.setHeight(mediumTextLineHeight * emoteScale);
} else {
data.size.setWidth(w * image.getScale() * emoteScale);
data.size.setHeight(h * image.getScale() * emoteScale);
}
}
}
return data.size;
}
void Word::updateSize()
{
this->dataByScale.clear();
} }
bool Word::isImage() const bool Word::isImage() const
@ -77,17 +112,17 @@ bool Word::hasTrailingSpace() const
return this->_hasTrailingSpace; return this->_hasTrailingSpace;
} }
QFont &Word::getFont() const QFont &Word::getFont(float scale) const
{ {
return FontManager::getInstance().getFont(this->font); return FontManager::getInstance().getFont(this->font, scale);
} }
QFontMetrics &Word::getFontMetrics() const QFontMetrics &Word::getFontMetrics(float scale) const
{ {
return FontManager::getInstance().getFontMetrics(this->font); return FontManager::getInstance().getFontMetrics(this->font, scale);
} }
Word::Type Word::getType() const Word::Flags Word::getFlags() const
{ {
return this->type; return this->type;
} }
@ -97,7 +132,7 @@ const QString &Word::getTooltip() const
return this->tooltip; return this->tooltip;
} }
const MessageColor &Word::getColor() const const MessageColor &Word::getTextColor() const
{ {
return this->color; return this->color;
} }
@ -128,24 +163,42 @@ int Word::getCharacterLength() const
return this->isImage() ? 2 : this->getText().length() + 1; return this->isImage() ? 2 : this->getText().length() + 1;
} }
short Word::getCharWidth(int index) const short Word::getCharWidth(int index, float scale) const
{ {
return this->getCharacterWidthCache().at(index); return this->getCharacterWidthCache(scale).at(index);
} }
std::vector<short> &Word::getCharacterWidthCache() const std::vector<short> &Word::getCharacterWidthCache(float scale) const
{ {
auto &data = this->getDataByScale(scale);
// lock not required because there is only one gui thread // lock not required because there is only one gui thread
// std::lock_guard<std::mutex> lock(this->charWidthCacheMutex); // std::lock_guard<std::mutex> lock(this->charWidthCacheMutex);
if (this->charWidthCache.size() == 0 && this->isText()) { if (data.charWidthCache.size() == 0 && this->isText()) {
for (int i = 0; i < this->getText().length(); i++) { for (int i = 0; i < this->getText().length(); i++) {
this->charWidthCache.push_back(this->getFontMetrics().charWidth(this->getText(), i)); data.charWidthCache.push_back(
this->getFontMetrics(scale).charWidth(this->getText(), i));
} }
} }
// TODO: on font change // TODO: on font change
return this->charWidthCache; return data.charWidthCache;
}
Word::ScaleDependantData &Word::getDataByScale(float scale) const
{
// try to find and return data for scale
for (auto it = this->dataByScale.begin(); it != this->dataByScale.end(); it++) {
if (it->scale == scale) {
return *it;
}
}
// create new data element and return that
this->dataByScale.emplace_back(scale);
return this->dataByScale.back();
} }
} // namespace messages } // namespace messages

View file

@ -4,6 +4,7 @@
#include "messages/lazyloadedimage.hpp" #include "messages/lazyloadedimage.hpp"
#include "messages/link.hpp" #include "messages/link.hpp"
#include "messages/messagecolor.hpp" #include "messages/messagecolor.hpp"
//#include "wordflags.hpp"
#include <stdint.h> #include <stdint.h>
#include <QRect> #include <QRect>
@ -15,7 +16,7 @@ namespace messages {
class Word class Word
{ {
public: public:
enum Type : uint32_t { enum Flags : uint32_t {
None = 0, None = 0,
Misc = (1 << 0), Misc = (1 << 0),
Text = (1 << 1), Text = (1 << 1),
@ -92,33 +93,36 @@ public:
{ {
} }
explicit Word(LazyLoadedImage *_image, Type getType, const QString &copytext, explicit Word(LazyLoadedImage *_image, Flags getFlags, const QString &copytext,
const QString &tooltip, const Link &getLink = Link()); const QString &tooltip, const Link &getLink = Link());
explicit Word(const QString &_text, Type getType, const MessageColor &getColor, explicit Word(const QString &_text, Flags getFlags, const MessageColor &textColor,
const QString &copytext, const QString &tooltip, const Link &getLink = Link()); FontManager::Type font, const QString &copytext, const QString &tooltip,
const Link &getLink = Link());
LazyLoadedImage &getImage() const;
const QString &getText() const;
int getWidth() const;
int getHeight() const;
void setSize(int _width, int _height);
bool isImage() const; bool isImage() const;
bool isText() const; bool isText() const;
LazyLoadedImage &getImage() const;
const QString &getText() const;
const QString &getCopyText() const; const QString &getCopyText() const;
bool hasTrailingSpace() const; bool hasTrailingSpace() const;
QFont &getFont() const; Flags getFlags() const;
QFontMetrics &getFontMetrics() const;
Type getType() const;
const QString &getTooltip() const; const QString &getTooltip() const;
const MessageColor &getColor() const; const MessageColor &getTextColor() const;
const Link &getLink() const; const Link &getLink() const;
int getXOffset() const; int getXOffset() const;
int getYOffset() const; int getYOffset() const;
void setOffset(int _xOffset, int _yOffset); void setOffset(int _xOffset, int _yOffset);
int getCharacterLength() const; int getCharacterLength() const;
short getCharWidth(int index) const; void updateSize();
QFont &getFont(float scale) const;
QFontMetrics &getFontMetrics(float scale) const;
int getWidth(float scale) const;
int getHeight(float scale) const;
QSize getSize(float scale) const;
short getCharWidth(int index, float scale) const;
private: private:
LazyLoadedImage *image; LazyLoadedImage *image;
@ -126,12 +130,10 @@ private:
MessageColor color; MessageColor color;
bool _isImage; bool _isImage;
Type type; Flags type;
QString copyText; QString copyText;
QString tooltip; QString tooltip;
int width = 16;
int height = 16;
int xOffset = 0; int xOffset = 0;
int yOffset = 0; int yOffset = 0;
@ -139,8 +141,22 @@ private:
FontManager::Type font = FontManager::Medium; FontManager::Type font = FontManager::Medium;
Link link; Link link;
std::vector<short> &getCharacterWidthCache() const; struct ScaleDependantData {
mutable std::vector<short> charWidthCache; float scale;
QSize size;
mutable std::vector<short> charWidthCache;
ScaleDependantData(float _scale)
: scale(_scale)
, size()
{
}
};
mutable std::list<ScaleDependantData> dataByScale;
inline ScaleDependantData &getDataByScale(float scale) const;
std::vector<short> &getCharacterWidthCache(float scale) const;
}; };
} // namespace messages } // namespace messages

View file

@ -0,0 +1 @@
#pragma once

View file

@ -4,15 +4,15 @@
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
WordPart::WordPart(Word &_word, int _x, int _y, int _lineNumber, const QString &_copyText, WordPart::WordPart(Word &_word, int _x, int _y, float scale, int _lineNumber,
bool _allowTrailingSpace) const QString &_copyText, bool _allowTrailingSpace)
: word(_word) : word(_word)
, copyText(_copyText) , copyText(_copyText)
, text(_word.isText() ? _word.getText() : QString()) , text(_word.isText() ? _word.getText() : QString())
, x(_x) , x(_x)
, y(_y) , y(_y)
, width(_word.getWidth()) , width(_word.getWidth(scale))
, height(_word.getHeight()) , height(_word.getHeight(scale))
, lineNumber(_lineNumber) , lineNumber(_lineNumber)
, _trailingSpace(!_word.getCopyText().isEmpty() && , _trailingSpace(!_word.getCopyText().isEmpty() &&
_word.hasTrailingSpace() & _allowTrailingSpace) _word.hasTrailingSpace() & _allowTrailingSpace)
@ -115,9 +115,9 @@ int WordPart::getCharacterLength() const
return this->getWord().isImage() ? 2 : this->getText().length() + 1; return this->getWord().isImage() ? 2 : this->getText().length() + 1;
} }
short WordPart::getCharWidth(int index) const short WordPart::getCharWidth(int index, float scale) const
{ {
return this->getWord().getCharWidth(index + this->wordCharOffset); return this->getWord().getCharWidth(index + this->wordCharOffset, scale);
} }
} // namespace messages } // namespace messages
} // namespace chatterino } // namespace chatterino

View file

@ -11,8 +11,8 @@ class Word;
class WordPart class WordPart
{ {
public: public:
WordPart(Word &getWord, int getX, int getY, int _lineNumber, const QString &getCopyText, WordPart(Word &getWord, int getX, int getY, float scale, int _lineNumber,
bool allowTrailingSpace = true); const QString &getCopyText, bool allowTrailingSpace = true);
WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight, int _lineNumber, WordPart(Word &getWord, int getX, int getY, int getWidth, int getHeight, int _lineNumber,
const QString &getCopyText, const QString &customText, bool allowTrailingSpace = true, const QString &getCopyText, const QString &customText, bool allowTrailingSpace = true,
@ -33,7 +33,7 @@ public:
const QString &getText() const; const QString &getText() const;
int getLineNumber() const; int getLineNumber() const;
int getCharacterLength() const; int getCharacterLength() const;
short getCharWidth(int index) const; short getCharWidth(int index, float scale) const;
private: private:
Word &word; Word &word;

View file

@ -0,0 +1,51 @@
#include <QAbstractNativeEventFilter>
#include <QApplication>
#include <QButtonGroup>
#include <QCheckBox>
#include <QClipboard>
#include <QColor>
#include <QCompleter>
#include <QDebug>
#include <QDialogButtonBox>
#include <QDockWidget>
#include <QDrag>
#include <QElapsedTimer>
#include <QFileInfo>
#include <QFlags>
#include <QFont>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QJsonDocument>
#include <QLabel>
#include <QLineEdit>
#include <QListView>
#include <QMenu>
#include <QMutex>
#include <QNetworkAccessManager>
#include <QPainter>
#include <QPoint>
#include <QProcess>
#include <QPropertyAnimation>
#include <QPushButton>
#include <QShortcut>
#include <QStackedLayout>
#include <QStandardPaths>
#include <QString>
#include <QTimer>
#include <QUrl>
#include <QUuid>
#include <QVBoxLayout>
#include <QVariant>
#include <algorithm>
#include <boost/signals2.hpp>
#include <chrono>
#include <ctime>
#include <future>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <tuple>
#include <vector>

View file

@ -75,7 +75,7 @@ void SettingsManager::load()
} }
} }
Word::Type SettingsManager::getWordTypeMask() Word::Flags SettingsManager::getWordTypeMask()
{ {
return this->wordTypeMask; return this->wordTypeMask;
} }
@ -120,7 +120,7 @@ void SettingsManager::updateWordTypeMask()
newMaskUint |= Word::AlwaysShow; newMaskUint |= Word::AlwaysShow;
Word::Type newMask = static_cast<Word::Type>(newMaskUint); Word::Flags newMask = static_cast<Word::Flags>(newMaskUint);
if (newMask != this->wordTypeMask) { if (newMask != this->wordTypeMask) {
this->wordTypeMask = newMask; this->wordTypeMask = newMask;

View file

@ -21,7 +21,7 @@ public:
void load(); void load();
void save(); void save();
messages::Word::Type getWordTypeMask(); messages::Word::Flags getWordTypeMask();
bool isIgnoredEmote(const QString &emote); bool isIgnoredEmote(const QString &emote);
QSettings &getQSettings(); QSettings &getQSettings();
SettingsSnapshot createSnapshot(); SettingsSnapshot createSnapshot();
@ -95,7 +95,7 @@ private:
QSettings settings; QSettings settings;
std::vector<std::reference_wrapper<BaseSetting>> settingsItems; std::vector<std::reference_wrapper<BaseSetting>> settingsItems;
messages::Word::Type wordTypeMask = messages::Word::Default; messages::Word::Flags wordTypeMask = messages::Word::Default;
pajlada::Settings::SettingListener wordMaskListener; pajlada::Settings::SettingListener wordMaskListener;
}; };

View file

@ -120,7 +120,7 @@ SharedMessage TwitchMessageBuilder::parse()
currentTwitchEmote->second.image->getName() + QString("\nTwitch Emote"))); currentTwitchEmote->second.image->getName() + QString("\nTwitch Emote")));
this->appendWord( this->appendWord(
Word(currentTwitchEmote->second.image->getName(), Word::TwitchEmoteText, textColor, Word(currentTwitchEmote->second.image->getName(), Word::TwitchEmoteText, textColor,
currentTwitchEmote->second.image->getName(), FontManager::Medium, currentTwitchEmote->second.image->getName(),
currentTwitchEmote->second.image->getName() + QString("\nTwitch Emote"))); currentTwitchEmote->second.image->getName() + QString("\nTwitch Emote")));
i += split.length() + 1; i += split.length() + 1;
@ -193,7 +193,7 @@ SharedMessage TwitchMessageBuilder::parse()
this->appendWord(Word( this->appendWord(Word(
QString("x" + string.mid(5)), Word::BitsAmount, MessageColor(bitsColor), QString("x" + string.mid(5)), Word::BitsAmount, MessageColor(bitsColor),
QString(string.mid(5)), QString("Twitch Cheer"), FontManager::Medium, QString(string.mid(5)), QString("Twitch Cheer"),
Link(Link::Url, Link(Link::Url,
QString("https://blog.twitch.tv/" QString("https://blog.twitch.tv/"
"introducing-cheering-celebrate-together-da62af41fac6")))); "introducing-cheering-celebrate-together-da62af41fac6"))));
@ -223,11 +223,12 @@ SharedMessage TwitchMessageBuilder::parse()
textColor = MessageColor(MessageColor::Link); textColor = MessageColor(MessageColor::Link);
} }
this->appendWord(Word(string, Word::Text, textColor, string, QString(), link)); this->appendWord(Word(string, Word::Text, textColor, FontManager::Medium, string,
QString(), link));
} else { // is emoji } else { // is emoji
this->appendWord(Word(emoteData.image, Word::EmojiImage, emoteData.image->getName(), this->appendWord(Word(emoteData.image, Word::EmojiImage, emoteData.image->getName(),
emoteData.image->getTooltip())); emoteData.image->getTooltip()));
Word(emoteData.image->getName(), Word::EmojiText, textColor, Word(emoteData.image->getName(), Word::EmojiText, textColor, FontManager::Medium,
emoteData.image->getName(), emoteData.image->getTooltip()); emoteData.image->getName(), emoteData.image->getTooltip());
} }
} }
@ -272,7 +273,7 @@ void TwitchMessageBuilder::parseChannelName()
{ {
QString channelName("#" + this->channel->name); QString channelName("#" + this->channel->name);
this->appendWord(Word(channelName, Word::Misc, MessageColor(MessageColor::System), this->appendWord(Word(channelName, Word::Misc, MessageColor(MessageColor::System),
QString(channelName), QString(), FontManager::Medium, QString(channelName), QString(),
Link(Link::Url, this->channel->name + "\n" + this->messageID))); Link(Link::Url, this->channel->name + "\n" + this->messageID)));
} }
@ -360,7 +361,8 @@ void TwitchMessageBuilder::appendUsername()
} }
this->appendWord(Word(usernameString, Word::Username, MessageColor(this->usernameColor), this->appendWord(Word(usernameString, Word::Username, MessageColor(this->usernameColor),
usernameString, QString(), Link(Link::UserInfo, this->userName))); FontManager::Medium, usernameString, QString(),
Link(Link::UserInfo, this->userName)));
} }
void TwitchMessageBuilder::parseHighlights() void TwitchMessageBuilder::parseHighlights()
@ -636,7 +638,7 @@ void TwitchMessageBuilder::parseTwitchBadges()
auto badgeSetIt = channelResources.badgeSets.find("subscriber"); auto badgeSetIt = channelResources.badgeSets.find("subscriber");
if (badgeSetIt == channelResources.badgeSets.end()) { if (badgeSetIt == channelResources.badgeSets.end()) {
// Fall back to default badge // Fall back to default badge
appendWord(Word(this->resources.badgeSubscriber, Word::Type::BadgeSubscription, appendWord(Word(this->resources.badgeSubscriber, Word::Flags::BadgeSubscription,
QString(), QString("Twitch Subscriber"))); QString(), QString("Twitch Subscriber")));
continue; continue;
} }
@ -649,14 +651,14 @@ void TwitchMessageBuilder::parseTwitchBadges()
if (badgeVersionIt == badgeSet.versions.end()) { if (badgeVersionIt == badgeSet.versions.end()) {
// Fall back to default badge // Fall back to default badge
appendWord(Word(this->resources.badgeSubscriber, Word::Type::BadgeSubscription, appendWord(Word(this->resources.badgeSubscriber, Word::Flags::BadgeSubscription,
QString(), QString("Twitch Subscriber"))); QString(), QString("Twitch Subscriber")));
continue; continue;
} }
auto &badgeVersion = badgeVersionIt->second; auto &badgeVersion = badgeVersionIt->second;
appendWord(Word(badgeVersion.badgeImage1x, Word::Type::BadgeSubscription, QString(), appendWord(Word(badgeVersion.badgeImage1x, Word::Flags::BadgeSubscription, QString(),
QString("Twitch " + QString::fromStdString(badgeVersion.title)))); QString("Twitch " + QString::fromStdString(badgeVersion.title))));
} else { } else {
if (!this->resources.dynamicBadgesLoaded) { if (!this->resources.dynamicBadgesLoaded) {
@ -671,7 +673,7 @@ void TwitchMessageBuilder::parseTwitchBadges()
continue; continue;
} }
Word::Type badgeType = Word::Type::BadgeVanity; Word::Flags badgeType = Word::Flags::BadgeVanity;
std::string badgeSetKey = parts[0].toStdString(); std::string badgeSetKey = parts[0].toStdString();
std::string versionKey = parts[1].toStdString(); std::string versionKey = parts[1].toStdString();

View file

@ -45,8 +45,8 @@ void EmotePopup::loadChannel(std::shared_ptr<Channel> _channel)
// TITLE // TITLE
messages::MessageBuilder builder1; messages::MessageBuilder builder1;
builder1.appendWord( builder1.appendWord(Word(title, Word::Flags::Text, MessageColor(MessageColor::Text),
Word(title, Word::Type::Text, MessageColor(MessageColor::Text), QString(), QString())); FontManager::Medium, QString(), QString()));
builder1.getMessage()->centered = true; builder1.getMessage()->centered = true;
emoteChannel->addMessage(builder1.getMessage()); emoteChannel->addMessage(builder1.getMessage());
@ -56,7 +56,7 @@ void EmotePopup::loadChannel(std::shared_ptr<Channel> _channel)
builder2.getMessage()->centered = true; builder2.getMessage()->centered = true;
map.each([&](const QString &key, const EmoteData &value) { map.each([&](const QString &key, const EmoteData &value) {
builder2.appendWord(Word(value.image, Word::Type::AlwaysShow, key, emoteDesc, builder2.appendWord(Word(value.image, Word::Flags::AlwaysShow, key, emoteDesc,
Link(Link::Type::InsertText, key))); Link(Link::Type::InsertText, key)));
}); });
@ -85,8 +85,8 @@ void EmotePopup::loadEmojis()
// title // title
messages::MessageBuilder builder1; messages::MessageBuilder builder1;
builder1.appendWord( builder1.appendWord(Word("emojis", Word::Flags::Text, MessageColor(MessageColor::Text),
Word("emojis", Word::Type::Text, MessageColor(MessageColor::Text), QString(), QString())); FontManager::Medium, QString(), QString()));
builder1.getMessage()->centered = true; builder1.getMessage()->centered = true;
emojiChannel->addMessage(builder1.getMessage()); emojiChannel->addMessage(builder1.getMessage());
@ -95,7 +95,7 @@ void EmotePopup::loadEmojis()
messages::MessageBuilder builder; messages::MessageBuilder builder;
builder.getMessage()->centered = true; builder.getMessage()->centered = true;
emojis.each([this, &builder](const QString &key, const EmoteData &value) { emojis.each([this, &builder](const QString &key, const EmoteData &value) {
builder.appendWord(Word(value.image, Word::Type::AlwaysShow, key, "emoji", builder.appendWord(Word(value.image, Word::Flags::AlwaysShow, key, "emoji",
Link(Link::Type::InsertText, key))); Link(Link::Type::InsertText, key)));
}); });
emojiChannel->addMessage(builder.getMessage()); emojiChannel->addMessage(builder.getMessage());

View file

@ -554,12 +554,14 @@ void ChannelView::updateMessageBuffer(messages::MessageRef *messageRef, QPixmap
} }
// text // text
else { else {
QColor color = wordPart.getWord().getColor().getColor(this->colorScheme); QColor color = wordPart.getWord().getTextColor().getColor(this->colorScheme);
this->colorScheme.normalizeColor(color); this->colorScheme.normalizeColor(color);
painter.setPen(color); painter.setPen(color);
painter.setFont(wordPart.getWord().getFont()); painter.setFont(wordPart.getWord().getFont(this->getDpiMultiplier()));
qDebug() << wordPart.getWord().getFont(this->getDpiMultiplier()).pointSize();
painter.drawText(QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000), painter.drawText(QRectF(wordPart.getX(), wordPart.getY(), 10000, 10000),
wordPart.getText(), QTextOption(Qt::AlignLeft | Qt::AlignTop)); wordPart.getText(), QTextOption(Qt::AlignLeft | Qt::AlignTop));
@ -626,7 +628,7 @@ void ChannelView::drawMessageSelection(QPainter &painter, messages::MessageRef *
int offset = this->selection.min.charIndex - charIndex; int offset = this->selection.min.charIndex - charIndex;
for (int j = 0; j < offset; j++) { for (int j = 0; j < offset; j++) {
rect.setLeft(rect.left() + part.getCharWidth(j)); rect.setLeft(rect.left() + part.getCharWidth(j, this->getDpiMultiplier()));
} }
if (isSingleWord) { if (isSingleWord) {
@ -635,7 +637,7 @@ void ChannelView::drawMessageSelection(QPainter &painter, messages::MessageRef *
rect.setRight(part.getX()); rect.setRight(part.getX());
for (int j = 0; j < offset + length; j++) { for (int j = 0; j < offset + length; j++) {
rect.setRight(rect.right() + part.getCharWidth(j)); rect.setRight(rect.right() + part.getCharWidth(j, this->getDpiMultiplier()));
} }
painter.fillRect(rect, selectionColor); painter.fillRect(rect, selectionColor);
@ -692,7 +694,7 @@ void ChannelView::drawMessageSelection(QPainter &painter, messages::MessageRef *
rect.setRight(part.getX()); rect.setRight(part.getX());
for (int j = 0; j < offset + length; j++) { for (int j = 0; j < offset + length; j++) {
rect.setRight(rect.right() + part.getCharWidth(j)); rect.setRight(rect.right() + part.getCharWidth(j, this->getDpiMultiplier()));
} }
} else { } else {
if (this->selection.max.charIndex == charIndex) { if (this->selection.max.charIndex == charIndex) {

View file

@ -29,9 +29,11 @@ SplitInput::SplitInput(Split *_chatWidget)
auto &fontManager = FontManager::getInstance(); auto &fontManager = FontManager::getInstance();
this->textInput.setFont(fontManager.getFont(FontManager::Type::Medium)); this->textInput.setFont(
fontManager.getFont(FontManager::Type::Medium, this->getDpiMultiplier()));
this->managedConnections.emplace_back(fontManager.fontChanged.connect([this, &fontManager]() { this->managedConnections.emplace_back(fontManager.fontChanged.connect([this, &fontManager]() {
this->textInput.setFont(fontManager.getFont(FontManager::Type::Medium)); this->textInput.setFont(
fontManager.getFont(FontManager::Type::Medium, this->getDpiMultiplier()));
})); }));
this->editContainer.addWidget(&this->textInput); this->editContainer.addWidget(&this->textInput);

View file

@ -180,7 +180,7 @@ QVBoxLayout *SettingsDialog::createAppearanceTab()
fontButton->connect(fontButton, &QPushButton::clicked, []() { fontButton->connect(fontButton, &QPushButton::clicked, []() {
auto &fontManager = FontManager::getInstance(); auto &fontManager = FontManager::getInstance();
QFontDialog dialog(fontManager.getFont(FontManager::Medium)); QFontDialog dialog(fontManager.getFont(FontManager::Medium, 1.));
dialog.connect(&dialog, &QFontDialog::fontSelected, [](const QFont &font) { dialog.connect(&dialog, &QFontDialog::fontSelected, [](const QFont &font) {
auto &fontManager = FontManager::getInstance(); auto &fontManager = FontManager::getInstance();