Created RichText namespace, and tweaked scrollbar behaviour

This commit is contained in:
Yousef Khadadeh 2025-11-10 14:45:51 +03:00
parent e0143f49f0
commit 435d9346a9
17 changed files with 163 additions and 139 deletions

View file

@ -53,8 +53,9 @@ set(sources "src/main.cpp"
"src/ResourceDisplay/FileResourceDisplay.cpp"
"src/Server/MainServer.cpp"
"src/Server/ServerData.cpp"
"src/RichTextBox/RichTextBox.cpp"
"src/RichTextBox/TextStyleModifier.cpp"
"src/RichText/TextBox.cpp"
"src/RichText/TextStyleModifier.cpp"
"src/RichText/ParagraphStyleData.cpp"
"src/DrawingProgram/DrawingProgramCache.cpp"
"src/DrawingProgram/DrawingProgram.cpp"
"src/DrawingProgram/BrushTool.cpp"

View file

@ -9,9 +9,11 @@
#include "../DrawCollision.hpp"
#endif
using namespace RichText;
DrawTextBox::DrawTextBox():
textBox(std::make_shared<RichTextBox>()),
cursor(std::make_shared<RichTextBox::Cursor>())
textBox(std::make_shared<TextBox>()),
cursor(std::make_shared<TextBox::Cursor>())
{
skia::textlayout::TextStyle tStyle;
textBox->set_initial_text_style(tStyle);
@ -30,7 +32,7 @@ void DrawTextBox::save(cereal::PortableBinaryOutputArchive& a) const {
}
void DrawTextBox::load(cereal::PortableBinaryInputArchive& a) {
RichTextBox::RichTextData richText;
TextData richText;
a(d.editing, d.p1, d.p2, richText, *cursor);
textBox->set_rich_text_data(richText);
}
@ -78,7 +80,7 @@ void DrawTextBox::draw(SkCanvas* canvas, const DrawData& drawData) {
canvas->clipRect(clipR);
canvas->translate(d.p1.x(), d.p1.y());
RichTextBox::PaintOpts paintOpts;
TextBox::PaintOpts paintOpts;
paintOpts.cursorColor = {0.7f, 0.7f, 1.0f};
if(d.editing && cursor)
paintOpts.cursor = *cursor;

View file

@ -2,7 +2,7 @@
#include "../SharedTypes.hpp"
#include "DrawComponent.hpp"
#include "../CoordSpaceHelper.hpp"
#include "../RichTextBox/RichTextBox.hpp"
#include "../RichText/TextBox.hpp"
#ifndef IS_SERVER
#include <include/core/SkPath.h>
@ -39,8 +39,8 @@ class DrawTextBox : public DrawComponent {
virtual SCollision::AABB<float> get_obj_coord_bounds() const override;
std::shared_ptr<RichTextBox> textBox;
std::shared_ptr<RichTextBox::Cursor> cursor;
std::shared_ptr<RichText::TextBox> textBox;
std::shared_ptr<RichText::TextBox::Cursor> cursor;
SCollision::BVHContainer<float> collisionTree;
#endif
};

View file

@ -14,11 +14,14 @@
#include <memory>
#include "EditTool.hpp"
using namespace RichText;
TextBoxEditTool::TextBoxEditTool(DrawingProgram& initDrawP):
DrawingProgramEditToolBase(initDrawP)
{}
bool TextBoxEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
std::shared_ptr<DrawTextBox> a = std::static_pointer_cast<DrawTextBox>(comp);
Toolbar& t = drawP.world.main.toolbar;
@ -117,10 +120,10 @@ uint8_t TextBoxEditTool::get_new_decoration_value() {
}
void TextBoxEditTool::set_mods_at_selection(const std::shared_ptr<DrawTextBox>& a) {
RichTextBox::TextPosition start = std::min(a->cursor->selectionBeginPos, a->cursor->selectionEndPos);
RichTextBox::TextPosition end = std::max(a->cursor->selectionBeginPos, a->cursor->selectionEndPos);
TextPosition start = std::min(a->cursor->selectionBeginPos, a->cursor->selectionEndPos);
TextPosition end = std::max(a->cursor->selectionBeginPos, a->cursor->selectionEndPos);
if(start == end)
currentMods = a->textBox->get_mods_used_at_pos(a->textBox->move(RichTextBox::Movement::LEFT, start));
currentMods = a->textBox->get_mods_used_at_pos(a->textBox->move(TextBox::Movement::LEFT, start));
else
currentMods = a->textBox->get_mods_used_at_pos(start);
}
@ -131,7 +134,7 @@ void TextBoxEditTool::commit_edit_updates(const std::shared_ptr<DrawComponent>&
a->d.editing = false;
auto pData = std::any_cast<TextBoxEditToolAllData>(prevData);
TextBoxEditToolAllData cData = get_all_data(a);
RichTextBox::RichTextData richText = a->textBox->get_rich_text_data();
TextData richText = a->textBox->get_rich_text_data();
drawP.world.undo.push(UndoManager::UndoRedoPair{
[&drawP = drawP, a, pData]() {
a->d = pData.textboxData;
@ -163,7 +166,7 @@ void TextBoxEditTool::edit_start(EditTool& editTool, const std::shared_ptr<DrawC
std::shared_ptr<DrawTextBox> a = std::static_pointer_cast<DrawTextBox>(comp);
auto& cur = a->cursor;
auto& textbox = a->textBox;
cur = std::make_shared<RichTextBox::Cursor>();
cur = std::make_shared<TextBox::Cursor>();
Vector2f textSelectPos = a->get_mouse_pos(drawP);
textbox->process_mouse_left_button(*cur, textSelectPos, true, false, false);
prevData = get_all_data(a);

View file

@ -17,7 +17,7 @@ class TextBoxEditTool : public DrawingProgramEditToolBase {
private:
struct TextBoxEditToolAllData {
DrawTextBox::Data textboxData;
RichTextBox::RichTextData richText;
RichText::TextData richText;
};
uint32_t newFontSize = 15;
@ -31,7 +31,7 @@ class TextBoxEditTool : public DrawingProgramEditToolBase {
uint8_t get_new_decoration_value();
void set_mods_at_selection(const std::shared_ptr<DrawTextBox>& a);
TextStyleModifier::ModifierMap currentMods;
RichText::TextStyleModifier::ModifierMap currentMods;
std::vector<std::string> sortedFontList;

View file

@ -10,7 +10,7 @@
#include <Helpers/SCollision.hpp>
#include <Helpers/Serializers.hpp>
#include <nlohmann/json.hpp>
#include "../../RichTextBox/RichTextBox.hpp"
#include "../../RichText/TextBox.hpp"
using namespace Eigen;
@ -103,7 +103,7 @@ struct UpdateInputData {
std::optional<std::string> textOut;
} clipboard;
std::function<void(std::shared_ptr<RichTextBox>, std::shared_ptr<RichTextBox::Cursor>)> richTextBoxEdit;
std::function<void(std::shared_ptr<RichText::TextBox>, std::shared_ptr<RichText::TextBox::Cursor>)> richTextBoxEdit;
SkFont get_font(float fSize) const;

View file

@ -1,6 +1,6 @@
#pragma once
#include "Element.hpp"
#include "../../RichTextBox/RichTextBox.hpp"
#include "../../RichText/TextBox.hpp"
#include <limits>
#include <modules/skparagraph/include/TextStyle.h>
@ -73,7 +73,7 @@ template <typename T> class TextBox : public Element {
canvas->translate(bb.min.x() + 2.0f, bb.min.y());
RichTextBox::PaintOpts paintOpts;
RichText::TextBox::PaintOpts paintOpts;
if(selection.selected)
paintOpts.cursor = *cur;
paintOpts.cursorColor = {io.theme->fillColor1.fR, io.theme->fillColor1.fG, io.theme->fillColor1.fB};
@ -97,11 +97,11 @@ template <typename T> class TextBox : public Element {
if(data && (*data == oldData) && !reallyForce && textbox && cur)
return;
textbox = std::make_shared<RichTextBox>();
textbox = std::make_shared<RichText::TextBox>();
textbox->set_font_collection(io.fontCollection);
textbox->set_allow_newlines(!singleLine);
cur = std::make_shared<RichTextBox::Cursor>();
cur = std::make_shared<RichText::TextBox::Cursor>();
if(data) {
cur->pos = cur->selectionBeginPos = cur->selectionEndPos = textbox->insert({0, 0}, toStr(*data));
@ -125,8 +125,8 @@ template <typename T> class TextBox : public Element {
std::function<std::optional<T>(const std::string&)> fromStr;
std::function<std::string(const T&)> toStr;
std::shared_ptr<RichTextBox> textbox;
std::shared_ptr<RichTextBox::Cursor> cur;
std::shared_ptr<RichText::TextBox> textbox;
std::shared_ptr<RichText::TextBox::Cursor> cur;
SCollision::AABB<float> bb;
};

View file

@ -669,7 +669,6 @@ void GUIManager::scroll_bar_area(const std::string& id, const std::function<void
float currentScrollPos = 0.0f;
bool isMoving = false;
bool clickedOnScroller = false;
float scrollerStartPos;
float mouseStartPos;
@ -749,20 +748,17 @@ void GUIManager::scroll_bar_area(const std::string& id, const std::function<void
CLAY({ .layout = {.sizing = {.width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0)}}}) {}
if(Clay_Hovered() && io->mouse.leftClick) {
sD.isMoving = true;
sD.clickedOnScroller = isHoveringOverScroller;
if(isHoveringOverScroller) {
if(isHoveringOverScroller)
sD.scrollerStartPos = scrollAreaBB.y + areaAboveScrollerSize + scrollerSize * 0.5f;
sD.mouseStartPos = io->mouse.pos.y();
}
else
sD.scrollerStartPos = std::clamp(io->mouse.pos.y(), scrollAreaBB.y + scrollerSize * 0.5f, scrollAreaBB.y + scrollAreaBB.height - scrollerSize * 0.5f);
sD.mouseStartPos = io->mouse.pos.y();
}
if(!io->mouse.leftHeld)
sD.isMoving = false;
if(sD.isMoving) {
float newScrollPosFrac;
if(sD.clickedOnScroller)
newScrollPosFrac = std::clamp((sD.scrollerStartPos - (sD.mouseStartPos - io->mouse.pos.y()) - scrollAreaBB.y - scrollerSize * 0.5f) / (scrollAreaBB.height - scrollerSize), 0.0f, 1.0f);
else
newScrollPosFrac = std::clamp((io->mouse.pos.y() - scrollAreaBB.y - scrollerSize * 0.5f) / (scrollAreaBB.height - scrollerSize), 0.0f, 1.0f);
newScrollPosFrac = std::clamp((sD.scrollerStartPos - (sD.mouseStartPos - io->mouse.pos.y()) - scrollAreaBB.y - scrollerSize * 0.5f) / (scrollAreaBB.height - scrollerSize), 0.0f, 1.0f);
sD.currentScrollPos = newScrollPosFrac * (-scrollPosMax);
}
sD.currentScrollPos = std::clamp(sD.currentScrollPos, -scrollPosMax, 0.0f);

View file

@ -77,7 +77,7 @@ void InputManager::text_input_silence_everything() {
text.newInput.clear();
}
void InputManager::Text::set_rich_text_box_input(const std::shared_ptr<RichTextBox>& nTextBox, const std::shared_ptr<RichTextBox::Cursor>& nCursor, const std::optional<TextStyleModifier::ModifierMap>& nModMap) {
void InputManager::Text::set_rich_text_box_input(const std::shared_ptr<RichText::TextBox>& nTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& nCursor, const std::optional<RichText::TextStyleModifier::ModifierMap>& nModMap) {
set_accepting_input();
newTextBox = nTextBox;
newCursor = nCursor;
@ -219,42 +219,42 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
case SDLK_UP:
set_key_down(e, KEY_TEXT_UP);
if(text.textBox && key(KEY_TEXT_UP).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::UP, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::UP, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
case SDLK_DOWN:
set_key_down(e, KEY_TEXT_DOWN);
if(text.textBox && key(KEY_TEXT_DOWN).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::DOWN, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::DOWN, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
case SDLK_LEFT:
set_key_down(e, KEY_TEXT_LEFT);
if(text.textBox && key(KEY_TEXT_LEFT).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::LEFT, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::LEFT, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
case SDLK_RIGHT:
set_key_down(e, KEY_TEXT_RIGHT);
if(text.textBox && key(KEY_TEXT_RIGHT).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::RIGHT, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::RIGHT, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
case SDLK_BACKSPACE:
set_key_down(e, KEY_TEXT_BACKSPACE);
if(text.textBox && key(KEY_TEXT_BACKSPACE).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::BACKSPACE, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::BACKSPACE, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
case SDLK_DELETE:
set_key_down(e, KEY_TEXT_DELETE);
if(text.textBox && key(KEY_TEXT_DELETE).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::DELETE, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::DELETE, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
case SDLK_HOME:
set_key_down(e, KEY_TEXT_HOME);
if(text.textBox && key(KEY_TEXT_HOME).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::HOME, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::HOME, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
case SDLK_END:
set_key_down(e, KEY_TEXT_END);
if(text.textBox && key(KEY_TEXT_END).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::END, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::END, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
case SDLK_LSHIFT:
set_key_down(e, KEY_TEXT_SHIFT);
@ -294,18 +294,18 @@ void InputManager::backend_key_down_update(const SDL_KeyboardEvent& e) {
if((kMod & SDL_KMOD_GUI) || (kMod & SDL_KMOD_CTRL))
set_key_down(e, KEY_TEXT_SELECTALL);
if(text.textBox && key(KEY_TEXT_SELECTALL).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::SELECT_ALL, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::SELECT_ALL, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
case SDLK_RETURN: {
set_key_down(e, KEY_TEXT_ENTER);
if(text.textBox && key(KEY_TEXT_ENTER).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::ENTER, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::ENTER, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
}
case SDLK_TAB: {
set_key_down(e, KEY_TEXT_TAB);
if(text.textBox && key(KEY_TEXT_TAB).repeat)
text.textBox->process_key_input(*text.cursor, RichTextBox::InputKey::TAB, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
text.textBox->process_key_input(*text.cursor, RichText::TextBox::InputKey::TAB, ctrl_or_meta_held(), key(KEY_GENERIC_LSHIFT).held, text.modMap);
break;
}
case SDLK_ESCAPE:

View file

@ -1,7 +1,8 @@
#pragma once
#include <Eigen/Dense>
#include <array>
#include "RichTextBox/TextStyleModifier.hpp"
#include "RichText/TextStyleModifier.hpp"
#include "RichText/TextBox.hpp"
#include "TimePoint.hpp"
#include <Helpers/Hashes.hpp>
#include <unordered_map>
@ -11,7 +12,6 @@
#include <SDL3/SDL.h>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_keycode.h>
#include "RichTextBox/RichTextBox.hpp"
using namespace Eigen;
@ -88,17 +88,17 @@ struct InputManager {
void set_accepting_input();
bool get_accepting_input();
void set_rich_text_box_input(const std::shared_ptr<RichTextBox>& nTextBox, const std::shared_ptr<RichTextBox::Cursor>& nCursor, const std::optional<TextStyleModifier::ModifierMap>& nModMap = std::nullopt);
void set_rich_text_box_input(const std::shared_ptr<RichText::TextBox>& nTextBox, const std::shared_ptr<RichText::TextBox::Cursor>& nCursor, const std::optional<RichText::TextStyleModifier::ModifierMap>& nModMap = std::nullopt);
void add_text_to_textbox(const std::string& inputText);
private:
std::shared_ptr<RichTextBox> textBox;
std::shared_ptr<RichTextBox::Cursor> cursor;
std::optional<TextStyleModifier::ModifierMap> modMap;
std::shared_ptr<RichText::TextBox> textBox;
std::shared_ptr<RichText::TextBox::Cursor> cursor;
std::optional<RichText::TextStyleModifier::ModifierMap> modMap;
std::shared_ptr<RichTextBox> newTextBox;
std::shared_ptr<RichTextBox::Cursor> newCursor;
std::optional<TextStyleModifier::ModifierMap> newModMap;
std::shared_ptr<RichText::TextBox> newTextBox;
std::shared_ptr<RichText::TextBox::Cursor> newCursor;
std::optional<RichText::TextStyleModifier::ModifierMap> newModMap;
bool acceptingInput = false;
bool acceptingInputNew = false;

View file

View file

@ -0,0 +1,6 @@
#pragma once
struct ParagraphStyleData {
};

View file

@ -1,4 +1,4 @@
#include "RichTextBox.hpp"
#include "TextBox.hpp"
#include "TextStyleModifier.hpp"
#include <limits>
#include <modules/skparagraph/include/DartTypes.h>
@ -12,7 +12,9 @@
#include <src/base/SkUTF.h>
#include <cereal/types/string.hpp>
void RichTextBox::RichTextData::save(cereal::PortableBinaryOutputArchive& a) const {
namespace RichText {
void TextData::save(cereal::PortableBinaryOutputArchive& a) const {
a(paragraphs);
a(static_cast<uint32_t>(tStyleMods.size()));
for(auto& [pos, modsInPos] : tStyleMods) {
@ -22,7 +24,7 @@ void RichTextBox::RichTextData::save(cereal::PortableBinaryOutputArchive& a) con
}
}
void RichTextBox::RichTextData::load(cereal::PortableBinaryInputArchive& a) {
void TextData::load(cereal::PortableBinaryInputArchive& a) {
a(paragraphs);
uint32_t tStyleModCount;
a(tStyleModCount);
@ -41,27 +43,27 @@ void RichTextBox::RichTextData::load(cereal::PortableBinaryInputArchive& a) {
}
}
bool RichTextBox::TextPosition::operator<(const RichTextBox::TextPosition& o) const {
bool TextPosition::operator<(const 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 {
bool TextPosition::operator>(const TextPosition& o) const {
return !(*this < o) && !(*this == o);
}
bool RichTextBox::TextPosition::operator>=(const RichTextBox::TextPosition& o) const {
bool TextPosition::operator>=(const TextPosition& o) const {
return (*this > o) || (*this == o);
}
bool RichTextBox::TextPosition::operator<=(const RichTextBox::TextPosition& o) const {
bool TextPosition::operator<=(const TextPosition& o) const {
return (*this < o) || (*this == o);
}
RichTextBox::RichTextBox() {
TextBox::TextBox() {
paragraphs.emplace_back();
}
void RichTextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift, const std::optional<TextStyleModifier::ModifierMap>& inputModMap) {
void TextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift, const std::optional<TextStyleModifier::ModifierMap>& inputModMap) {
bool moved = false;
switch(in) {
@ -122,7 +124,7 @@ void RichTextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool sh
inputChangedTextBox = true;
}
void RichTextBox::process_mouse_left_button(Cursor& cur, const Vector2f& pos, bool clicked, bool held, bool shift) {
void TextBox::process_mouse_left_button(Cursor& cur, const Vector2f& pos, bool clicked, bool held, bool shift) {
if(clicked || held) {
rebuild();
@ -138,11 +140,11 @@ void RichTextBox::process_mouse_left_button(Cursor& cur, const Vector2f& pos, bo
}
}
std::string RichTextBox::process_copy(Cursor& cur) {
std::string TextBox::process_copy(Cursor& cur) {
return get_text_between(cur.selectionBeginPos, cur.selectionEndPos);
}
std::string RichTextBox::process_cut(Cursor& cur) {
std::string TextBox::process_cut(Cursor& cur) {
std::string toRet = process_copy(cur);
if(cur.selectionBeginPos != cur.selectionEndPos) {
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.selectionBeginPos, cur.selectionEndPos);
@ -152,7 +154,7 @@ std::string RichTextBox::process_cut(Cursor& cur) {
return toRet;
}
void RichTextBox::process_text_input(Cursor& cur, const std::string& in, const std::optional<TextStyleModifier::ModifierMap>& inputModMap) {
void TextBox::process_text_input(Cursor& cur, const std::string& in, const std::optional<TextStyleModifier::ModifierMap>& inputModMap) {
if(!in.empty()) {
if(cur.selectionBeginPos != cur.selectionEndPos)
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.selectionBeginPos, cur.selectionEndPos);
@ -162,7 +164,7 @@ void RichTextBox::process_text_input(Cursor& cur, const std::string& in, const s
}
}
size_t RichTextBox::count_grapheme(const std::string& text) {
size_t TextBox::count_grapheme(const std::string& text) {
auto u = SkUnicodes::ICU::Make();
auto breakIterator = u->makeBreakIterator(SkUnicode::BreakType::kGraphemes);
breakIterator->setText(text.c_str(), text.size());
@ -174,7 +176,7 @@ size_t RichTextBox::count_grapheme(const std::string& text) {
return count - 1;
}
size_t RichTextBox::next_grapheme(const std::string& text, size_t textBytePos) {
size_t TextBox::next_grapheme(const std::string& text, size_t textBytePos) {
if(textBytePos == text.size())
return text.size();
auto u = SkUnicodes::ICU::Make();
@ -184,7 +186,7 @@ size_t RichTextBox::next_grapheme(const std::string& text, size_t textBytePos) {
return count + textBytePos;
}
size_t RichTextBox::prev_grapheme(const std::string& text, size_t textBytePos) {
size_t TextBox::prev_grapheme(const std::string& text, size_t textBytePos) {
if(textBytePos == 0)
return 0;
auto u = SkUnicodes::ICU::Make();
@ -199,7 +201,7 @@ size_t RichTextBox::prev_grapheme(const std::string& text, size_t textBytePos) {
}
}
RichTextBox::TextPosition RichTextBox::get_text_pos_from_byte_pos(const std::string& text, size_t textBytePos) {
TextPosition TextBox::get_text_pos_from_byte_pos(const std::string& text, size_t textBytePos) {
TextPosition toRet{0, 0};
for(size_t i = 0; i < textBytePos; i++) {
char c = text[i];
@ -213,7 +215,7 @@ RichTextBox::TextPosition RichTextBox::get_text_pos_from_byte_pos(const std::str
return toRet;
}
size_t RichTextBox::get_byte_pos_from_text_pos(TextPosition textPos) {
size_t TextBox::get_byte_pos_from_text_pos(TextPosition textPos) {
size_t toRet = 0;
for(size_t pIndex = 0; pIndex < textPos.fParagraphIndex; pIndex++)
toRet += paragraphs[pIndex].text.size() + 1; // Add 1 byte for newline
@ -221,7 +223,7 @@ size_t RichTextBox::get_byte_pos_from_text_pos(TextPosition textPos) {
return toRet;
}
RichTextBox::TextPosition RichTextBox::get_text_pos_closest_to_point(Vector2f point) {
TextPosition TextBox::get_text_pos_closest_to_point(Vector2f point) {
rebuild();
skia::textlayout::Paragraph::GlyphClusterInfo glyphInfo;
@ -267,8 +269,8 @@ RichTextBox::TextPosition RichTextBox::get_text_pos_closest_to_point(Vector2f po
return TextPosition{pIndex, 0};
}
RichTextBox::RichTextData RichTextBox::get_rich_text_data() {
RichTextData toRet;
TextData TextBox::get_rich_text_data() {
TextData toRet;
for(auto& p : paragraphs) {
toRet.paragraphs.emplace_back();
toRet.paragraphs.back().text = p.text;
@ -277,18 +279,18 @@ RichTextBox::RichTextData RichTextBox::get_rich_text_data() {
return toRet;
}
void RichTextBox::clear_text() {
void TextBox::clear_text() {
paragraphs.clear();
tStyleMods.clear();
paragraphs.emplace_back();
}
void RichTextBox::set_string(const std::string& str) {
void TextBox::set_string(const std::string& str) {
clear_text();
insert({0, 0}, str);
}
void RichTextBox::set_rich_text_data(const RichTextData& richText) {
void TextBox::set_rich_text_data(const TextData& richText) {
paragraphs.clear();
for(auto& p : richText.paragraphs) {
paragraphs.emplace_back();
@ -301,11 +303,11 @@ void RichTextBox::set_rich_text_data(const RichTextData& richText) {
needsRebuild = true;
}
std::string RichTextBox::get_string() {
return get_text_between({0, 0}, move(RichTextBox::Movement::END, {0, 0}));
std::string TextBox::get_string() {
return get_text_between({0, 0}, move(TextBox::Movement::END, {0, 0}));
}
std::string RichTextBox::get_text_between(TextPosition p1, TextPosition p2) {
std::string TextBox::get_text_between(TextPosition p1, TextPosition p2) {
p1 = move(Movement::NOWHERE, p1);
p2 = move(Movement::NOWHERE, p2);
@ -329,7 +331,7 @@ std::string RichTextBox::get_text_between(TextPosition p1, TextPosition p2) {
}
void RichTextBox::set_initial_text_style(const skia::textlayout::TextStyle& tStyle) {
void TextBox::set_initial_text_style(const skia::textlayout::TextStyle& tStyle) {
if(!tStyle.equals(initialTStyle)) {
initialTStyle = tStyle;
inputChangedTextBox = true;
@ -337,7 +339,7 @@ void RichTextBox::set_initial_text_style(const skia::textlayout::TextStyle& tSty
}
}
void RichTextBox::set_initial_text_style_modifier(const std::shared_ptr<TextStyleModifier>& modifier) {
void TextBox::set_initial_text_style_modifier(const std::shared_ptr<TextStyleModifier>& modifier) {
if(tStyleMods.empty() || tStyleMods[0].pos != TextPosition{0, 0}) {
if(tStyleMods.empty())
tStyleMods.emplace_back();
@ -349,7 +351,7 @@ void RichTextBox::set_initial_text_style_modifier(const std::shared_ptr<TextStyl
remove_duplicate_text_style_mods();
}
TextStyleModifier::ModifierMap RichTextBox::get_mods_used_at_pos(TextPosition p) {
TextStyleModifier::ModifierMap TextBox::get_mods_used_at_pos(TextPosition p) {
p = move(Movement::NOWHERE, p);
TextStyleModifier::ModifierMap toRet = TextStyleModifier::get_default_modifiers();
@ -366,7 +368,7 @@ TextStyleModifier::ModifierMap RichTextBox::get_mods_used_at_pos(TextPosition p)
return toRet;
}
std::shared_ptr<TextStyleModifier> RichTextBox::get_last_text_style_mod_before_pos(TextPosition pos, TextStyleModifier::ModifierType modType) {
std::shared_ptr<TextStyleModifier> TextBox::get_last_text_style_mod_before_pos(TextPosition pos, TextStyleModifier::ModifierType modType) {
std::shared_ptr<TextStyleModifier> lastModOfThisTypeBeforeEnd = TextStyleModifier::get_default_modifier(modType);
for(auto& [tPos, tStyleModsInPos] : tStyleMods) {
if(tPos <= pos) {
@ -380,7 +382,7 @@ std::shared_ptr<TextStyleModifier> RichTextBox::get_last_text_style_mod_before_p
return lastModOfThisTypeBeforeEnd;
}
void RichTextBox::erase_if_over_all_styles_until_pos(TextPosition pos, const std::function<bool(TextPosition, const std::shared_ptr<TextStyleModifier>&)>& func) {
void TextBox::erase_if_over_all_styles_until_pos(TextPosition pos, const std::function<bool(TextPosition, const std::shared_ptr<TextStyleModifier>&)>& func) {
for(auto& [tPos, tStyleModsInPos] : tStyleMods) {
if(tPos <= pos) {
std::erase_if(tStyleModsInPos, [&](auto& item) {
@ -392,7 +394,7 @@ void RichTextBox::erase_if_over_all_styles_until_pos(TextPosition pos, const std
}
}
void RichTextBox::set_text_style_modifier_between(TextPosition p1, TextPosition p2, const std::shared_ptr<TextStyleModifier>& modifier) {
void TextBox::set_text_style_modifier_between(TextPosition p1, TextPosition p2, const std::shared_ptr<TextStyleModifier>& modifier) {
p1 = move(Movement::NOWHERE, p1);
p2 = move(Movement::NOWHERE, p2);
if(p1 != p2) {
@ -411,7 +413,7 @@ void RichTextBox::set_text_style_modifier_between(TextPosition p1, TextPosition
}
}
void RichTextBox::insert_style_at_pos(TextPosition pos, const std::shared_ptr<TextStyleModifier>& modifier) {
void TextBox::insert_style_at_pos(TextPosition pos, const std::shared_ptr<TextStyleModifier>& modifier) {
size_t indexToPlaceAt = 0;
bool finishedInsertingStyle = false;
for(auto& [tPos, tStyleModsInPos] : tStyleMods) {
@ -432,7 +434,7 @@ void RichTextBox::insert_style_at_pos(TextPosition pos, const std::shared_ptr<Te
}
}
RichTextBox::TextPosition RichTextBox::move(Movement movement, TextPosition pos, std::optional<float>* previousX, bool flipDependingOnTextDirection) {
TextPosition TextBox::move(Movement movement, TextPosition pos, std::optional<float>* previousX, bool flipDependingOnTextDirection) {
if(pos.fParagraphIndex >= paragraphs.size()) {
pos.fParagraphIndex = paragraphs.size() - 1;
pos.fTextByteIndex = paragraphs[pos.fParagraphIndex].text.size();
@ -608,7 +610,7 @@ RichTextBox::TextPosition RichTextBox::move(Movement movement, TextPosition pos,
}
void RichTextBox::set_width(float newWidth) {
void TextBox::set_width(float newWidth) {
if(width != newWidth) {
width = std::max(newWidth, 4.0f);
inputChangedTextBox = true;
@ -616,11 +618,11 @@ void RichTextBox::set_width(float newWidth) {
}
}
void RichTextBox::set_allow_newlines(bool allow) {
void TextBox::set_allow_newlines(bool allow) {
newlinesAllowed = allow;
}
void RichTextBox::set_font_collection(const sk_sp<skia::textlayout::FontCollection>& fC) {
void TextBox::set_font_collection(const sk_sp<skia::textlayout::FontCollection>& fC) {
if(fontCollection != fC) {
fontCollection = fC;
inputChangedTextBox = true;
@ -628,7 +630,7 @@ void RichTextBox::set_font_collection(const sk_sp<skia::textlayout::FontCollecti
}
}
void RichTextBox::rebuild() {
void TextBox::rebuild() {
if(needsRebuild) {
float heightOffset = 0.0f;
@ -683,7 +685,7 @@ void RichTextBox::rebuild() {
}
}
void RichTextBox::remove_duplicate_text_style_mods() {
void TextBox::remove_duplicate_text_style_mods() {
TextStyleModifier::ModifierMap previouslyAppliedMods = TextStyleModifier::get_default_modifiers();
std::erase_if(tStyleMods, [&previouslyAppliedMods](PositionedTextStyleMod& tStyleModsInPos) {
@ -700,11 +702,11 @@ void RichTextBox::remove_duplicate_text_style_mods() {
});
}
void RichTextBox::set_tab_space_width(unsigned newTabWidth) {
void TextBox::set_tab_space_width(unsigned newTabWidth) {
tabWidth = newTabWidth;
}
RichTextBox::TextPosition RichTextBox::insert(TextPosition pos, std::string_view textToInsert, const std::optional<TextStyleModifier::ModifierMap>& inputModMap) {
TextPosition TextBox::insert(TextPosition pos, std::string_view textToInsert, const std::optional<TextStyleModifier::ModifierMap>& inputModMap) {
TextPosition oldPos = pos = move(Movement::NOWHERE, pos);
for(char c : textToInsert) {
@ -756,7 +758,7 @@ RichTextBox::TextPosition RichTextBox::insert(TextPosition pos, std::string_view
return pos;
}
RichTextBox::TextPosition RichTextBox::remove(TextPosition p1, TextPosition p2) {
TextPosition TextBox::remove(TextPosition p1, TextPosition p2) {
p1 = move(Movement::NOWHERE, p1);
p2 = move(Movement::NOWHERE, p2);
@ -807,7 +809,7 @@ RichTextBox::TextPosition RichTextBox::remove(TextPosition p1, TextPosition p2)
return start;
}
SkRect RichTextBox::get_cursor_rect(TextPosition pos) {
SkRect TextBox::get_cursor_rect(TextPosition pos) {
pos = move(Movement::NOWHERE, pos);
SkPoint topPoint;
@ -853,7 +855,7 @@ SkRect RichTextBox::get_cursor_rect(TextPosition pos) {
return SkRect::MakeXYWH(topPoint.x() - CURSOR_WIDTH * 0.5f, topPoint.y(), CURSOR_WIDTH, height);
}
void RichTextBox::rects_between_text_positions_func(TextPosition p1, TextPosition p2, std::function<void(const SkRect& r)> f) {
void TextBox::rects_between_text_positions_func(TextPosition p1, TextPosition p2, std::function<void(const SkRect& r)> f) {
p1 = move(Movement::NOWHERE, p1);
p2 = move(Movement::NOWHERE, p2);
@ -882,7 +884,7 @@ void RichTextBox::rects_between_text_positions_func(TextPosition p1, TextPositio
}
}
void RichTextBox::paint(SkCanvas* canvas, const PaintOpts& paintOpts) {
void TextBox::paint(SkCanvas* canvas, const PaintOpts& paintOpts) {
rebuild();
if(paintOpts.cursor.has_value()) {
@ -901,3 +903,5 @@ void RichTextBox::paint(SkCanvas* canvas, const PaintOpts& paintOpts) {
for(auto& pData : paragraphs)
pData.p->paint(canvas, 0.0f, pData.heightOffset);
}
}

View file

@ -9,9 +9,45 @@
#include "TextStyleModifier.hpp"
#include "cereal/archives/portable_binary.hpp"
class RichTextBox {
namespace RichText {
struct TextPosition {
// Set to uint32_t values because theyll be saved and sent over networks. Size has to be consistent
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)fParagraphIndex, (uint32_t)fTextByteIndex);
}
bool operator==(const TextPosition& o) const = default;
bool operator<(const TextPosition& o) const;
bool operator>(const TextPosition& o) const;
bool operator>=(const TextPosition& o) const;
bool operator<=(const TextPosition& o) const;
};
struct PositionedTextStyleMod {
TextPosition pos;
TextStyleModifier::ModifierMap mods;
};
typedef std::vector<PositionedTextStyleMod> TextStyleModContainer;
struct TextData {
struct Paragraph {
std::string text;
template<typename Archive> void serialize(Archive& a) {
a(text);
}
};
void save(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);
TextStyleModContainer tStyleMods;
std::vector<Paragraph> paragraphs;
};
class TextBox {
public:
RichTextBox();
TextBox();
bool inputChangedTextBox = false;
@ -27,27 +63,6 @@ class RichTextBox {
END
};
struct TextPosition {
// Set to uint32_t values because theyll be saved and sent over networks. Size has to be consistent
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)fParagraphIndex, (uint32_t)fTextByteIndex);
}
bool operator==(const TextPosition& o) const = default;
bool operator<(const TextPosition& o) const;
bool operator>(const TextPosition& o) const;
bool operator>=(const RichTextBox::TextPosition& o) const;
bool operator<=(const RichTextBox::TextPosition& o) const;
};
struct PositionedTextStyleMod {
TextPosition pos;
TextStyleModifier::ModifierMap mods;
};
typedef std::vector<PositionedTextStyleMod> TextStyleModContainer;
struct Cursor {
TextPosition pos = {0, 0};
TextPosition selectionBeginPos = {0, 0};
@ -65,19 +80,6 @@ class RichTextBox {
std::optional<Cursor> cursor;
};
struct RichTextData {
struct Paragraph {
std::string text;
template<typename Archive> void serialize(Archive& a) {
a(text);
}
};
void save(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);
TextStyleModContainer tStyleMods;
std::vector<Paragraph> paragraphs;
};
TextPosition move(Movement movement, TextPosition pos, std::optional<float>* previousX = nullptr, bool flipDependingOnTextDirection = false);
void set_width(float newWidth);
TextPosition insert(TextPosition pos, std::string_view textToInsert, const std::optional<TextStyleModifier::ModifierMap>& inputModMap = std::nullopt);
@ -105,8 +107,8 @@ class RichTextBox {
void set_text_style_modifier_between(TextPosition p1, TextPosition p2, const std::shared_ptr<TextStyleModifier>& modifier);
TextStyleModifier::ModifierMap get_mods_used_at_pos(TextPosition p);
RichTextData get_rich_text_data();
void set_rich_text_data(const RichTextData& richText);
TextData get_rich_text_data();
void set_rich_text_data(const TextData& richText);
void clear_text();
void set_string(const std::string& str);
@ -157,3 +159,5 @@ class RichTextBox {
TextStyleModContainer tStyleMods;
std::vector<ParagraphData> paragraphs;
};
}

View file

@ -6,6 +6,8 @@
#define WEIGHT_VALUE_MODIFIER 100
namespace RichText {
TextStyleModifier::ModifierMap TextStyleModifier::defaultMods;
std::shared_ptr<TextStyleModifier> TextStyleModifier::allocate_modifier(ModifierType type) {
@ -223,3 +225,5 @@ void DecorationTextStyleModifier::modify_text_style(skia::textlayout::TextStyle&
bool DecorationTextStyleModifier::equivalent_data(TextStyleModifier& modifier) const {
return decorationValue == static_cast<DecorationTextStyleModifier&>(modifier).decorationValue;
}
}

View file

@ -5,6 +5,8 @@
#include "cereal/archives/portable_binary.hpp"
#include "../SharedTypes.hpp"
namespace RichText {
class TextStyleModifier {
public:
enum class ModifierType : uint16_t {
@ -117,3 +119,5 @@ class FontFamiliesTextStyleModifier : public TextStyleModifier {
std::vector<SkString> families;
virtual bool equivalent_data(TextStyleModifier& modifier) const override;
};
}

View file

@ -1915,7 +1915,7 @@ void Toolbar::initialize_io_before_update() {
io->key.paste = main.input.get_clipboard_paste_happened();
io->key.cut = main.input.key(InputManager::KEY_TEXT_CUT).repeat;
io->key.escape = main.input.key(InputManager::KEY_GENERIC_ESCAPE).repeat;
io->richTextBoxEdit = [&](std::shared_ptr<RichTextBox> t, std::shared_ptr<RichTextBox::Cursor> c) {
io->richTextBoxEdit = [&](std::shared_ptr<RichText::TextBox> t, std::shared_ptr<RichText::TextBox::Cursor> c) {
main.input.text.set_rich_text_box_input(t, c);
};