forked from blue/squawk
first attempt
This commit is contained in:
parent
8f949277f6
commit
51ac1ac709
@ -15,4 +15,6 @@ target_sources(squawk PRIVATE
|
|||||||
resizer.h
|
resizer.h
|
||||||
shadowoverlay.cpp
|
shadowoverlay.cpp
|
||||||
shadowoverlay.h
|
shadowoverlay.h
|
||||||
|
textmeter.cpp
|
||||||
|
textmeter.h
|
||||||
)
|
)
|
||||||
|
233
ui/utils/textmeter.cpp
Normal file
233
ui/utils/textmeter.cpp
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "textmeter.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
TextMeter::TextMeter():
|
||||||
|
base(),
|
||||||
|
fonts()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMeter::~TextMeter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextMeter::initializeFonts(const QFont& font)
|
||||||
|
{
|
||||||
|
fonts.clear();
|
||||||
|
QList<QFontDatabase::WritingSystem> supported = base.writingSystems(font.family());
|
||||||
|
std::set<QFontDatabase::WritingSystem> sup;
|
||||||
|
std::set<QString> added({font.family()});
|
||||||
|
for (const QFontDatabase::WritingSystem& system : supported) {
|
||||||
|
sup.insert(system);
|
||||||
|
}
|
||||||
|
fonts.push_back(QFontMetrics(font));
|
||||||
|
QString style = base.styleString(font);
|
||||||
|
|
||||||
|
QList<QFontDatabase::WritingSystem> systems = base.writingSystems();
|
||||||
|
for (const QFontDatabase::WritingSystem& system : systems) {
|
||||||
|
if (sup.count(system) == 0) {
|
||||||
|
QStringList families = base.families(system);
|
||||||
|
if (!families.empty() && added.count(families.first()) == 0) {
|
||||||
|
QString family(families.first());
|
||||||
|
QFont nfont = base.font(family, style, font.pointSize());
|
||||||
|
if (added.count(nfont.family()) == 0) {
|
||||||
|
added.insert(family);
|
||||||
|
fonts.push_back(QFontMetrics(nfont));
|
||||||
|
qDebug() << "Added font" << nfont.family() << "for" << system;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize TextMeter::boundingSize(const QString& text, const QSize& limits) const
|
||||||
|
{
|
||||||
|
// QString str("ridiculus mus. Suspendisse potenti. Cras pretium venenatis enim, faucibus accumsan ex");
|
||||||
|
// bool first = true;
|
||||||
|
// int width = 0;
|
||||||
|
// QStringList list = str.split(" ");
|
||||||
|
// QFontMetrics m = fonts.front();
|
||||||
|
// for (const QString& word : list) {
|
||||||
|
// if (first) {
|
||||||
|
// first = false;
|
||||||
|
// } else {
|
||||||
|
// width += m.horizontalAdvance(QChar::Space);
|
||||||
|
// }
|
||||||
|
// width += m.horizontalAdvance(word);
|
||||||
|
// for (const QChar& ch : word) {
|
||||||
|
// width += m.horizontalAdvance(ch);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// qDebug() << "together:" << m.horizontalAdvance(str);
|
||||||
|
// qDebug() << "apart:" << width;
|
||||||
|
// I cant measure or wrap text this way, this simple example shows that even this gives differen result
|
||||||
|
// The Qt implementation under it is thousands and thousands lines of code in QTextEngine
|
||||||
|
// I simply can't get though it
|
||||||
|
|
||||||
|
if (text.size() == 0) {
|
||||||
|
return QSize (0, 0);
|
||||||
|
}
|
||||||
|
Helper current(limits.width());
|
||||||
|
for (const QChar& ch : text) {
|
||||||
|
if (newLine(ch)) {
|
||||||
|
current.computeNewWord();
|
||||||
|
if (current.height == 0) {
|
||||||
|
current.height = fonts.front().lineSpacing();
|
||||||
|
}
|
||||||
|
current.beginNewLine();
|
||||||
|
} else if (visible(ch)) {
|
||||||
|
bool found = false;
|
||||||
|
for (const QFontMetrics& metrics : fonts) {
|
||||||
|
if (metrics.inFont(ch)) {
|
||||||
|
current.computeChar(ch, metrics);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
current.computeChar(ch, fonts.front());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.computeNewWord();
|
||||||
|
current.beginNewLine();
|
||||||
|
|
||||||
|
int& height = current.size.rheight();
|
||||||
|
if (height > 0) {
|
||||||
|
height -= fonts.front().leading();
|
||||||
|
}
|
||||||
|
|
||||||
|
return current.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextMeter::Helper::computeChar(const QChar& ch, const QFontMetrics& metrics)
|
||||||
|
{
|
||||||
|
int ha = metrics.horizontalAdvance(ch);
|
||||||
|
if (newWord(ch)) {
|
||||||
|
if (printOnLineBreak(ch)) {
|
||||||
|
if (!lineOverflow(metrics, ha, ch)){
|
||||||
|
computeNewWord();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
computeNewWord();
|
||||||
|
delayedSpaceWidth = ha;
|
||||||
|
lastSpace = ch;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lineOverflow(metrics, ha, ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextMeter::Helper::computeNewLine(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch)
|
||||||
|
{
|
||||||
|
if (wordBeganWithTheLine) {
|
||||||
|
text = word.chopped(1);
|
||||||
|
width = wordWidth - horizontalAdvance;
|
||||||
|
height = wordHeight;
|
||||||
|
}
|
||||||
|
if (width != metrics.horizontalAdvance(text)) {
|
||||||
|
qDebug() << "Kerning Error" << width - metrics.horizontalAdvance(text);
|
||||||
|
}
|
||||||
|
beginNewLine();
|
||||||
|
if (wordBeganWithTheLine) {
|
||||||
|
word = ch;
|
||||||
|
wordWidth = horizontalAdvance;
|
||||||
|
wordHeight = metrics.lineSpacing();
|
||||||
|
}
|
||||||
|
|
||||||
|
wordBeganWithTheLine = true;
|
||||||
|
delayedSpaceWidth = 0;
|
||||||
|
lastSpace = QChar::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextMeter::Helper::beginNewLine()
|
||||||
|
{
|
||||||
|
size.rheight() += height;
|
||||||
|
size.rwidth() = qMax(size.width(), width);
|
||||||
|
qDebug() << text;
|
||||||
|
text = "";
|
||||||
|
width = 0;
|
||||||
|
height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextMeter::Helper::computeNewWord()
|
||||||
|
{
|
||||||
|
width += wordWidth + delayedSpaceWidth;
|
||||||
|
height = qMax(height, wordHeight);
|
||||||
|
if (lastSpace != QChar::Null) {
|
||||||
|
text += lastSpace;
|
||||||
|
}
|
||||||
|
text += word;
|
||||||
|
word = "";
|
||||||
|
wordWidth = 0;
|
||||||
|
wordHeight = 0;
|
||||||
|
delayedSpaceWidth = 0;
|
||||||
|
lastSpace = QChar::Null;
|
||||||
|
wordBeganWithTheLine = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextMeter::Helper::lineOverflow(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch)
|
||||||
|
{
|
||||||
|
wordHeight = qMax(wordHeight, metrics.lineSpacing());
|
||||||
|
wordWidth += horizontalAdvance;
|
||||||
|
word += ch;
|
||||||
|
if (width + delayedSpaceWidth + wordWidth > maxWidth) {
|
||||||
|
computeNewLine(metrics, horizontalAdvance, ch);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool TextMeter::newLine(const QChar& ch)
|
||||||
|
{
|
||||||
|
return ch == QChar::LineFeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextMeter::newWord(const QChar& ch)
|
||||||
|
{
|
||||||
|
return ch.isSpace() || ch.isPunct();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextMeter::printOnLineBreak(const QChar& ch)
|
||||||
|
{
|
||||||
|
return ch != QChar::Space;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextMeter::visible(const QChar& ch)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMeter::Helper::Helper(int p_maxWidth):
|
||||||
|
width(0),
|
||||||
|
height(0),
|
||||||
|
wordWidth(0),
|
||||||
|
wordHeight(0),
|
||||||
|
delayedSpaceWidth(0),
|
||||||
|
maxWidth(p_maxWidth),
|
||||||
|
wordBeganWithTheLine(true),
|
||||||
|
text(""),
|
||||||
|
word(""),
|
||||||
|
lastSpace(QChar::Null),
|
||||||
|
size(0, 0)
|
||||||
|
{
|
||||||
|
}
|
68
ui/utils/textmeter.h
Normal file
68
ui/utils/textmeter.h
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef TEXTMETER_H
|
||||||
|
#define TEXTMETER_H
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <QFontMetrics>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QFontDatabase>
|
||||||
|
|
||||||
|
class TextMeter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextMeter();
|
||||||
|
~TextMeter();
|
||||||
|
void initializeFonts(const QFont& font);
|
||||||
|
QSize boundingSize(const QString& text, const QSize& limits) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QFontDatabase base;
|
||||||
|
std::list<QFontMetrics> fonts;
|
||||||
|
|
||||||
|
struct Helper {
|
||||||
|
Helper(int maxWidth);
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int wordWidth;
|
||||||
|
int wordHeight;
|
||||||
|
int delayedSpaceWidth;
|
||||||
|
int maxWidth;
|
||||||
|
bool wordBeganWithTheLine;
|
||||||
|
QString text;
|
||||||
|
QString word;
|
||||||
|
QChar lastSpace;
|
||||||
|
QSize size;
|
||||||
|
|
||||||
|
void computeNewLine(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch);
|
||||||
|
void computeChar(const QChar& ch, const QFontMetrics& metrics);
|
||||||
|
void computeNewWord();
|
||||||
|
void beginNewLine();
|
||||||
|
bool lineOverflow(const QFontMetrics& metrics, int horizontalAdvance, const QChar& ch);
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool newLine(const QChar& ch);
|
||||||
|
static bool newWord(const QChar& ch);
|
||||||
|
static bool visible(const QChar& ch);
|
||||||
|
static bool printOnLineBreak(const QChar& ch);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEXTMETER_H
|
@ -42,6 +42,7 @@ MessageDelegate::MessageDelegate(QObject* parent):
|
|||||||
nickFont(),
|
nickFont(),
|
||||||
dateFont(),
|
dateFont(),
|
||||||
bodyMetrics(bodyFont),
|
bodyMetrics(bodyFont),
|
||||||
|
bodyMeter(),
|
||||||
nickMetrics(nickFont),
|
nickMetrics(nickFont),
|
||||||
dateMetrics(dateFont),
|
dateMetrics(dateFont),
|
||||||
buttonHeight(0),
|
buttonHeight(0),
|
||||||
@ -123,10 +124,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
|||||||
opt.displayAlignment = Qt::AlignRight | Qt::AlignTop;
|
opt.displayAlignment = Qt::AlignRight | Qt::AlignTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize bodySize(0, 0);
|
QSize bodySize = bodyMeter.boundingSize(data.text, opt.rect.size());
|
||||||
if (data.text.size() > 0) {
|
|
||||||
bodySize = bodyMetrics.boundingRect(opt.rect, Qt::TextWordWrap, data.text).size();
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect rect;
|
QRect rect;
|
||||||
if (ntds) {
|
if (ntds) {
|
||||||
@ -306,7 +304,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
|
|||||||
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
||||||
QSize messageSize(0, 0);
|
QSize messageSize(0, 0);
|
||||||
if (data.text.size() > 0) {
|
if (data.text.size() > 0) {
|
||||||
messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size();
|
messageSize = bodyMeter.boundingSize(data.text, messageRect.size());
|
||||||
messageSize.rheight() += textMargin;
|
messageSize.rheight() += textMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,10 +388,13 @@ void MessageDelegate::initializeFonts(const QFont& font)
|
|||||||
dateFont.setPointSize(dateFont.pointSize() * dateFontMultiplier);
|
dateFont.setPointSize(dateFont.pointSize() * dateFontMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bodyFont.setKerning(false);
|
||||||
bodyMetrics = QFontMetrics(bodyFont);
|
bodyMetrics = QFontMetrics(bodyFont);
|
||||||
nickMetrics = QFontMetrics(nickFont);
|
nickMetrics = QFontMetrics(nickFont);
|
||||||
dateMetrics = QFontMetrics(dateFont);
|
dateMetrics = QFontMetrics(dateFont);
|
||||||
|
|
||||||
|
bodyMeter.initializeFonts(bodyFont);
|
||||||
|
|
||||||
Preview::initializeFont(bodyFont);
|
Preview::initializeFont(bodyFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include "shared/global.h"
|
#include "shared/global.h"
|
||||||
#include "shared/utils.h"
|
#include "shared/utils.h"
|
||||||
#include "shared/pathcheck.h"
|
#include "shared/pathcheck.h"
|
||||||
|
#include "ui/utils/textmeter.h"
|
||||||
|
|
||||||
#include "preview.h"
|
#include "preview.h"
|
||||||
|
|
||||||
@ -94,6 +95,7 @@ private:
|
|||||||
QFont nickFont;
|
QFont nickFont;
|
||||||
QFont dateFont;
|
QFont dateFont;
|
||||||
QFontMetrics bodyMetrics;
|
QFontMetrics bodyMetrics;
|
||||||
|
TextMeter bodyMeter;
|
||||||
QFontMetrics nickMetrics;
|
QFontMetrics nickMetrics;
|
||||||
QFontMetrics dateMetrics;
|
QFontMetrics dateMetrics;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user