mirror of
https://github.com/vale981/tdesktop
synced 2025-03-10 04:56:43 -04:00

Commit 8d354382a4
introduced a regression in RTL phrases display.
When an RTL line had trailing spaces we started displaying them in
front of the text still assuming counted line width value that did
not include those trailing spaces.
Line width is not including trailing spaces width because it is
allowed to fit in the line any number of spaces.
Also text block "left padding" entity was eliminated. If we have
some spaces in the start of the text block (for example a text block
after a link) we just add an empty word and mark those spaces as its
right padding.
219 lines
5.1 KiB
C++
219 lines
5.1 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
|
|
|
Telegram Desktop 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.
|
|
|
|
It 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.
|
|
|
|
In addition, as a special exception, the copyright holders give permission
|
|
to link the code of portions of this program with the OpenSSL library.
|
|
|
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
|
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
|
*/
|
|
#pragma once
|
|
|
|
#include "private/qfontengine_p.h"
|
|
|
|
enum TextBlockType {
|
|
TextBlockTNewline = 0x01,
|
|
TextBlockTText = 0x02,
|
|
TextBlockTEmoji = 0x03,
|
|
TextBlockTSkip = 0x04,
|
|
};
|
|
|
|
enum TextBlockFlags {
|
|
TextBlockFBold = 0x01,
|
|
TextBlockFItalic = 0x02,
|
|
TextBlockFUnderline = 0x04,
|
|
TextBlockFTilde = 0x08, // tilde fix in OpenSans
|
|
TextBlockFSemibold = 0x10,
|
|
TextBlockFCode = 0x20,
|
|
TextBlockFPre = 0x40,
|
|
};
|
|
|
|
class ITextBlock {
|
|
public:
|
|
ITextBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, uint16 lnkIndex) : _from(from), _flags((flags & 0xFF) | ((lnkIndex & 0xFFFF) << 12)) {
|
|
}
|
|
|
|
uint16 from() const {
|
|
return _from;
|
|
}
|
|
int32 width() const {
|
|
return _width.toInt();
|
|
}
|
|
int32 rpadding() const {
|
|
return _rpadding.toInt();
|
|
}
|
|
QFixed f_width() const {
|
|
return _width;
|
|
}
|
|
QFixed f_rpadding() const {
|
|
return _rpadding;
|
|
}
|
|
|
|
// Should be virtual, but optimized throught type() call.
|
|
QFixed f_rbearing() const;
|
|
|
|
uint16 lnkIndex() const {
|
|
return (_flags >> 12) & 0xFFFF;
|
|
}
|
|
void setLnkIndex(uint16 lnkIndex) {
|
|
_flags = (_flags & ~(0xFFFF << 12)) | (lnkIndex << 12);
|
|
}
|
|
|
|
TextBlockType type() const {
|
|
return TextBlockType((_flags >> 8) & 0x0F);
|
|
}
|
|
int32 flags() const {
|
|
return (_flags & 0xFF);
|
|
}
|
|
|
|
virtual ITextBlock *clone() const = 0;
|
|
virtual ~ITextBlock() {
|
|
}
|
|
|
|
protected:
|
|
uint16 _from = 0;
|
|
|
|
uint32 _flags = 0; // 4 bits empty, 16 bits lnkIndex, 4 bits type, 8 bits flags
|
|
|
|
QFixed _width = 0;
|
|
|
|
// Right padding: spaces after the last content of the block (like a word).
|
|
// This holds spaces after the end of the block, for example a text ending
|
|
// with a space before a link has started. If text block has a leading spaces
|
|
// (for example a text block after a link block) it is prepended with an empty
|
|
// word that holds those spaces as a right padding.
|
|
QFixed _rpadding = 0;
|
|
|
|
};
|
|
|
|
class NewlineBlock : public ITextBlock {
|
|
public:
|
|
Qt::LayoutDirection nextDirection() const {
|
|
return _nextDir;
|
|
}
|
|
|
|
ITextBlock *clone() const {
|
|
return new NewlineBlock(*this);
|
|
}
|
|
|
|
private:
|
|
NewlineBlock(const style::font &font, const QString &str, uint16 from, uint16 length) : ITextBlock(font, str, from, length, 0, 0), _nextDir(Qt::LayoutDirectionAuto) {
|
|
_flags |= ((TextBlockTNewline & 0x0F) << 8);
|
|
}
|
|
|
|
Qt::LayoutDirection _nextDir;
|
|
|
|
friend class Text;
|
|
friend class TextParser;
|
|
|
|
friend class TextPainter;
|
|
|
|
};
|
|
|
|
class TextWord {
|
|
public:
|
|
TextWord() = default;
|
|
TextWord(uint16 from, QFixed width, QFixed rbearing, QFixed rpadding = 0)
|
|
: _from(from)
|
|
, _width(width)
|
|
, _rpadding(rpadding)
|
|
, _rbearing(rbearing.value() > 0x7FFF ? 0x7FFF : (rbearing.value() < -0x7FFF ? -0x7FFF : rbearing.value())) {
|
|
}
|
|
uint16 from() const {
|
|
return _from;
|
|
}
|
|
QFixed f_rbearing() const {
|
|
return QFixed::fromFixed(_rbearing);
|
|
}
|
|
QFixed f_width() const {
|
|
return _width;
|
|
}
|
|
QFixed f_rpadding() const {
|
|
return _rpadding;
|
|
}
|
|
void add_rpadding(QFixed padding) {
|
|
_rpadding += padding;
|
|
}
|
|
|
|
private:
|
|
uint16 _from = 0;
|
|
QFixed _width, _rpadding;
|
|
int16 _rbearing = 0;
|
|
|
|
};
|
|
|
|
class TextBlock : public ITextBlock {
|
|
public:
|
|
ITextBlock *clone() const {
|
|
return new TextBlock(*this);
|
|
}
|
|
|
|
private:
|
|
TextBlock(const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uchar flags, uint16 lnkIndex);
|
|
|
|
friend class ITextBlock;
|
|
QFixed real_f_rbearing() const {
|
|
return _words.isEmpty() ? 0 : _words.back().f_rbearing();
|
|
}
|
|
|
|
typedef QVector<TextWord> TextWords;
|
|
TextWords _words;
|
|
|
|
friend class Text;
|
|
friend class TextParser;
|
|
|
|
friend class BlockParser;
|
|
friend class TextPainter;
|
|
|
|
};
|
|
|
|
class EmojiBlock : public ITextBlock {
|
|
public:
|
|
ITextBlock *clone() const {
|
|
return new EmojiBlock(*this);
|
|
}
|
|
|
|
private:
|
|
EmojiBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, uint16 lnkIndex, EmojiPtr emoji);
|
|
|
|
EmojiPtr emoji = nullptr;
|
|
|
|
friend class Text;
|
|
friend class TextParser;
|
|
|
|
friend class TextPainter;
|
|
|
|
};
|
|
|
|
class SkipBlock : public ITextBlock {
|
|
public:
|
|
int32 height() const {
|
|
return _height;
|
|
}
|
|
|
|
ITextBlock *clone() const {
|
|
return new SkipBlock(*this);
|
|
}
|
|
|
|
private:
|
|
SkipBlock(const style::font &font, const QString &str, uint16 from, int32 w, int32 h, uint16 lnkIndex);
|
|
|
|
int32 _height;
|
|
|
|
friend class Text;
|
|
friend class TextParser;
|
|
|
|
friend class TextPainter;
|
|
|
|
};
|