Can now toggle some modifiers
This commit is contained in:
parent
fe936b6c25
commit
4857b40af3
7 changed files with 113 additions and 24 deletions
|
|
@ -14,20 +14,19 @@ DrawTextBox::DrawTextBox():
|
|||
cursor(std::make_shared<RichTextBox::Cursor>())
|
||||
{
|
||||
skia::textlayout::TextStyle tStyle;
|
||||
tStyle.setFontFamilies({SkString{"Roboto"}});
|
||||
tStyle.setFontSize(16.0f);
|
||||
tStyle.setForegroundPaint(SkPaint{SkColor4f{0.0f, 1.0f, 1.0f, 1.0f}});
|
||||
textBox->set_initial_text_style(tStyle);
|
||||
|
||||
auto boldMod = std::make_shared<WeightTextStyleModifier>();
|
||||
boldMod->set_weight(SkFontStyle::kNormal_Weight);
|
||||
textBox->set_initial_text_style_modifier(boldMod);
|
||||
auto italicMod = std::make_shared<SlantTextStyleModifier>();
|
||||
italicMod->set_slant(SkFontStyle::Slant::kUpright_Slant);
|
||||
textBox->set_initial_text_style_modifier(italicMod);
|
||||
auto colorMod = std::make_shared<ColorTextStyleModifier>();
|
||||
colorMod->color = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||
textBox->set_initial_text_style_modifier(colorMod);
|
||||
|
||||
auto textSizeMod = std::make_shared<SizeTextStyleModifier>();
|
||||
textSizeMod->size = 16.0f;
|
||||
textBox->set_initial_text_style_modifier(textSizeMod);
|
||||
|
||||
auto fontFamilyMod = std::make_shared<FontFamiliesTextStyleModifier>();
|
||||
fontFamilyMod->families = {SkString{"Roboto"}};
|
||||
textBox->set_initial_text_style_modifier(fontFamilyMod);
|
||||
}
|
||||
|
||||
DrawComponentType DrawTextBox::get_type() const {
|
||||
|
|
|
|||
|
|
@ -15,16 +15,20 @@ TextBoxEditTool::TextBoxEditTool(DrawingProgram& 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;
|
||||
|
||||
t.gui.push_id("edit tool text");
|
||||
t.gui.text_label_centered("Edit Text");
|
||||
if(t.gui.text_button_wide("Bold button", "Bold")) {
|
||||
|
||||
bool isBold = std::static_pointer_cast<WeightTextStyleModifier>(modsAtStartOfSelection[TextStyleModifier::ModifierType::WEIGHT])->get_weight() == SkFontStyle::Weight::kBold_Weight;
|
||||
if(t.gui.text_button_wide("Bold button", "Bold", isBold)) {
|
||||
auto boldMod = std::make_shared<WeightTextStyleModifier>();
|
||||
boldMod->set_weight(SkFontStyle::kBold_Weight);
|
||||
boldMod->set_weight(isBold ? SkFontStyle::Weight::kNormal_Weight : SkFontStyle::Weight::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")) {
|
||||
bool isItalic = std::static_pointer_cast<SlantTextStyleModifier>(modsAtStartOfSelection[TextStyleModifier::ModifierType::SLANT])->get_slant() == SkFontStyle::Slant::kItalic_Slant;
|
||||
if(t.gui.text_button_wide("Italic button", "Italic", isItalic)) {
|
||||
auto italicMod = std::make_shared<SlantTextStyleModifier>();
|
||||
italicMod->set_slant(SkFontStyle::Slant::kItalic_Slant);
|
||||
italicMod->set_slant(isItalic ? SkFontStyle::Slant::kUpright_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")) {
|
||||
|
|
@ -51,7 +55,13 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
|
|||
//});
|
||||
t.gui.pop_id();
|
||||
|
||||
return true; // NOTE: In general, the update data should always be sent even if no changes were made, because we're sending the data over an unreliable channel.
|
||||
if(a->textBox->inputChangedTextBox)
|
||||
modsAtStartOfSelection = a->textBox->get_mods_used_at_pos(std::min(a->cursor->selectionBeginPos, a->cursor->selectionEndPos));
|
||||
|
||||
// NOTE: There should be a system that periodically sends updates even if no changes are made, since unreliable channels can drop update data
|
||||
bool oldInputChangedTextBox = a->textBox->inputChangedTextBox;
|
||||
a->textBox->inputChangedTextBox = false;
|
||||
return oldInputChangedTextBox;
|
||||
}
|
||||
|
||||
void TextBoxEditTool::commit_edit_updates(const std::shared_ptr<DrawComponent>& comp, std::any& prevData) {
|
||||
|
|
@ -97,7 +107,13 @@ void TextBoxEditTool::edit_start(EditTool& editTool, const std::shared_ptr<DrawC
|
|||
textbox->process_mouse_left_button(*cur, textSelectPos, true, false, false);
|
||||
prevData = get_all_data(a);
|
||||
a->d.editing = true;
|
||||
a->textBox->inputChangedTextBox = true;
|
||||
|
||||
a->commit_update(drawP);
|
||||
a->client_send_update(drawP, false);
|
||||
|
||||
modsAtStartOfSelection = a->textBox->get_mods_used_at_pos(std::min(a->cursor->selectionBeginPos, a->cursor->selectionEndPos));
|
||||
|
||||
editTool.add_point_handle({&a->d.p1, nullptr, &a->d.p2});
|
||||
editTool.add_point_handle({&a->d.p2, &a->d.p1, nullptr});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,5 +19,8 @@ class TextBoxEditTool : public DrawingProgramEditToolBase {
|
|||
DrawTextBox::Data textboxData;
|
||||
RichTextBox::RichTextData richText;
|
||||
};
|
||||
|
||||
TextStyleModifier::ModifierMap modsAtStartOfSelection;
|
||||
|
||||
TextBoxEditToolAllData get_all_data(const std::shared_ptr<DrawTextBox>& a);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -118,17 +118,23 @@ void RichTextBox::process_key_input(Cursor& cur, InputKey in, bool ctrl, bool sh
|
|||
|
||||
if(in != InputKey::UP && in != InputKey::DOWN)
|
||||
cur.previousX = std::nullopt;
|
||||
|
||||
inputChangedTextBox = true;
|
||||
}
|
||||
|
||||
void RichTextBox::process_mouse_left_button(Cursor& cur, const Vector2f& pos, bool clicked, bool held, bool shift) {
|
||||
if(clicked || held) {
|
||||
rebuild();
|
||||
|
||||
Cursor oldCursor = cur;
|
||||
|
||||
cur.pos = get_text_pos_closest_to_point(pos);
|
||||
cur.selectionBeginPos = cur.pos;
|
||||
if(clicked && !shift)
|
||||
cur.selectionEndPos = cur.selectionBeginPos;
|
||||
cur.previousX = std::nullopt;
|
||||
|
||||
inputChangedTextBox |= (oldCursor != cur);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -141,6 +147,7 @@ std::string RichTextBox::process_cut(Cursor& cur) {
|
|||
if(cur.selectionBeginPos != cur.selectionEndPos) {
|
||||
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.selectionBeginPos, cur.selectionEndPos);
|
||||
cur.previousX = std::nullopt;
|
||||
inputChangedTextBox = true;
|
||||
}
|
||||
return toRet;
|
||||
}
|
||||
|
|
@ -151,6 +158,7 @@ void RichTextBox::process_text_input(Cursor& cur, const std::string& in) {
|
|||
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = remove(cur.selectionBeginPos, cur.selectionEndPos);
|
||||
cur.selectionEndPos = cur.selectionBeginPos = cur.pos = insert(cur.pos, in);
|
||||
cur.previousX = std::nullopt;
|
||||
inputChangedTextBox = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,6 +297,7 @@ void RichTextBox::set_rich_text_data(const RichTextData& richText) {
|
|||
if(richText.paragraphs.empty())
|
||||
paragraphs.emplace_back();
|
||||
tStyleMods = richText.tStyleMods;
|
||||
inputChangedTextBox = true;
|
||||
needsRebuild = true;
|
||||
}
|
||||
|
||||
|
|
@ -323,6 +332,7 @@ std::string RichTextBox::get_text_between(TextPosition p1, TextPosition p2) {
|
|||
void RichTextBox::set_initial_text_style(const skia::textlayout::TextStyle& tStyle) {
|
||||
if(!tStyle.equals(initialTStyle)) {
|
||||
initialTStyle = tStyle;
|
||||
inputChangedTextBox = true;
|
||||
needsRebuild = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -339,8 +349,25 @@ 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) {
|
||||
p = move(Movement::NOWHERE, p);
|
||||
|
||||
TextStyleModifier::ModifierMap toRet = TextStyleModifier::get_default_modifiers();
|
||||
|
||||
for(auto& [pos, tStyleModInPos] : tStyleMods) {
|
||||
if(pos <= p) {
|
||||
for(auto& [modType, tStyle] : tStyleModInPos)
|
||||
toRet[modType] = tStyle;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
std::shared_ptr<TextStyleModifier> RichTextBox::get_last_text_style_mod_before_pos(TextPosition pos, TextStyleModifier::ModifierType modType) {
|
||||
std::shared_ptr<TextStyleModifier> lastModOfThisTypeBeforeEnd;
|
||||
std::shared_ptr<TextStyleModifier> lastModOfThisTypeBeforeEnd = TextStyleModifier::get_default_modifier(modType);
|
||||
for(auto& [tPos, tStyleModsInPos] : tStyleMods) {
|
||||
if(tPos <= pos) {
|
||||
auto containedOfSameType = tStyleModsInPos.find(modType);
|
||||
|
|
@ -377,9 +404,9 @@ void RichTextBox::set_text_style_modifier_between(TextPosition p1, TextPosition
|
|||
return p >= start && modifier->get_type() == modToCheck->get_type();
|
||||
});
|
||||
insert_style_at_pos(start, modifier);
|
||||
if(end != move(Movement::END, {0, 0}))
|
||||
insert_style_at_pos(end, lastModOfThisTypeBeforeEnd);
|
||||
insert_style_at_pos(end, lastModOfThisTypeBeforeEnd); // Even if the end is the literal end of the text (which wont be seen), the end style must still be placed at the end so that it can merge with and delete the start style when the styled text is erased
|
||||
remove_duplicate_text_style_mods();
|
||||
inputChangedTextBox = true;
|
||||
needsRebuild = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -584,6 +611,7 @@ RichTextBox::TextPosition RichTextBox::move(Movement movement, TextPosition pos,
|
|||
void RichTextBox::set_width(float newWidth) {
|
||||
if(width != newWidth) {
|
||||
width = newWidth;
|
||||
inputChangedTextBox = true;
|
||||
needsRebuild = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -595,6 +623,7 @@ void RichTextBox::set_allow_newlines(bool allow) {
|
|||
void RichTextBox::set_font_collection(const sk_sp<skia::textlayout::FontCollection>& fC) {
|
||||
if(fontCollection != fC) {
|
||||
fontCollection = fC;
|
||||
inputChangedTextBox = true;
|
||||
needsRebuild = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -646,16 +675,17 @@ void RichTextBox::rebuild() {
|
|||
heightOffset += pData.p->getHeight();
|
||||
}
|
||||
|
||||
//std::cout << "size: " << tStyleMods.size() << std::endl;
|
||||
//for(auto& s : tStyleMods)
|
||||
// std::cout << s.pos.fParagraphIndex << " " << s.pos.fTextByteIndex << std::endl;
|
||||
std::cout << "size: " << tStyleMods.size() << std::endl;
|
||||
for(auto& s : tStyleMods)
|
||||
std::cout << s.pos.fParagraphIndex << " " << s.pos.fTextByteIndex << std::endl;
|
||||
|
||||
needsRebuild = false;
|
||||
}
|
||||
}
|
||||
|
||||
void RichTextBox::remove_duplicate_text_style_mods() {
|
||||
std::unordered_map<TextStyleModifier::ModifierType, std::shared_ptr<TextStyleModifier>> previouslyAppliedMods;
|
||||
TextStyleModifier::ModifierMap previouslyAppliedMods = TextStyleModifier::get_default_modifiers();
|
||||
|
||||
std::erase_if(tStyleMods, [&previouslyAppliedMods](PositionedTextStyleMod& tStyleModsInPos) {
|
||||
std::erase_if(tStyleModsInPos.mods, [&previouslyAppliedMods](const std::pair<TextStyleModifier::ModifierType, std::shared_ptr<TextStyleModifier>>& tStyleModPair) {
|
||||
auto [placedIt, insertionTookPlace] = previouslyAppliedMods.emplace(tStyleModPair.first, tStyleModPair.second);
|
||||
|
|
@ -715,6 +745,7 @@ RichTextBox::TextPosition RichTextBox::insert(TextPosition pos, std::string_view
|
|||
|
||||
if(!textToInsert.empty()) {
|
||||
remove_duplicate_text_style_mods();
|
||||
inputChangedTextBox = true;
|
||||
needsRebuild = true;
|
||||
}
|
||||
|
||||
|
|
@ -766,6 +797,7 @@ RichTextBox::TextPosition RichTextBox::remove(TextPosition p1, TextPosition p2)
|
|||
}
|
||||
|
||||
remove_duplicate_text_style_mods();
|
||||
inputChangedTextBox = true;
|
||||
needsRebuild = true;
|
||||
|
||||
return start;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cwchar>
|
||||
#include <include/core/SkCanvas.h>
|
||||
#include <modules/skparagraph/include/FontCollection.h>
|
||||
#include <modules/skparagraph/include/Paragraph.h>
|
||||
|
|
@ -10,9 +11,10 @@
|
|||
|
||||
class RichTextBox {
|
||||
public:
|
||||
|
||||
RichTextBox();
|
||||
|
||||
bool inputChangedTextBox = false;
|
||||
|
||||
enum class Movement {
|
||||
NOWHERE,
|
||||
LEFT,
|
||||
|
|
@ -41,7 +43,7 @@ class RichTextBox {
|
|||
|
||||
struct PositionedTextStyleMod {
|
||||
TextPosition pos;
|
||||
std::unordered_map<TextStyleModifier::ModifierType, std::shared_ptr<TextStyleModifier>> mods;
|
||||
TextStyleModifier::ModifierMap mods;
|
||||
};
|
||||
|
||||
typedef std::vector<PositionedTextStyleMod> TextStyleModContainer;
|
||||
|
|
@ -54,6 +56,8 @@ class RichTextBox {
|
|||
a(pos, selectionBeginPos, selectionEndPos);
|
||||
}
|
||||
std::optional<float> previousX;
|
||||
bool operator==(const Cursor& o) const = default;
|
||||
bool operator!=(const Cursor& o) const = default;
|
||||
};
|
||||
|
||||
struct PaintOpts {
|
||||
|
|
@ -99,6 +103,7 @@ class RichTextBox {
|
|||
void set_initial_text_style(const skia::textlayout::TextStyle& tStyle);
|
||||
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);
|
||||
TextStyleModifier::ModifierMap get_mods_used_at_pos(TextPosition p);
|
||||
|
||||
RichTextData get_rich_text_data();
|
||||
void set_rich_text_data(const RichTextData& richText);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
#include "TextStyleModifier.hpp"
|
||||
#include <Helpers/ConvertVec.hpp>
|
||||
#include <Helpers/Serializers.hpp>
|
||||
#include <include/core/SkFontStyle.h>
|
||||
|
||||
#define WEIGHT_VALUE_MODIFIER 100
|
||||
|
||||
TextStyleModifier::ModifierMap TextStyleModifier::defaultMods;
|
||||
|
||||
std::shared_ptr<TextStyleModifier> TextStyleModifier::allocate_modifier(ModifierType type) {
|
||||
switch(type) {
|
||||
case ModifierType::SIZE:
|
||||
|
|
@ -16,10 +19,33 @@ std::shared_ptr<TextStyleModifier> TextStyleModifier::allocate_modifier(Modifier
|
|||
return std::make_shared<SlantTextStyleModifier>();
|
||||
case ModifierType::FONT_FAMILIES:
|
||||
return std::make_shared<FontFamiliesTextStyleModifier>();
|
||||
case ModifierType::COUNT:
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<TextStyleModifier> TextStyleModifier::get_default_modifier(ModifierType type) {
|
||||
auto& defaultMods = get_default_modifiers();
|
||||
auto it = defaultMods.find(type);
|
||||
if(it == defaultMods.end())
|
||||
return nullptr;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const TextStyleModifier::ModifierMap& TextStyleModifier::get_default_modifiers() {
|
||||
if(defaultMods.empty()) {
|
||||
auto weightMod = std::make_shared<WeightTextStyleModifier>();
|
||||
weightMod->set_weight(SkFontStyle::kNormal_Weight);
|
||||
defaultMods[ModifierType::WEIGHT] = weightMod;
|
||||
|
||||
auto slantMod = std::make_shared<SlantTextStyleModifier>();
|
||||
slantMod->set_slant(SkFontStyle::kUpright_Slant);
|
||||
defaultMods[ModifierType::SLANT] = slantMod;
|
||||
}
|
||||
return defaultMods;
|
||||
}
|
||||
|
||||
bool TextStyleModifier::equivalent(TextStyleModifier& modifier) const {
|
||||
if(modifier.get_type() != get_type())
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -12,9 +12,16 @@ class TextStyleModifier {
|
|||
COLOR,
|
||||
WEIGHT,
|
||||
SLANT,
|
||||
FONT_FAMILIES
|
||||
FONT_FAMILIES,
|
||||
|
||||
COUNT
|
||||
};
|
||||
|
||||
typedef std::unordered_map<ModifierType, std::shared_ptr<TextStyleModifier>> ModifierMap;
|
||||
|
||||
static std::shared_ptr<TextStyleModifier> allocate_modifier(ModifierType type);
|
||||
static std::shared_ptr<TextStyleModifier> get_default_modifier(ModifierType type);
|
||||
static const ModifierMap& get_default_modifiers();
|
||||
virtual ModifierType get_type() const = 0;
|
||||
virtual void save(cereal::PortableBinaryOutputArchive& a) const = 0;
|
||||
virtual void load(cereal::PortableBinaryInputArchive& a) = 0;
|
||||
|
|
@ -22,6 +29,7 @@ class TextStyleModifier {
|
|||
bool equivalent(TextStyleModifier& modifier) const;
|
||||
virtual ~TextStyleModifier();
|
||||
private:
|
||||
static ModifierMap defaultMods;
|
||||
virtual bool equivalent_data(TextStyleModifier& modifier) const = 0;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue