293 lines
6.9 KiB
C++
293 lines
6.9 KiB
C++
// This file is part of Desktop App Toolkit,
|
|
// a set of libraries for developing nice desktop applications.
|
|
//
|
|
// For license and copyright information please follow this link:
|
|
// https://github.com/desktop-app/legal/blob/master/LEGAL
|
|
//
|
|
#include "ui/text/text_block.h"
|
|
|
|
#include "styles/style_basic.h"
|
|
|
|
#include <private/qfontengine_p.h>
|
|
|
|
namespace Ui {
|
|
namespace Text {
|
|
|
|
style::font WithFlags(
|
|
const style::font &font,
|
|
TextBlockFlags flags,
|
|
style::FontFlags fontFlags) {
|
|
using namespace style::internal;
|
|
|
|
using Flag = style::FontFlag;
|
|
if (!flags && !fontFlags) {
|
|
return font;
|
|
} else if (IsMono(flags) || (fontFlags & Flag::Monospace)) {
|
|
return font->monospace();
|
|
}
|
|
auto result = font;
|
|
if ((flags & TextBlockFlag::Bold)
|
|
|| (flags & TextBlockFlag::Semibold)
|
|
|| (fontFlags & Flag::Bold)) {
|
|
result = result->bold();
|
|
}
|
|
if ((flags & TextBlockFlag::Italic) || (fontFlags & Flag::Italic)) {
|
|
result = result->italic();
|
|
}
|
|
if ((flags & TextBlockFlag::Underline)
|
|
|| (fontFlags & Flag::Underline)) {
|
|
result = result->underline();
|
|
}
|
|
if ((flags & TextBlockFlag::StrikeOut)
|
|
|| (fontFlags & Flag::StrikeOut)) {
|
|
result = result->strikeout();
|
|
}
|
|
if ((flags & TextBlockFlag::Subscript)
|
|
|| (flags & TextBlockFlag::Superscript)
|
|
|| (fontFlags & Flag::SubOrSuper)) {
|
|
result = result->suborsuper();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Qt::LayoutDirection UnpackParagraphDirection(bool ltr, bool rtl) {
|
|
return ltr
|
|
? Qt::LeftToRight
|
|
: rtl
|
|
? Qt::RightToLeft
|
|
: Qt::LayoutDirectionAuto;
|
|
}
|
|
|
|
AbstractBlock::AbstractBlock(TextBlockType type, BlockDescriptor descriptor)
|
|
: _position(descriptor.position)
|
|
, _type(static_cast<uint16>(type))
|
|
, _colorIndex(descriptor.colorIndex)
|
|
, _bgIndex(descriptor.bgIndex)
|
|
, _flags(descriptor.flags)
|
|
, _linkIndex(descriptor.linkIndex) {
|
|
}
|
|
|
|
uint16 AbstractBlock::position() const {
|
|
return _position;
|
|
}
|
|
|
|
TextBlockType AbstractBlock::type() const {
|
|
return static_cast<TextBlockType>(_type);
|
|
}
|
|
|
|
TextBlockFlags AbstractBlock::flags() const {
|
|
return TextBlockFlags::from_raw(_flags);
|
|
}
|
|
|
|
int AbstractBlock::objectWidth() const {
|
|
switch (type()) {
|
|
case TextBlockType::Emoji: return st::emojiSize + 2 * st::emojiPadding;
|
|
case TextBlockType::CustomEmoji:
|
|
return static_cast<const CustomEmojiBlock*>(this)->custom()->width();
|
|
case TextBlockType::Skip:
|
|
return static_cast<const SkipBlock*>(this)->width();
|
|
}
|
|
Unexpected("Type in AbstractBlock::objectWidth.");
|
|
}
|
|
|
|
uint16 AbstractBlock::linkIndex() const {
|
|
return _linkIndex;
|
|
}
|
|
|
|
uint16 AbstractBlock::colorIndex() const {
|
|
return _colorIndex;
|
|
}
|
|
|
|
uint16 AbstractBlock::bgIndex() const {
|
|
return _bgIndex;
|
|
}
|
|
|
|
void AbstractBlock::setLinkIndex(uint16 index) {
|
|
_linkIndex = index;
|
|
}
|
|
|
|
TextBlock::TextBlock(BlockDescriptor descriptor)
|
|
: AbstractBlock(TextBlockType::Text, descriptor) {
|
|
}
|
|
|
|
EmojiBlock::EmojiBlock(BlockDescriptor descriptor, EmojiPtr emoji)
|
|
: AbstractBlock(TextBlockType::Emoji, descriptor)
|
|
, _emoji(emoji) {
|
|
}
|
|
|
|
CustomEmojiBlock::CustomEmojiBlock(
|
|
BlockDescriptor descriptor,
|
|
std::unique_ptr<CustomEmoji> custom)
|
|
: AbstractBlock(TextBlockType::CustomEmoji, descriptor)
|
|
, _custom(std::move(custom)) {
|
|
}
|
|
|
|
NewlineBlock::NewlineBlock(BlockDescriptor descriptor, uint16 quoteIndex)
|
|
: AbstractBlock(TextBlockType::Newline, descriptor)
|
|
, _quoteIndex(quoteIndex) {
|
|
}
|
|
|
|
SkipBlock::SkipBlock(BlockDescriptor descriptor, int width, int height)
|
|
: AbstractBlock(TextBlockType::Skip, descriptor)
|
|
, _width(width)
|
|
, _height(height) {
|
|
}
|
|
|
|
int SkipBlock::width() const {
|
|
return _width;
|
|
}
|
|
|
|
int SkipBlock::height() const {
|
|
return _height;
|
|
}
|
|
|
|
Block::Block() {
|
|
Unexpected("Should not be called.");
|
|
}
|
|
|
|
Block::Block(Block &&other) {
|
|
switch (other->type()) {
|
|
case TextBlockType::Newline:
|
|
emplace<NewlineBlock>(std::move(other.unsafe<NewlineBlock>()));
|
|
break;
|
|
case TextBlockType::Text:
|
|
emplace<TextBlock>(std::move(other.unsafe<TextBlock>()));
|
|
break;
|
|
case TextBlockType::Emoji:
|
|
emplace<EmojiBlock>(std::move(other.unsafe<EmojiBlock>()));
|
|
break;
|
|
case TextBlockType::CustomEmoji:
|
|
emplace<CustomEmojiBlock>(
|
|
std::move(other.unsafe<CustomEmojiBlock>()));
|
|
break;
|
|
case TextBlockType::Skip:
|
|
emplace<SkipBlock>(std::move(other.unsafe<SkipBlock>()));
|
|
break;
|
|
default:
|
|
Unexpected("Bad text block type in Block(Block&&).");
|
|
}
|
|
}
|
|
|
|
Block &Block::operator=(Block &&other) {
|
|
if (&other == this) {
|
|
return *this;
|
|
}
|
|
destroy();
|
|
switch (other->type()) {
|
|
case TextBlockType::Newline:
|
|
emplace<NewlineBlock>(std::move(other.unsafe<NewlineBlock>()));
|
|
break;
|
|
case TextBlockType::Text:
|
|
emplace<TextBlock>(std::move(other.unsafe<TextBlock>()));
|
|
break;
|
|
case TextBlockType::Emoji:
|
|
emplace<EmojiBlock>(std::move(other.unsafe<EmojiBlock>()));
|
|
break;
|
|
case TextBlockType::CustomEmoji:
|
|
emplace<CustomEmojiBlock>(
|
|
std::move(other.unsafe<CustomEmojiBlock>()));
|
|
break;
|
|
case TextBlockType::Skip:
|
|
emplace<SkipBlock>(std::move(other.unsafe<SkipBlock>()));
|
|
break;
|
|
default:
|
|
Unexpected("Bad text block type in operator=(Block&&).");
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Block::~Block() {
|
|
destroy();
|
|
}
|
|
|
|
Block Block::Newline(BlockDescriptor descriptor, uint16 quoteIndex) {
|
|
return New<NewlineBlock>(descriptor, quoteIndex);
|
|
}
|
|
|
|
Block Block::Text(BlockDescriptor descriptor) {
|
|
return New<TextBlock>(descriptor);
|
|
}
|
|
|
|
Block Block::Emoji(BlockDescriptor descriptor, EmojiPtr emoji) {
|
|
return New<EmojiBlock>(descriptor, emoji);
|
|
}
|
|
|
|
Block Block::CustomEmoji(
|
|
BlockDescriptor descriptor,
|
|
std::unique_ptr<Text::CustomEmoji> custom) {
|
|
return New<CustomEmojiBlock>(descriptor, std::move(custom));
|
|
}
|
|
|
|
Block Block::Skip(BlockDescriptor descriptor, int width, int height) {
|
|
return New<SkipBlock>(descriptor, width, height);
|
|
}
|
|
|
|
AbstractBlock *Block::get() {
|
|
return &unsafe<AbstractBlock>();
|
|
}
|
|
|
|
const AbstractBlock *Block::get() const {
|
|
return &unsafe<AbstractBlock>();
|
|
}
|
|
|
|
AbstractBlock *Block::operator->() {
|
|
return get();
|
|
}
|
|
|
|
const AbstractBlock *Block::operator->() const {
|
|
return get();
|
|
}
|
|
|
|
AbstractBlock &Block::operator*() {
|
|
return *get();
|
|
}
|
|
|
|
const AbstractBlock &Block::operator*() const {
|
|
return *get();
|
|
}
|
|
|
|
void Block::destroy() {
|
|
switch (get()->type()) {
|
|
case TextBlockType::Newline:
|
|
unsafe<NewlineBlock>().~NewlineBlock();
|
|
break;
|
|
case TextBlockType::Text:
|
|
unsafe<TextBlock>().~TextBlock();
|
|
break;
|
|
case TextBlockType::Emoji:
|
|
unsafe<EmojiBlock>().~EmojiBlock();
|
|
break;
|
|
case TextBlockType::CustomEmoji:
|
|
unsafe<CustomEmojiBlock>().~CustomEmojiBlock();
|
|
break;
|
|
case TextBlockType::Skip:
|
|
unsafe<SkipBlock>().~SkipBlock();
|
|
break;
|
|
default:
|
|
Unexpected("Bad text block type in Block(Block&&).");
|
|
}
|
|
}
|
|
|
|
int CountBlockHeight(
|
|
const AbstractBlock *block,
|
|
const style::TextStyle *st) {
|
|
return (block->type() == TextBlockType::Skip)
|
|
? static_cast<const SkipBlock*>(block)->height()
|
|
: (block->type() == TextBlockType::CustomEmoji)
|
|
? [&] {
|
|
const auto custom = static_cast<const CustomEmojiBlock*>(block)
|
|
->custom();
|
|
if (const auto metrics = custom->vertical(*st)) {
|
|
return metrics->height();
|
|
}
|
|
return st->lineHeight
|
|
? st->lineHeight
|
|
: st->font->height;
|
|
}()
|
|
: st->lineHeight
|
|
? st->lineHeight
|
|
: st->font->height;
|
|
}
|
|
|
|
} // namespace Text
|
|
} // namespace Ui
|