Added text style modifiers
This commit is contained in:
parent
640d4673ad
commit
b43fedaaac
11 changed files with 385 additions and 34 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#include "RichTextBox.hpp"
|
||||
#include "TextStyleModifier.hpp"
|
||||
#include <limits>
|
||||
#include <modules/skparagraph/include/DartTypes.h>
|
||||
#include <modules/skparagraph/include/Metrics.h>
|
||||
|
|
@ -9,6 +10,37 @@
|
|||
#include <modules/skparagraph/src/ParagraphImpl.h>
|
||||
#include <Helpers/MathExtras.hpp>
|
||||
#include <src/base/SkUTF.h>
|
||||
#include <cereal/types/string.hpp>
|
||||
|
||||
void RichTextBox::RichTextData::Paragraph::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(text);
|
||||
a(static_cast<uint32_t>(tStyles.size()));
|
||||
for(auto& [pos, styles] : tStyles) {
|
||||
a(pos, static_cast<uint16_t>(styles.size()));
|
||||
for(auto& [styleType, style] : styles)
|
||||
a(static_cast<uint16_t>(styleType), *style);
|
||||
}
|
||||
}
|
||||
|
||||
void RichTextBox::RichTextData::Paragraph::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(text);
|
||||
uint32_t styleCount;
|
||||
a(styleCount);
|
||||
tStyles.clear();
|
||||
for(uint32_t i = 0; i < styleCount; i++) {
|
||||
uint32_t pos;
|
||||
uint16_t styleCountAtPos;
|
||||
a(pos, styleCountAtPos);
|
||||
TextModAtPosContainer& mods = tStyles[pos];
|
||||
for(uint16_t j = 0; j < styleCountAtPos; j++) {
|
||||
uint16_t styleType;
|
||||
a(styleType);
|
||||
std::shared_ptr<TextStyleModifier> modifier = TextStyleModifier::allocate_modifier(static_cast<TextStyleModifier::ModifierType>(styleType));
|
||||
a(*modifier);
|
||||
mods.emplace(static_cast<TextStyleModifier::ModifierType>(styleType), modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RichTextBox::TextPosition::operator<(const RichTextBox::TextPosition& o) const {
|
||||
return (this->fParagraphIndex < o.fParagraphIndex) || (this->fParagraphIndex == o.fParagraphIndex && this->fTextByteIndex < o.fTextByteIndex);
|
||||
|
|
@ -50,13 +82,13 @@ void RichTextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool sh
|
|||
if(cur.selectionBeginPos != cur.selectionEndPos)
|
||||
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.selectionBeginPos, cur.selectionEndPos);
|
||||
else
|
||||
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.pos, move(Movement::LEFT, cur.pos));
|
||||
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.pos, move(ctrl ? Movement::LEFT_WORD : Movement::LEFT, cur.pos));
|
||||
break;
|
||||
case InputKey::DELETE:
|
||||
if(cur.selectionBeginPos != cur.selectionEndPos)
|
||||
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.selectionBeginPos, cur.selectionEndPos);
|
||||
else
|
||||
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.pos, move(Movement::RIGHT, cur.pos));
|
||||
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.pos, move(ctrl ? Movement::RIGHT_WORD : Movement::RIGHT, cur.pos));
|
||||
break;
|
||||
case InputKey::ENTER:
|
||||
process_text_input(cur, "\n");
|
||||
|
|
@ -217,7 +249,13 @@ RichTextBox::TextPosition RichTextBox::get_text_pos_closest_to_point(Vector2f po
|
|||
}
|
||||
|
||||
RichTextBox::RichTextData RichTextBox::get_rich_text_data() {
|
||||
return {get_string()};
|
||||
RichTextData toRet;
|
||||
for(auto& p : paragraphs) {
|
||||
toRet.paragraphs.emplace_back();
|
||||
toRet.paragraphs.back().text = p.text;
|
||||
toRet.paragraphs.back().tStyles = p.tStyles;
|
||||
}
|
||||
return toRet;
|
||||
}
|
||||
|
||||
void RichTextBox::clear_text() {
|
||||
|
|
@ -231,7 +269,15 @@ void RichTextBox::set_string(const std::string& str) {
|
|||
}
|
||||
|
||||
void RichTextBox::set_rich_text_data(const RichTextData& richText) {
|
||||
set_string(richText.text);
|
||||
paragraphs.clear();
|
||||
for(auto& p : richText.paragraphs) {
|
||||
paragraphs.emplace_back();
|
||||
paragraphs.back().text = p.text;
|
||||
paragraphs.back().tStyles = p.tStyles;
|
||||
}
|
||||
if(richText.paragraphs.empty())
|
||||
paragraphs.emplace_back();
|
||||
needsRebuild = true;
|
||||
}
|
||||
|
||||
std::string RichTextBox::get_string() {
|
||||
|
|
@ -261,6 +307,16 @@ std::string RichTextBox::get_text_between(TextPosition p1, TextPosition p2) {
|
|||
return toRet;
|
||||
}
|
||||
|
||||
void RichTextBox::set_initial_text_style_modifier(const std::shared_ptr<TextStyleModifier>& modifier) {
|
||||
std::shared_ptr<TextStyleModifier>& modToSet = paragraphs.back().tStyles[0][modifier->get_type()];
|
||||
needsRebuild |= ((modToSet == nullptr) || (!modifier->equivalent(*modToSet)));
|
||||
modToSet = modifier;
|
||||
}
|
||||
|
||||
void RichTextBox::set_text_style_modifier_between(TextPosition p1, TextPosition p2, const std::shared_ptr<TextStyleModifier>& modifier) {
|
||||
needsRebuild = true;
|
||||
}
|
||||
|
||||
RichTextBox::TextPosition RichTextBox::move(Movement movement, TextPosition pos, std::optional<float>* previousX, bool flipDependingOnTextDirection) {
|
||||
if(pos.fParagraphIndex >= paragraphs.size()) {
|
||||
pos.fParagraphIndex = paragraphs.size() - 1;
|
||||
|
|
@ -457,18 +513,35 @@ void RichTextBox::set_font_collection(const sk_sp<skia::textlayout::FontCollecti
|
|||
|
||||
void RichTextBox::rebuild() {
|
||||
if(needsRebuild) {
|
||||
|
||||
float heightOffset = 0.0f;
|
||||
skia::textlayout::TextStyle tStyle;
|
||||
|
||||
for(ParagraphData& pData : paragraphs) {
|
||||
pData.pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft);
|
||||
pData.pStyle.setTextDirection(skia::textlayout::TextDirection::kLtr);
|
||||
skia::textlayout::TextStyle tStyle;
|
||||
tStyle.setFontSize(16.0f);
|
||||
tStyle.setFontFamilies({SkString{"Roboto"}});
|
||||
pData.pStyle.setTextStyle(tStyle);
|
||||
|
||||
skia::textlayout::ParagraphBuilderImpl a(pData.pStyle, fontCollection, SkUnicodes::ICU::Make());
|
||||
a.addText(pData.text.c_str(), pData.text.length());
|
||||
|
||||
size_t startPos = 0;
|
||||
for(auto& [pos, tStylesAtPos] : pData.tStyles) {
|
||||
if(pos > pData.text.size())
|
||||
break;
|
||||
|
||||
for(auto& [styleType, styleMod] : tStylesAtPos)
|
||||
styleMod->modify_text_style(tStyle);
|
||||
if(startPos != pos) {
|
||||
a.pushStyle(tStyle);
|
||||
a.addText(pData.text.c_str() + startPos, pos - startPos);
|
||||
a.pop();
|
||||
}
|
||||
startPos = pos;
|
||||
}
|
||||
|
||||
if(startPos < pData.text.size()) {
|
||||
a.pushStyle(tStyle);
|
||||
a.addText(pData.text.c_str() + startPos, pData.text.size() - startPos);
|
||||
a.pop();
|
||||
}
|
||||
|
||||
pData.p = a.Build();
|
||||
pData.p->layout(width);
|
||||
pData.heightOffset = heightOffset;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,16 @@
|
|||
#include <include/core/SkCanvas.h>
|
||||
#include <modules/skparagraph/include/FontCollection.h>
|
||||
#include <modules/skparagraph/include/Paragraph.h>
|
||||
#include <map>
|
||||
#include "../SharedTypes.hpp"
|
||||
#include "TextStyleModifier.hpp"
|
||||
#include "cereal/archives/portable_binary.hpp"
|
||||
|
||||
class RichTextBox {
|
||||
public:
|
||||
typedef std::unordered_map<TextStyleModifier::ModifierType, std::shared_ptr<TextStyleModifier>> TextModAtPosContainer;
|
||||
typedef std::map<size_t, TextModAtPosContainer> TextModContainer;
|
||||
|
||||
RichTextBox();
|
||||
|
||||
enum class Movement {
|
||||
|
|
@ -48,15 +54,16 @@ class RichTextBox {
|
|||
};
|
||||
|
||||
struct RichTextData {
|
||||
std::string text;
|
||||
bool operator==(const RichTextData& o) const = default;
|
||||
bool operator!=(const RichTextData& o) const = default;
|
||||
template <typename Archive> void save(Archive& a) const {
|
||||
a(text);
|
||||
}
|
||||
template <typename Archive> void load(Archive& a) {
|
||||
a(text);
|
||||
struct Paragraph {
|
||||
std::string text;
|
||||
TextModContainer tStyles;
|
||||
void save(cereal::PortableBinaryOutputArchive& a) const;
|
||||
void load(cereal::PortableBinaryInputArchive& a);
|
||||
};
|
||||
template<typename Archive> void serialize(Archive& a) {
|
||||
a(paragraphs);
|
||||
}
|
||||
std::vector<Paragraph> paragraphs;
|
||||
};
|
||||
|
||||
TextPosition move(Movement movement, TextPosition pos, std::optional<float>* previousX = nullptr, bool flipDependingOnTextDirection = false);
|
||||
|
|
@ -81,6 +88,9 @@ class RichTextBox {
|
|||
SELECT_ALL
|
||||
};
|
||||
|
||||
void set_initial_text_style_modifier(const std::shared_ptr<TextStyleModifier>& modifier);
|
||||
void set_text_style_modifier_between(TextPosition p1, TextPosition p2, const std::shared_ptr<TextStyleModifier>& modifier);
|
||||
|
||||
RichTextData get_rich_text_data();
|
||||
void set_rich_text_data(const RichTextData& richText);
|
||||
void clear_text();
|
||||
|
|
@ -100,6 +110,7 @@ class RichTextBox {
|
|||
static size_t next_grapheme(const std::string& text, size_t textBytePos);
|
||||
static size_t prev_grapheme(const std::string& text, size_t textBytePos);
|
||||
static TextPosition get_text_pos_from_byte_pos(const std::string& text, size_t textBytePos);
|
||||
|
||||
size_t get_byte_pos_from_text_pos(TextPosition textPos);
|
||||
|
||||
void rects_between_text_positions_func(TextPosition p1, TextPosition p2, std::function<void(const SkRect& r)> f);
|
||||
|
|
@ -112,7 +123,6 @@ class RichTextBox {
|
|||
|
||||
bool newlinesAllowed = true;
|
||||
float width = 0.0f;
|
||||
float fontSize = 0.0f;
|
||||
bool needsRebuild = true;
|
||||
unsigned tabWidth = 8;
|
||||
sk_sp<skia::textlayout::FontCollection> fontCollection;
|
||||
|
|
@ -122,6 +132,7 @@ class RichTextBox {
|
|||
std::unique_ptr<skia::textlayout::Paragraph> p;
|
||||
float heightOffset;
|
||||
skia::textlayout::ParagraphStyle pStyle;
|
||||
TextModContainer tStyles;
|
||||
};
|
||||
|
||||
std::vector<ParagraphData> paragraphs;
|
||||
|
|
|
|||
155
src/RichTextBox/TextStyleModifier.cpp
Normal file
155
src/RichTextBox/TextStyleModifier.cpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#include "TextStyleModifier.hpp"
|
||||
#include <Helpers/ConvertVec.hpp>
|
||||
#include <Helpers/Serializers.hpp>
|
||||
|
||||
#define WEIGHT_VALUE_MODIFIER 100
|
||||
|
||||
std::shared_ptr<TextStyleModifier> TextStyleModifier::allocate_modifier(ModifierType type) {
|
||||
switch(type) {
|
||||
case ModifierType::SIZE:
|
||||
return std::make_shared<SizeTextStyleModifier>();
|
||||
case ModifierType::COLOR:
|
||||
return std::make_shared<ColorTextStyleModifier>();
|
||||
case ModifierType::WEIGHT:
|
||||
return std::make_shared<WeightTextStyleModifier>();
|
||||
case ModifierType::SLANT:
|
||||
return std::make_shared<SlantTextStyleModifier>();
|
||||
case ModifierType::FONT_FAMILIES:
|
||||
return std::make_shared<FontFamiliesTextStyleModifier>();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TextStyleModifier::equivalent(TextStyleModifier& modifier) const {
|
||||
if(modifier.get_type() != get_type())
|
||||
return false;
|
||||
return equivalent_data(modifier);
|
||||
}
|
||||
|
||||
TextStyleModifier::~TextStyleModifier() {}
|
||||
|
||||
|
||||
|
||||
TextStyleModifier::ModifierType WeightTextStyleModifier::get_type() const {
|
||||
return ModifierType::WEIGHT;
|
||||
}
|
||||
|
||||
void WeightTextStyleModifier::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(weightValue);
|
||||
}
|
||||
|
||||
void WeightTextStyleModifier::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(weightValue);
|
||||
}
|
||||
|
||||
void WeightTextStyleModifier::set_weight(SkFontStyle::Weight newWeight) {
|
||||
weightValue = newWeight / WEIGHT_VALUE_MODIFIER;
|
||||
}
|
||||
|
||||
SkFontStyle::Weight WeightTextStyleModifier::get_weight() {
|
||||
return static_cast<SkFontStyle::Weight>(static_cast<int>(weightValue) * WEIGHT_VALUE_MODIFIER);
|
||||
}
|
||||
|
||||
void WeightTextStyleModifier::modify_text_style(skia::textlayout::TextStyle& style) const {
|
||||
style.setFontStyle(SkFontStyle(static_cast<int>(weightValue) * WEIGHT_VALUE_MODIFIER, style.getFontStyle().width(), style.getFontStyle().slant()));
|
||||
}
|
||||
|
||||
bool WeightTextStyleModifier::equivalent_data(TextStyleModifier& modifier) const {
|
||||
return weightValue == static_cast<WeightTextStyleModifier&>(modifier).weightValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TextStyleModifier::ModifierType SlantTextStyleModifier::get_type() const {
|
||||
return ModifierType::SLANT;
|
||||
}
|
||||
|
||||
void SlantTextStyleModifier::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(slantValue);
|
||||
}
|
||||
|
||||
void SlantTextStyleModifier::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(slantValue);
|
||||
}
|
||||
|
||||
void SlantTextStyleModifier::set_slant(SkFontStyle::Slant newSlant) {
|
||||
slantValue = newSlant;
|
||||
}
|
||||
|
||||
SkFontStyle::Slant SlantTextStyleModifier::get_slant() {
|
||||
return static_cast<SkFontStyle::Slant>(slantValue);
|
||||
}
|
||||
|
||||
void SlantTextStyleModifier::modify_text_style(skia::textlayout::TextStyle& style) const {
|
||||
style.setFontStyle(SkFontStyle(style.getFontStyle().weight(), style.getFontStyle().width(), static_cast<SkFontStyle::Slant>(slantValue)));
|
||||
}
|
||||
|
||||
bool SlantTextStyleModifier::equivalent_data(TextStyleModifier& modifier) const {
|
||||
return slantValue == static_cast<SlantTextStyleModifier&>(modifier).slantValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TextStyleModifier::ModifierType ColorTextStyleModifier::get_type() const {
|
||||
return ModifierType::COLOR;
|
||||
}
|
||||
|
||||
void ColorTextStyleModifier::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(color);
|
||||
}
|
||||
|
||||
void ColorTextStyleModifier::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(color);
|
||||
}
|
||||
|
||||
void ColorTextStyleModifier::modify_text_style(skia::textlayout::TextStyle& style) const {
|
||||
style.setForegroundPaint(SkPaint{convert_vec4<SkColor4f>(color)});
|
||||
}
|
||||
|
||||
bool ColorTextStyleModifier::equivalent_data(TextStyleModifier& modifier) const {
|
||||
return color == static_cast<ColorTextStyleModifier&>(modifier).color;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TextStyleModifier::ModifierType SizeTextStyleModifier::get_type() const {
|
||||
return ModifierType::SIZE;
|
||||
}
|
||||
|
||||
void SizeTextStyleModifier::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(size);
|
||||
}
|
||||
|
||||
void SizeTextStyleModifier::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(size);
|
||||
}
|
||||
|
||||
void SizeTextStyleModifier::modify_text_style(skia::textlayout::TextStyle& style) const {
|
||||
style.setFontSize(size);
|
||||
}
|
||||
|
||||
bool SizeTextStyleModifier::equivalent_data(TextStyleModifier& modifier) const {
|
||||
return size == static_cast<SizeTextStyleModifier&>(modifier).size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TextStyleModifier::ModifierType FontFamiliesTextStyleModifier::get_type() const {
|
||||
return ModifierType::FONT_FAMILIES;
|
||||
}
|
||||
|
||||
void FontFamiliesTextStyleModifier::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(families);
|
||||
}
|
||||
|
||||
void FontFamiliesTextStyleModifier::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(families);
|
||||
}
|
||||
|
||||
void FontFamiliesTextStyleModifier::modify_text_style(skia::textlayout::TextStyle& style) const {
|
||||
style.setFontFamilies(families);
|
||||
}
|
||||
|
||||
bool FontFamiliesTextStyleModifier::equivalent_data(TextStyleModifier& modifier) const {
|
||||
return families == static_cast<FontFamiliesTextStyleModifier&>(modifier).families;
|
||||
}
|
||||
85
src/RichTextBox/TextStyleModifier.hpp
Normal file
85
src/RichTextBox/TextStyleModifier.hpp
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <modules/skparagraph/include/Paragraph.h>
|
||||
#include <modules/skparagraph/include/TextStyle.h>
|
||||
#include "cereal/archives/portable_binary.hpp"
|
||||
#include "../SharedTypes.hpp"
|
||||
|
||||
class TextStyleModifier {
|
||||
public:
|
||||
enum class ModifierType : uint16_t {
|
||||
SIZE = 0,
|
||||
COLOR,
|
||||
WEIGHT,
|
||||
SLANT,
|
||||
FONT_FAMILIES
|
||||
};
|
||||
static std::shared_ptr<TextStyleModifier> allocate_modifier(ModifierType type);
|
||||
virtual ModifierType get_type() const = 0;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const = 0;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) = 0;
|
||||
virtual void modify_text_style(skia::textlayout::TextStyle& style) const = 0;
|
||||
bool equivalent(TextStyleModifier& modifier) const;
|
||||
virtual ~TextStyleModifier();
|
||||
private:
|
||||
virtual bool equivalent_data(TextStyleModifier& modifier) const = 0;
|
||||
};
|
||||
|
||||
class WeightTextStyleModifier : public TextStyleModifier {
|
||||
public:
|
||||
virtual ModifierType get_type() const override;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void modify_text_style(skia::textlayout::TextStyle& style) const override;
|
||||
void set_weight(SkFontStyle::Weight newWeight);
|
||||
SkFontStyle::Weight get_weight();
|
||||
private:
|
||||
virtual bool equivalent_data(TextStyleModifier& modifier) const override;
|
||||
uint8_t weightValue;
|
||||
};
|
||||
|
||||
class SlantTextStyleModifier : public TextStyleModifier {
|
||||
public:
|
||||
virtual ModifierType get_type() const override;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void modify_text_style(skia::textlayout::TextStyle& style) const override;
|
||||
void set_slant(SkFontStyle::Slant newSlant);
|
||||
SkFontStyle::Slant get_slant();
|
||||
private:
|
||||
virtual bool equivalent_data(TextStyleModifier& modifier) const override;
|
||||
uint8_t slantValue;
|
||||
};
|
||||
|
||||
class ColorTextStyleModifier : public TextStyleModifier {
|
||||
public:
|
||||
virtual ModifierType get_type() const override;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void modify_text_style(skia::textlayout::TextStyle& style) const override;
|
||||
Vector4f color;
|
||||
private:
|
||||
virtual bool equivalent_data(TextStyleModifier& modifier) const override;
|
||||
};
|
||||
|
||||
class SizeTextStyleModifier : public TextStyleModifier {
|
||||
public:
|
||||
virtual ModifierType get_type() const override;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void modify_text_style(skia::textlayout::TextStyle& style) const override;
|
||||
float size;
|
||||
private:
|
||||
virtual bool equivalent_data(TextStyleModifier& modifier) const override;
|
||||
};
|
||||
|
||||
class FontFamiliesTextStyleModifier : public TextStyleModifier {
|
||||
public:
|
||||
virtual ModifierType get_type() const override;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const override;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) override;
|
||||
virtual void modify_text_style(skia::textlayout::TextStyle& style) const override;
|
||||
std::vector<SkString> families;
|
||||
private:
|
||||
virtual bool equivalent_data(TextStyleModifier& modifier) const override;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue