first attempt

This commit is contained in:
Blue 2022-04-17 14:58:46 +03:00
parent 8f949277f6
commit 51ac1ac709
Signed by untrusted user: blue
GPG Key ID: 9B203B252A63EE38
5 changed files with 311 additions and 5 deletions

View File

@ -15,4 +15,6 @@ target_sources(squawk PRIVATE
resizer.h
shadowoverlay.cpp
shadowoverlay.h
textmeter.cpp
textmeter.h
)

233
ui/utils/textmeter.cpp Normal file
View 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
View 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

View File

@ -42,6 +42,7 @@ MessageDelegate::MessageDelegate(QObject* parent):
nickFont(),
dateFont(),
bodyMetrics(bodyFont),
bodyMeter(),
nickMetrics(nickFont),
dateMetrics(dateFont),
buttonHeight(0),
@ -123,10 +124,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
opt.displayAlignment = Qt::AlignRight | Qt::AlignTop;
}
QSize bodySize(0, 0);
if (data.text.size() > 0) {
bodySize = bodyMetrics.boundingRect(opt.rect, Qt::TextWordWrap, data.text).size();
}
QSize bodySize = bodyMeter.boundingSize(data.text, opt.rect.size());
QRect rect;
if (ntds) {
@ -306,7 +304,7 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
QSize messageSize(0, 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;
}
@ -390,10 +388,13 @@ void MessageDelegate::initializeFonts(const QFont& font)
dateFont.setPointSize(dateFont.pointSize() * dateFontMultiplier);
}
bodyFont.setKerning(false);
bodyMetrics = QFontMetrics(bodyFont);
nickMetrics = QFontMetrics(nickFont);
dateMetrics = QFontMetrics(dateFont);
bodyMeter.initializeFonts(bodyFont);
Preview::initializeFont(bodyFont);
}

View File

@ -34,6 +34,7 @@
#include "shared/global.h"
#include "shared/utils.h"
#include "shared/pathcheck.h"
#include "ui/utils/textmeter.h"
#include "preview.h"
@ -94,6 +95,7 @@ private:
QFont nickFont;
QFont dateFont;
QFontMetrics bodyMetrics;
TextMeter bodyMeter;
QFontMetrics nickMetrics;
QFontMetrics dateMetrics;