2025-10-16 21:22:08 +03:00
|
|
|
#pragma once
|
|
|
|
|
#include <cstdint>
|
2025-11-05 12:29:31 +03:00
|
|
|
#include <cwchar>
|
2025-10-16 21:22:08 +03:00
|
|
|
#include <include/core/SkCanvas.h>
|
|
|
|
|
#include <modules/skparagraph/include/FontCollection.h>
|
|
|
|
|
#include <modules/skparagraph/include/Paragraph.h>
|
2025-11-04 17:31:23 +03:00
|
|
|
#include <modules/skparagraph/include/TextStyle.h>
|
2025-10-16 21:22:08 +03:00
|
|
|
#include "../SharedTypes.hpp"
|
2025-11-04 10:58:26 +03:00
|
|
|
#include "TextStyleModifier.hpp"
|
|
|
|
|
#include "cereal/archives/portable_binary.hpp"
|
2025-10-16 21:22:08 +03:00
|
|
|
|
|
|
|
|
class RichTextBox {
|
|
|
|
|
public:
|
|
|
|
|
RichTextBox();
|
|
|
|
|
|
2025-11-05 12:29:31 +03:00
|
|
|
bool inputChangedTextBox = false;
|
|
|
|
|
|
2025-10-16 21:22:08 +03:00
|
|
|
enum class Movement {
|
2025-10-19 15:54:00 +03:00
|
|
|
NOWHERE,
|
2025-10-16 21:22:08 +03:00
|
|
|
LEFT,
|
|
|
|
|
RIGHT,
|
|
|
|
|
UP,
|
|
|
|
|
DOWN,
|
2025-10-18 13:32:12 +03:00
|
|
|
LEFT_WORD,
|
|
|
|
|
RIGHT_WORD,
|
2025-10-16 21:22:08 +03:00
|
|
|
HOME,
|
|
|
|
|
END
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-18 13:32:12 +03:00
|
|
|
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();
|
2025-11-04 17:31:23 +03:00
|
|
|
size_t fTextByteIndex = std::numeric_limits<uint32_t>::max();
|
2025-10-18 13:32:12 +03:00
|
|
|
template <typename Archive> void serialize(Archive& a) {
|
2025-11-04 17:31:23 +03:00
|
|
|
a((uint32_t)fParagraphIndex, (uint32_t)fTextByteIndex);
|
2025-10-18 13:32:12 +03:00
|
|
|
}
|
|
|
|
|
bool operator==(const TextPosition& o) const = default;
|
|
|
|
|
bool operator<(const TextPosition& o) const;
|
2025-11-04 17:31:23 +03:00
|
|
|
bool operator>(const TextPosition& o) const;
|
2025-11-04 19:04:09 +03:00
|
|
|
bool operator>=(const RichTextBox::TextPosition& o) const;
|
|
|
|
|
bool operator<=(const RichTextBox::TextPosition& o) const;
|
2025-11-04 17:31:23 +03:00
|
|
|
};
|
|
|
|
|
|
2025-11-04 22:30:43 +03:00
|
|
|
struct PositionedTextStyleMod {
|
|
|
|
|
TextPosition pos;
|
2025-11-05 12:29:31 +03:00
|
|
|
TextStyleModifier::ModifierMap mods;
|
2025-10-18 13:32:12 +03:00
|
|
|
};
|
|
|
|
|
|
2025-11-04 22:30:43 +03:00
|
|
|
typedef std::vector<PositionedTextStyleMod> TextStyleModContainer;
|
|
|
|
|
|
2025-10-16 21:22:08 +03:00
|
|
|
struct Cursor {
|
2025-10-18 13:32:12 +03:00
|
|
|
TextPosition pos = {0, 0};
|
|
|
|
|
TextPosition selectionBeginPos = {0, 0};
|
|
|
|
|
TextPosition selectionEndPos = {0, 0};
|
2025-10-20 17:30:36 +03:00
|
|
|
template <typename Archive> void serialize(Archive& a) {
|
|
|
|
|
a(pos, selectionBeginPos, selectionEndPos);
|
|
|
|
|
}
|
2025-10-18 13:32:12 +03:00
|
|
|
std::optional<float> previousX;
|
2025-11-05 12:29:31 +03:00
|
|
|
bool operator==(const Cursor& o) const = default;
|
|
|
|
|
bool operator!=(const Cursor& o) const = default;
|
2025-10-16 21:22:08 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct PaintOpts {
|
2025-10-18 13:32:12 +03:00
|
|
|
Vector3f cursorColor = {1, 0, 0};
|
2025-10-16 21:22:08 +03:00
|
|
|
std::optional<Cursor> cursor;
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-25 17:31:55 +03:00
|
|
|
struct RichTextData {
|
2025-11-04 10:58:26 +03:00
|
|
|
struct Paragraph {
|
|
|
|
|
std::string text;
|
2025-11-04 17:31:23 +03:00
|
|
|
template<typename Archive> void serialize(Archive& a) {
|
|
|
|
|
a(text);
|
|
|
|
|
}
|
2025-11-04 10:58:26 +03:00
|
|
|
};
|
2025-11-04 22:30:43 +03:00
|
|
|
void save(cereal::PortableBinaryOutputArchive& a) const;
|
|
|
|
|
void load(cereal::PortableBinaryInputArchive& a);
|
|
|
|
|
TextStyleModContainer tStyleMods;
|
2025-11-04 10:58:26 +03:00
|
|
|
std::vector<Paragraph> paragraphs;
|
2025-10-25 17:31:55 +03:00
|
|
|
};
|
|
|
|
|
|
2025-10-20 11:41:22 +03:00
|
|
|
TextPosition move(Movement movement, TextPosition pos, std::optional<float>* previousX = nullptr, bool flipDependingOnTextDirection = false);
|
2025-10-16 21:22:08 +03:00
|
|
|
void set_width(float newWidth);
|
2025-11-09 16:24:40 +03:00
|
|
|
TextPosition insert(TextPosition pos, std::string_view textToInsert, const std::optional<TextStyleModifier::ModifierMap>& inputModMap = std::nullopt);
|
2025-10-19 15:54:00 +03:00
|
|
|
TextPosition remove(TextPosition p1, TextPosition p2);
|
2025-10-16 21:22:08 +03:00
|
|
|
void set_font_collection(const sk_sp<skia::textlayout::FontCollection>& fC);
|
|
|
|
|
void paint(SkCanvas* canvas, const PaintOpts& paintOpts);
|
|
|
|
|
void set_allow_newlines(bool allow);
|
|
|
|
|
|
|
|
|
|
enum class InputKey {
|
|
|
|
|
LEFT,
|
|
|
|
|
RIGHT,
|
|
|
|
|
UP,
|
|
|
|
|
DOWN,
|
|
|
|
|
HOME,
|
|
|
|
|
END,
|
|
|
|
|
BACKSPACE,
|
|
|
|
|
DELETE,
|
|
|
|
|
ENTER,
|
2025-10-20 17:30:36 +03:00
|
|
|
TAB,
|
2025-10-16 21:22:08 +03:00
|
|
|
SELECT_ALL
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-04 17:31:23 +03:00
|
|
|
void set_initial_text_style(const skia::textlayout::TextStyle& tStyle);
|
2025-11-04 22:30:43 +03:00
|
|
|
void set_initial_text_style_modifier(const std::shared_ptr<TextStyleModifier>& modifier);
|
2025-11-04 10:58:26 +03:00
|
|
|
void set_text_style_modifier_between(TextPosition p1, TextPosition p2, const std::shared_ptr<TextStyleModifier>& modifier);
|
2025-11-05 12:29:31 +03:00
|
|
|
TextStyleModifier::ModifierMap get_mods_used_at_pos(TextPosition p);
|
2025-11-04 10:58:26 +03:00
|
|
|
|
2025-10-25 17:31:55 +03:00
|
|
|
RichTextData get_rich_text_data();
|
|
|
|
|
void set_rich_text_data(const RichTextData& richText);
|
|
|
|
|
void clear_text();
|
|
|
|
|
void set_string(const std::string& str);
|
|
|
|
|
|
2025-10-18 13:32:12 +03:00
|
|
|
void process_mouse_left_button(Cursor& cur, const Vector2f& pos, bool clicked, bool held, bool shift);
|
2025-11-09 16:24:40 +03:00
|
|
|
void process_key_input(Cursor& cur, InputKey in, bool ctrl, bool shift, const std::optional<TextStyleModifier::ModifierMap>& inputModMap = std::nullopt);
|
2025-10-16 21:22:08 +03:00
|
|
|
std::string process_copy(Cursor& cur);
|
|
|
|
|
std::string process_cut(Cursor& cur);
|
2025-10-19 15:54:00 +03:00
|
|
|
std::string get_text_between(TextPosition p1, TextPosition p2);
|
2025-10-20 17:30:36 +03:00
|
|
|
std::string get_string();
|
|
|
|
|
void set_tab_space_width(unsigned newTabWidth);
|
2025-11-09 16:24:40 +03:00
|
|
|
void process_text_input(Cursor& cur, const std::string& in, const std::optional<TextStyleModifier::ModifierMap>& inputModMap = std::nullopt);
|
2025-10-20 17:30:36 +03:00
|
|
|
|
2025-10-16 21:22:08 +03:00
|
|
|
private:
|
2025-10-19 15:54:00 +03:00
|
|
|
static size_t count_grapheme(const std::string& text);
|
|
|
|
|
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);
|
2025-11-04 10:58:26 +03:00
|
|
|
|
2025-10-19 15:54:00 +03:00
|
|
|
size_t get_byte_pos_from_text_pos(TextPosition textPos);
|
2025-10-18 13:32:12 +03:00
|
|
|
|
2025-10-19 15:54:00 +03:00
|
|
|
void rects_between_text_positions_func(TextPosition p1, TextPosition p2, std::function<void(const SkRect& r)> f);
|
|
|
|
|
|
2025-10-18 13:32:12 +03:00
|
|
|
SkRect get_cursor_rect(TextPosition pos);
|
|
|
|
|
|
2025-10-19 15:54:00 +03:00
|
|
|
TextPosition get_text_pos_closest_to_point(Vector2f point);
|
2025-11-04 22:30:43 +03:00
|
|
|
std::shared_ptr<TextStyleModifier> get_last_text_style_mod_before_pos(TextPosition pos, TextStyleModifier::ModifierType modType);
|
|
|
|
|
void erase_if_over_all_styles_until_pos(TextPosition pos, const std::function<bool(TextPosition, const std::shared_ptr<TextStyleModifier>&)>& func);
|
|
|
|
|
void insert_style_at_pos(TextPosition pos, const std::shared_ptr<TextStyleModifier>& modifier);
|
2025-10-16 21:22:08 +03:00
|
|
|
|
2025-11-05 08:51:52 +03:00
|
|
|
void remove_duplicate_text_style_mods();
|
|
|
|
|
|
2025-10-16 21:22:08 +03:00
|
|
|
void rebuild();
|
|
|
|
|
|
|
|
|
|
bool newlinesAllowed = true;
|
2025-10-18 13:32:12 +03:00
|
|
|
float width = 0.0f;
|
2025-10-16 21:22:08 +03:00
|
|
|
bool needsRebuild = true;
|
2025-10-20 17:30:36 +03:00
|
|
|
unsigned tabWidth = 8;
|
2025-10-16 21:22:08 +03:00
|
|
|
sk_sp<skia::textlayout::FontCollection> fontCollection;
|
2025-10-19 15:54:00 +03:00
|
|
|
|
|
|
|
|
struct ParagraphData {
|
|
|
|
|
std::string text;
|
|
|
|
|
std::unique_ptr<skia::textlayout::Paragraph> p;
|
|
|
|
|
float heightOffset;
|
2025-10-20 11:41:22 +03:00
|
|
|
skia::textlayout::ParagraphStyle pStyle;
|
2025-10-19 15:54:00 +03:00
|
|
|
};
|
|
|
|
|
|
2025-11-04 17:31:23 +03:00
|
|
|
skia::textlayout::TextStyle initialTStyle;
|
2025-11-04 22:30:43 +03:00
|
|
|
TextStyleModContainer tStyleMods;
|
2025-10-19 15:54:00 +03:00
|
|
|
std::vector<ParagraphData> paragraphs;
|
2025-10-16 21:22:08 +03:00
|
|
|
};
|