Can now add styles to textbox (stacking styles still has issues)
This commit is contained in:
parent
b43fedaaac
commit
bb388efffc
5 changed files with 191 additions and 78 deletions
|
|
@ -54,6 +54,10 @@ void DrawTextBox::update_from_delayed_ptr(const std::shared_ptr<DrawComponent>&
|
|||
void DrawTextBox::init_text_box(DrawingProgram& drawP) {
|
||||
textBox->set_font_collection(drawP.world.main.fonts.collection); // Getting a segfault relating to the paragraph cache means that the font collection hasn't been set yet
|
||||
textBox->set_width(d.p2.x() - d.p1.x());
|
||||
skia::textlayout::TextStyle tStyle;
|
||||
tStyle.setFontFamilies({SkString{"Roboto"}});
|
||||
tStyle.setFontSize(16.0f);
|
||||
textBox->set_initial_text_style(tStyle);
|
||||
}
|
||||
|
||||
void DrawTextBox::draw(SkCanvas* canvas, const DrawData& drawData) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "../DrawData.hpp"
|
||||
#include "Helpers/SCollision.hpp"
|
||||
#include <cereal/types/vector.hpp>
|
||||
#include <include/core/SkFontStyle.h>
|
||||
#include <memory>
|
||||
#include "EditTool.hpp"
|
||||
|
||||
|
|
@ -16,7 +17,21 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
|
|||
Toolbar& t = drawP.world.main.toolbar;
|
||||
t.gui.push_id("edit tool text");
|
||||
t.gui.text_label_centered("Edit Text");
|
||||
//t.gui.slider_scalar_field("Text Relative Size", "Text Size", &a->d.textSize, 5.0f, 100.0f);
|
||||
if(t.gui.text_button_wide("Bold button", "Bold")) {
|
||||
auto boldMod = std::make_shared<WeightTextStyleModifier>();
|
||||
boldMod->set_weight(SkFontStyle::kBold_Weight);
|
||||
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, boldMod);
|
||||
}
|
||||
if(t.gui.text_button_wide("Italic button", "Italic")) {
|
||||
auto italicMod = std::make_shared<SlantTextStyleModifier>();
|
||||
italicMod->set_slant(SkFontStyle::Slant::kItalic_Slant);
|
||||
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, italicMod);
|
||||
}
|
||||
if(t.gui.text_button_wide("Red button", "Red")) {
|
||||
auto redMod = std::make_shared<ColorTextStyleModifier>();
|
||||
redMod->color = {1.0f, 0.0f, 0.0f, 1.0f};
|
||||
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, redMod);
|
||||
}
|
||||
//t.gui.left_to_right_line_layout([&]() {
|
||||
// CLAY({.layout = {.sizing = {.width = CLAY_SIZING_FIXED(40), .height = CLAY_SIZING_FIXED(40)}}}) {
|
||||
// if(t.gui.color_button("Text Color", &a->d.textColor, &a->d.textColor == t.colorRight))
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "Element.hpp"
|
||||
#include "../../RichTextBox/RichTextBox.hpp"
|
||||
#include <limits>
|
||||
#include <modules/skparagraph/include/TextStyle.h>
|
||||
|
||||
namespace GUIStuff {
|
||||
|
||||
|
|
@ -66,15 +67,13 @@ template <typename T> class TextBox : public Element {
|
|||
paintOpts.cursor = *cur;
|
||||
paintOpts.cursorColor = {io.theme->fillColor1.fR, io.theme->fillColor1.fG, io.theme->fillColor1.fB};
|
||||
|
||||
auto fontFamiliesMod = std::make_shared<FontFamiliesTextStyleModifier>();
|
||||
fontFamiliesMod->families.emplace_back("Roboto");
|
||||
textbox->set_initial_text_style_modifier(fontFamiliesMod);
|
||||
auto fontSizeMod = std::make_shared<SizeTextStyleModifier>();
|
||||
fontSizeMod->size = io.fontSize;
|
||||
textbox->set_initial_text_style_modifier(fontSizeMod);
|
||||
auto fontColorMod = std::make_shared<ColorTextStyleModifier>();
|
||||
fontColorMod->color = convert_vec4<Vector4f>(io.theme->frontColor1);
|
||||
textbox->set_initial_text_style_modifier(fontColorMod);
|
||||
skia::textlayout::TextStyle tStyle;
|
||||
std::vector<SkString> families;
|
||||
families.emplace_back("Roboto");
|
||||
tStyle.setFontFamilies(families);
|
||||
tStyle.setFontSize(io.fontSize);
|
||||
tStyle.setForegroundPaint(SkPaint{io.theme->frontColor1});
|
||||
textbox->set_initial_text_style(tStyle);
|
||||
|
||||
textbox->paint(canvas, paintOpts);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,40 +12,31 @@
|
|||
#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::TextStyleRangeModifier::save(cereal::PortableBinaryOutputArchive& a) const {
|
||||
a(start, end, modifier->get_type(), *modifier);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
void RichTextBox::TextStyleRangeModifier::load(cereal::PortableBinaryInputArchive& a) {
|
||||
a(start, end);
|
||||
TextStyleModifier::ModifierType mType;
|
||||
a(mType);
|
||||
modifier = TextStyleModifier::allocate_modifier(mType);
|
||||
a(*modifier);
|
||||
}
|
||||
|
||||
void RichTextBox::TextStyleRangeModifier::apply_to_start_and_end(const std::function<void(TextPosition&)>& f) {
|
||||
f(start);
|
||||
f(end);
|
||||
}
|
||||
|
||||
bool RichTextBox::TextPosition::operator<(const RichTextBox::TextPosition& o) const {
|
||||
return (this->fParagraphIndex < o.fParagraphIndex) || (this->fParagraphIndex == o.fParagraphIndex && this->fTextByteIndex < o.fTextByteIndex);
|
||||
}
|
||||
|
||||
bool RichTextBox::TextPosition::operator>(const RichTextBox::TextPosition& o) const {
|
||||
return !(*this < o) && !(*this == o);
|
||||
}
|
||||
|
||||
RichTextBox::RichTextBox() {
|
||||
paragraphs.emplace_back();
|
||||
}
|
||||
|
|
@ -97,8 +88,8 @@ void RichTextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool sh
|
|||
process_text_input(cur, "\t");
|
||||
break;
|
||||
case InputKey::SELECT_ALL:
|
||||
cur.selectionBeginPos = {0, 0};
|
||||
cur.pos = cur.selectionEndPos = {paragraphs.back().text.size(), paragraphs.size() - 1};
|
||||
cur.selectionBeginPos = move(Movement::HOME, cur.pos);
|
||||
cur.pos = cur.selectionEndPos = move(Movement::END, cur.pos);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +207,7 @@ RichTextBox::TextPosition RichTextBox::get_text_pos_closest_to_point(Vector2f po
|
|||
|
||||
bool foundGlyph = pData.p->getClosestGlyphClusterAt(point.x(), point.y(), &glyphInfo);
|
||||
if(foundGlyph) {
|
||||
TextPosition toRet = {0, pIndex};
|
||||
TextPosition toRet = {pIndex, 0};
|
||||
toRet.fTextByteIndex = glyphInfo.fClusterTextRange.start;
|
||||
|
||||
if(glyphInfo.fGlyphClusterPosition == skia::textlayout::TextDirection::kLtr) {
|
||||
|
|
@ -245,7 +236,7 @@ RichTextBox::TextPosition RichTextBox::get_text_pos_closest_to_point(Vector2f po
|
|||
return toRet;
|
||||
}
|
||||
else
|
||||
return TextPosition{0, pIndex};
|
||||
return TextPosition{pIndex, 0};
|
||||
}
|
||||
|
||||
RichTextBox::RichTextData RichTextBox::get_rich_text_data() {
|
||||
|
|
@ -253,13 +244,14 @@ RichTextBox::RichTextData RichTextBox::get_rich_text_data() {
|
|||
for(auto& p : paragraphs) {
|
||||
toRet.paragraphs.emplace_back();
|
||||
toRet.paragraphs.back().text = p.text;
|
||||
toRet.paragraphs.back().tStyles = p.tStyles;
|
||||
}
|
||||
toRet.tStyleModifiers = tStyleModifiers;
|
||||
return toRet;
|
||||
}
|
||||
|
||||
void RichTextBox::clear_text() {
|
||||
paragraphs.clear();
|
||||
tStyleModifiers.clear();
|
||||
paragraphs.emplace_back();
|
||||
}
|
||||
|
||||
|
|
@ -273,10 +265,11 @@ void RichTextBox::set_rich_text_data(const RichTextData& richText) {
|
|||
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();
|
||||
tStyleModifiers = richText.tStyleModifiers;
|
||||
fit_text_style_ranges_in_text();
|
||||
needsRebuild = true;
|
||||
}
|
||||
|
||||
|
|
@ -307,14 +300,26 @@ 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_initial_text_style(const skia::textlayout::TextStyle& tStyle) {
|
||||
if(!tStyle.equals(initialTStyle)) {
|
||||
initialTStyle = tStyle;
|
||||
needsRebuild = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RichTextBox::set_text_style_modifier_between(TextPosition p1, TextPosition p2, const std::shared_ptr<TextStyleModifier>& modifier) {
|
||||
needsRebuild = true;
|
||||
p1 = move(Movement::NOWHERE, p1);
|
||||
p2 = move(Movement::NOWHERE, p2);
|
||||
if(p1 != p2) {
|
||||
fit_text_style_ranges_in_text();
|
||||
TextStyleRangeModifier toPlace;
|
||||
toPlace.start = std::min(p1, p2);
|
||||
toPlace.end = std::max(p1, p2);
|
||||
toPlace.modifier = modifier;
|
||||
tStyleModifiers.emplace_back(toPlace);
|
||||
needsRebuild = true;
|
||||
}
|
||||
}
|
||||
|
||||
RichTextBox::TextPosition RichTextBox::move(Movement movement, TextPosition pos, std::optional<float>* previousX, bool flipDependingOnTextDirection) {
|
||||
|
|
@ -378,7 +383,7 @@ RichTextBox::TextPosition RichTextBox::move(Movement movement, TextPosition pos,
|
|||
if(lineNumber <= 0 && paragraphs[pos.fParagraphIndex - 1].text.empty()) {
|
||||
if(previousX != nullptr && !previousX->has_value())
|
||||
*previousX = get_cursor_rect(pos).centerX();
|
||||
pos = TextPosition{0, pos.fParagraphIndex - 1};
|
||||
pos = TextPosition{pos.fParagraphIndex - 1, 0};
|
||||
}
|
||||
else {
|
||||
skia::textlayout::LineMetrics prevLineMetrics;
|
||||
|
|
@ -410,7 +415,7 @@ RichTextBox::TextPosition RichTextBox::move(Movement movement, TextPosition pos,
|
|||
if((lineNumber == -1 || lineNumber == static_cast<int>(paragraphs[pos.fParagraphIndex].p->lineNumber() - 1)) && paragraphs[pos.fParagraphIndex + 1].text.empty()) {
|
||||
if(previousX != nullptr && !previousX->has_value())
|
||||
*previousX = get_cursor_rect(pos).centerX();
|
||||
pos = TextPosition{0, pos.fParagraphIndex + 1};
|
||||
pos = TextPosition{pos.fParagraphIndex + 1, 0};
|
||||
}
|
||||
else {
|
||||
skia::textlayout::LineMetrics nextLineMetrics;
|
||||
|
|
@ -511,35 +516,71 @@ void RichTextBox::set_font_collection(const sk_sp<skia::textlayout::FontCollecti
|
|||
}
|
||||
}
|
||||
|
||||
void RichTextBox::fit_text_style_ranges_in_text() {
|
||||
std::erase_if(tStyleModifiers, [&](TextStyleRangeModifier& tStyleMod) {
|
||||
tStyleMod.start = move(Movement::NOWHERE, tStyleMod.start);
|
||||
tStyleMod.end = move(Movement::NOWHERE, tStyleMod.end);
|
||||
return tStyleMod.start == tStyleMod.end;
|
||||
});
|
||||
}
|
||||
|
||||
void RichTextBox::rebuild() {
|
||||
if(needsRebuild) {
|
||||
float heightOffset = 0.0f;
|
||||
skia::textlayout::TextStyle tStyle;
|
||||
std::map<TextPosition, std::vector<std::shared_ptr<TextStyleModifier>>> modifierStackCommands;
|
||||
|
||||
for(ParagraphData& pData : paragraphs) {
|
||||
for(TextStyleRangeModifier& modifierRange : tStyleModifiers) {
|
||||
modifierStackCommands[modifierRange.start].emplace_back(modifierRange.modifier);
|
||||
modifierStackCommands[modifierRange.end].emplace_back(modifierRange.modifier);
|
||||
}
|
||||
|
||||
auto nextStackCommandIt = modifierStackCommands.begin();
|
||||
std::unordered_map<TextStyleModifier::ModifierType, std::stack<std::shared_ptr<TextStyleModifier>>> modifierStacks;
|
||||
|
||||
skia::textlayout::TextStyle tStyle = initialTStyle;
|
||||
|
||||
for(size_t pIndex = 0; pIndex < paragraphs.size(); pIndex++) {
|
||||
ParagraphData& pData = paragraphs[pIndex];
|
||||
pData.pStyle.setTextAlign(skia::textlayout::TextAlign::kLeft);
|
||||
pData.pStyle.setTextDirection(skia::textlayout::TextDirection::kLtr);
|
||||
pData.pStyle.setTextStyle(tStyle);
|
||||
size_t tIndex = 0;
|
||||
skia::textlayout::ParagraphBuilderImpl a(pData.pStyle, fontCollection, SkUnicodes::ICU::Make());
|
||||
|
||||
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) {
|
||||
for(;;) {
|
||||
if(nextStackCommandIt == modifierStackCommands.end() || nextStackCommandIt->first.fParagraphIndex != pIndex) {
|
||||
a.pushStyle(tStyle);
|
||||
a.addText(pData.text.c_str() + startPos, pos - startPos);
|
||||
a.addText(pData.text.c_str() + tIndex, pData.text.length() - tIndex);
|
||||
a.pop();
|
||||
break;
|
||||
}
|
||||
startPos = pos;
|
||||
}
|
||||
else {
|
||||
auto& nextStackCommand = *nextStackCommandIt;
|
||||
|
||||
if(startPos < pData.text.size()) {
|
||||
a.pushStyle(tStyle);
|
||||
a.addText(pData.text.c_str() + startPos, pData.text.size() - startPos);
|
||||
a.pop();
|
||||
// Print with previous style
|
||||
a.pushStyle(tStyle);
|
||||
a.addText(pData.text.c_str() + tIndex, nextStackCommand.first.fTextByteIndex - tIndex);
|
||||
a.pop();
|
||||
tIndex = nextStackCommand.first.fTextByteIndex;
|
||||
|
||||
// Modify stack with new data
|
||||
for(const std::shared_ptr<TextStyleModifier>& modifier : nextStackCommand.second) {
|
||||
auto& modStack = modifierStacks[modifier->get_type()];
|
||||
if(modStack.empty() || modStack.top() != modifier)
|
||||
modStack.emplace(modifier);
|
||||
else
|
||||
modStack.pop();
|
||||
}
|
||||
|
||||
// Modify style with new stacks
|
||||
tStyle = initialTStyle;
|
||||
for(auto& [modType, modifier] : modifierStacks) {
|
||||
if(!modifier.empty())
|
||||
modifier.top()->modify_text_style(tStyle);
|
||||
}
|
||||
|
||||
++nextStackCommandIt;
|
||||
}
|
||||
}
|
||||
|
||||
pData.p = a.Build();
|
||||
|
|
@ -547,6 +588,7 @@ void RichTextBox::rebuild() {
|
|||
pData.heightOffset = heightOffset;
|
||||
heightOffset += pData.p->getHeight();
|
||||
}
|
||||
|
||||
needsRebuild = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -561,6 +603,16 @@ RichTextBox::TextPosition RichTextBox::insert(TextPosition pos, std::string_view
|
|||
for(char c : textToInsert) {
|
||||
if(c == '\n') {
|
||||
if(newlinesAllowed) {
|
||||
for(TextStyleRangeModifier& tStyle : tStyleModifiers) {
|
||||
tStyle.apply_to_start_and_end([&pos](TextPosition& p){
|
||||
if(p.fParagraphIndex > pos.fParagraphIndex)
|
||||
p.fParagraphIndex++;
|
||||
else if(p.fParagraphIndex == pos.fParagraphIndex && p.fTextByteIndex >= pos.fTextByteIndex) {
|
||||
p.fTextByteIndex = p.fTextByteIndex - pos.fTextByteIndex;
|
||||
p.fParagraphIndex++;
|
||||
}
|
||||
});
|
||||
}
|
||||
paragraphs.insert(paragraphs.begin() + pos.fParagraphIndex + 1, ParagraphData{});
|
||||
paragraphs[pos.fParagraphIndex + 1].text = paragraphs[pos.fParagraphIndex].text.substr(pos.fTextByteIndex, paragraphs[pos.fParagraphIndex].text.size() - pos.fTextByteIndex);
|
||||
paragraphs[pos.fParagraphIndex].text.erase(pos.fTextByteIndex, paragraphs[pos.fParagraphIndex].text.size() - pos.fTextByteIndex);
|
||||
|
|
@ -569,15 +621,28 @@ RichTextBox::TextPosition RichTextBox::insert(TextPosition pos, std::string_view
|
|||
}
|
||||
}
|
||||
else if(c == '\t') {
|
||||
for(TextStyleRangeModifier& tStyle : tStyleModifiers) {
|
||||
tStyle.apply_to_start_and_end([&pos, tabWidth = tabWidth](TextPosition& p) {
|
||||
if(p.fParagraphIndex == pos.fParagraphIndex && p.fTextByteIndex >= pos.fTextByteIndex)
|
||||
p.fTextByteIndex += tabWidth;
|
||||
});
|
||||
}
|
||||
paragraphs[pos.fParagraphIndex].text.insert(pos.fTextByteIndex, std::string(tabWidth, ' '));
|
||||
pos.fTextByteIndex += tabWidth;
|
||||
}
|
||||
else {
|
||||
for(TextStyleRangeModifier& tStyle : tStyleModifiers) {
|
||||
tStyle.apply_to_start_and_end([&pos](TextPosition& p) {
|
||||
if(p.fParagraphIndex == pos.fParagraphIndex && p.fTextByteIndex >= pos.fTextByteIndex)
|
||||
p.fTextByteIndex++;
|
||||
});
|
||||
}
|
||||
paragraphs[pos.fParagraphIndex].text.insert(paragraphs[pos.fParagraphIndex].text.begin() + pos.fTextByteIndex, c);
|
||||
pos.fTextByteIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
fit_text_style_ranges_in_text();
|
||||
needsRebuild = true;
|
||||
return pos;
|
||||
}
|
||||
|
|
@ -601,6 +666,25 @@ RichTextBox::TextPosition RichTextBox::remove(TextPosition p1, TextPosition p2)
|
|||
paragraph.text.insert(start.fTextByteIndex, paragraphs[end.fParagraphIndex].text.substr(end.fTextByteIndex));
|
||||
paragraphs.erase(paragraphs.begin() + start.fParagraphIndex + 1, paragraphs.begin() + end.fParagraphIndex + 1);
|
||||
}
|
||||
|
||||
for(TextStyleRangeModifier& tStyle : tStyleModifiers) {
|
||||
tStyle.apply_to_start_and_end([&start, &end](TextPosition& p) {
|
||||
if(p > start) {
|
||||
if(end > p)
|
||||
p = start;
|
||||
else if(start.fParagraphIndex == p.fParagraphIndex)
|
||||
p.fTextByteIndex -= (end.fTextByteIndex - start.fTextByteIndex);
|
||||
else if(end.fParagraphIndex == p.fParagraphIndex) {
|
||||
p.fParagraphIndex = start.fParagraphIndex;
|
||||
p.fTextByteIndex = start.fTextByteIndex + (p.fTextByteIndex - end.fTextByteIndex);
|
||||
}
|
||||
else
|
||||
p.fParagraphIndex -= (end.fParagraphIndex - start.fParagraphIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fit_text_style_ranges_in_text();
|
||||
needsRebuild = true;
|
||||
return start;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,13 @@
|
|||
#include <include/core/SkCanvas.h>
|
||||
#include <modules/skparagraph/include/FontCollection.h>
|
||||
#include <modules/skparagraph/include/Paragraph.h>
|
||||
#include <map>
|
||||
#include <modules/skparagraph/include/TextStyle.h>
|
||||
#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 {
|
||||
|
|
@ -29,13 +26,23 @@ class RichTextBox {
|
|||
|
||||
struct TextPosition {
|
||||
// Set to uint32_t values because theyll be saved and sent over networks. Size has to be consistent
|
||||
size_t fTextByteIndex = std::numeric_limits<uint32_t>::max();
|
||||
size_t fParagraphIndex = std::numeric_limits<uint32_t>::max();
|
||||
size_t fTextByteIndex = std::numeric_limits<uint32_t>::max();
|
||||
template <typename Archive> void serialize(Archive& a) {
|
||||
a((uint32_t)fTextByteIndex, (uint32_t)fParagraphIndex);
|
||||
a((uint32_t)fParagraphIndex, (uint32_t)fTextByteIndex);
|
||||
}
|
||||
bool operator==(const TextPosition& o) const = default;
|
||||
bool operator<(const TextPosition& o) const;
|
||||
bool operator>(const TextPosition& o) const;
|
||||
};
|
||||
|
||||
struct TextStyleRangeModifier {
|
||||
TextPosition start;
|
||||
TextPosition end;
|
||||
std::shared_ptr<TextStyleModifier> modifier;
|
||||
void apply_to_start_and_end(const std::function<void(TextPosition&)>& f);
|
||||
void save(cereal::PortableBinaryOutputArchive& a) const;
|
||||
void load(cereal::PortableBinaryInputArchive& a);
|
||||
};
|
||||
|
||||
struct Cursor {
|
||||
|
|
@ -56,14 +63,15 @@ class RichTextBox {
|
|||
struct RichTextData {
|
||||
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(text);
|
||||
}
|
||||
};
|
||||
template<typename Archive> void serialize(Archive& a) {
|
||||
a(paragraphs);
|
||||
a(paragraphs, tStyleModifiers);
|
||||
}
|
||||
std::vector<Paragraph> paragraphs;
|
||||
std::vector<TextStyleRangeModifier> tStyleModifiers;
|
||||
};
|
||||
|
||||
TextPosition move(Movement movement, TextPosition pos, std::optional<float>* previousX = nullptr, bool flipDependingOnTextDirection = false);
|
||||
|
|
@ -88,7 +96,7 @@ class RichTextBox {
|
|||
SELECT_ALL
|
||||
};
|
||||
|
||||
void set_initial_text_style_modifier(const std::shared_ptr<TextStyleModifier>& modifier);
|
||||
void set_initial_text_style(const skia::textlayout::TextStyle& tStyle);
|
||||
void set_text_style_modifier_between(TextPosition p1, TextPosition p2, const std::shared_ptr<TextStyleModifier>& modifier);
|
||||
|
||||
RichTextData get_rich_text_data();
|
||||
|
|
@ -115,6 +123,8 @@ class RichTextBox {
|
|||
|
||||
void rects_between_text_positions_func(TextPosition p1, TextPosition p2, std::function<void(const SkRect& r)> f);
|
||||
|
||||
void fit_text_style_ranges_in_text();
|
||||
|
||||
SkRect get_cursor_rect(TextPosition pos);
|
||||
|
||||
TextPosition get_text_pos_closest_to_point(Vector2f point);
|
||||
|
|
@ -132,8 +142,9 @@ class RichTextBox {
|
|||
std::unique_ptr<skia::textlayout::Paragraph> p;
|
||||
float heightOffset;
|
||||
skia::textlayout::ParagraphStyle pStyle;
|
||||
TextModContainer tStyles;
|
||||
};
|
||||
|
||||
skia::textlayout::TextStyle initialTStyle;
|
||||
std::vector<ParagraphData> paragraphs;
|
||||
std::vector<TextStyleRangeModifier> tStyleModifiers;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue