Can now undo/redo styles

This commit is contained in:
Yousef Khadadeh 2025-11-12 14:54:46 +03:00
parent 94438c7b97
commit b0b04a28c8
6 changed files with 79 additions and 20 deletions

View file

@ -21,7 +21,6 @@ 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;
@ -32,7 +31,7 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
t.gui.text_label("Font");
if(t.gui.font_picker("font picker", &newFontName)) {
currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES] = std::make_shared<FontFamiliesTextStyleModifier>(std::vector<SkString>{SkString{newFontName.c_str(), newFontName.size()}});
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES]);
add_undo_if_selecting_area(a, [&]() {a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::FONT_FAMILIES]);});
}
});
@ -40,13 +39,13 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
if(t.gui.svg_icon_button("Bold button", "data/icons/RemixIcon/bold.svg", newIsBold)) {
newIsBold = !newIsBold;
currentMods[TextStyleModifier::ModifierType::WEIGHT] = std::make_shared<WeightTextStyleModifier>(newIsBold ? SkFontStyle::Weight::kBold_Weight : SkFontStyle::Weight::kNormal_Weight);
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::WEIGHT]);
add_undo_if_selecting_area(a, [&]() {a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::WEIGHT]);});
}
if(t.gui.svg_icon_button("Italic button", "data/icons/RemixIcon/italic.svg", newIsItalic)) {
newIsItalic = !newIsItalic;
currentMods[TextStyleModifier::ModifierType::SLANT] = std::make_shared<SlantTextStyleModifier>(newIsItalic ? SkFontStyle::Slant::kItalic_Slant : SkFontStyle::Slant::kUpright_Slant);
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::SLANT]);
add_undo_if_selecting_area(a, [&]() {a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::SLANT]);});
}
bool decorationEdited = false;
@ -64,38 +63,46 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
}
if(decorationEdited) {
currentMods[TextStyleModifier::ModifierType::DECORATION] = std::make_shared<DecorationTextStyleModifier>(get_new_decoration_value());
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::DECORATION]);
add_undo_if_selecting_area(a, [&]() {a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::DECORATION]);});
}
});
t.gui.left_to_right_line_centered_layout([&]() {
if(t.gui.svg_icon_button("Align left button", "data/icons/RemixIcon/align-left.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kLeft))
a->textBox->set_text_alignment_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kLeft);
add_undo([&](){a->textBox->set_text_alignment_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kLeft);});
if(t.gui.svg_icon_button("Align center button", "data/icons/RemixIcon/align-center.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kCenter))
a->textBox->set_text_alignment_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kCenter);
add_undo([&](){a->textBox->set_text_alignment_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kCenter);});
if(t.gui.svg_icon_button("Align right button", "data/icons/RemixIcon/align-right.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kRight))
a->textBox->set_text_alignment_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kRight);
add_undo([&](){a->textBox->set_text_alignment_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kRight);});
if(t.gui.svg_icon_button("Align justify button", "data/icons/RemixIcon/align-justify.svg", currentPStyle.textAlignment == skia::textlayout::TextAlign::kJustify))
a->textBox->set_text_alignment_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kJustify);
add_undo([&](){a->textBox->set_text_alignment_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextAlign::kJustify);});
if(t.gui.svg_icon_button("Text direction left", "data/icons/RemixIcon/text-direction-l.svg", currentPStyle.textDirection == skia::textlayout::TextDirection::kLtr))
a->textBox->set_text_direction_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextDirection::kLtr);
add_undo([&](){a->textBox->set_text_direction_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextDirection::kLtr);});
if(t.gui.svg_icon_button("Text direction right", "data/icons/RemixIcon/text-direction-r.svg", currentPStyle.textDirection == skia::textlayout::TextDirection::kRtl))
a->textBox->set_text_direction_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextDirection::kRtl);
add_undo([&](){a->textBox->set_text_direction_between(a->cursor->selectionBeginPos.fParagraphIndex, a->cursor->selectionEndPos.fParagraphIndex, skia::textlayout::TextDirection::kRtl);});
});
if(t.gui.slider_scalar_field<uint32_t>("Font Size Slider", "Font Size", &newFontSize, 3, 100)) {
hold_undo_data("Font Size", a);
currentMods[TextStyleModifier::ModifierType::SIZE] = std::make_shared<SizeTextStyleModifier>(newFontSize);
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::SIZE]);
}
else
release_undo_data("Font Size");
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", &newTextColor, &newTextColor == t.colorRight)) {
if(t.gui.color_button("Text Color", &newTextColor, &newTextColor == t.colorRight))
t.color_selector_right(&newTextColor == t.colorRight ? nullptr : &newTextColor);
}
}
t.gui.text_label("Text Color");
});
if(&newTextColor == t.colorRight)
hold_undo_data("Text Color", a);
else
release_undo_data("Text Color");
if((&newTextColor == t.colorRight) && t.isUpdatingColorRight) {
currentMods[TextStyleModifier::ModifierType::COLOR] = std::make_shared<ColorTextStyleModifier>(newTextColor);
a->textBox->set_text_style_modifier_between(a->cursor->selectionBeginPos, a->cursor->selectionEndPos, currentMods[TextStyleModifier::ModifierType::COLOR]);
@ -112,6 +119,42 @@ bool TextBoxEditTool::edit_gui(const std::shared_ptr<DrawComponent>& comp) {
return oldInputChangedTextBox;
}
void TextBoxEditTool::hold_undo_data(const std::string& undoName, const std::shared_ptr<DrawTextBox>& a) {
auto it = undoHeldData.find(undoName);
if(it != undoHeldData.end()) {
std::pair<RichText::TextBox::Cursor, RichText::TextData>& undoData = it->second;
if(undoData.first != *a->cursor)
undoHeldData.erase(it);
}
else if(a->cursor->selectionBeginPos != a->cursor->selectionEndPos)
undoHeldData.emplace(undoName, std::pair<RichText::TextBox::Cursor, RichText::TextData>{*a->cursor, a->textBox->get_rich_text_data()});
}
void TextBoxEditTool::release_undo_data(const std::string& undoName) {
auto it = undoHeldData.find(undoName);
if(it != undoHeldData.end()) {
std::pair<RichText::TextBox::Cursor, RichText::TextData>& undoData = it->second;
InputManager& input = drawP.world.main.input;
input.text.add_textbox_undo(undoData.first, undoData.second);
undoHeldData.erase(it);
std::cout << "Yay 2!" << std::endl;
}
}
void TextBoxEditTool::add_undo_if_selecting_area(const std::shared_ptr<DrawTextBox>& a, const std::function<void()>& func) {
if(a->cursor->selectionBeginPos == a->cursor->selectionEndPos)
func();
else {
InputManager& input = drawP.world.main.input;
input.text.do_textbox_operation_with_undo(func);
}
}
void TextBoxEditTool::add_undo(const std::function<void()>& func) {
InputManager& input = drawP.world.main.input;
input.text.do_textbox_operation_with_undo(func);
}
uint8_t TextBoxEditTool::get_new_decoration_value() {
uint8_t toRet = skia::textlayout::TextDecoration::kNoDecoration;
if(newIsUnderlined)

View file

@ -32,6 +32,14 @@ class TextBoxEditTool : public DrawingProgramEditToolBase {
uint8_t get_new_decoration_value();
void set_styles_at_selection(const std::shared_ptr<DrawTextBox>& a);
void add_undo_if_selecting_area(const std::shared_ptr<DrawTextBox>& a, const std::function<void()>& func);
void add_undo(const std::function<void()>& func);
void hold_undo_data(const std::string& undoName, const std::shared_ptr<DrawTextBox>& a);
void release_undo_data(const std::string& undoName);
std::unordered_map<std::string, std::pair<RichText::TextBox::Cursor, RichText::TextData>> undoHeldData;
RichText::TextStyleModifier::ModifierMap currentMods;
std::vector<std::string> sortedFontList;

View file

@ -107,13 +107,13 @@ void InputManager::Text::add_textbox_undo(const RichText::TextBox::Cursor& prevC
if(textBox) {
textboxUndo.push({[textBox = textBox, cursor = cursor, prevCursor = prevCursor, prevRichText = prevRichText]() {
textBox->set_rich_text_data(prevRichText);
*cursor = prevCursor;
cursor->selectionBeginPos = cursor->selectionEndPos = cursor->pos = std::max(prevCursor.selectionEndPos, prevCursor.selectionBeginPos);
cursor->previousX = std::nullopt;
return true;
},
[textBox = textBox, cursor = cursor, currentCursor = *cursor, currentRichText = textBox->get_rich_text_data()]() {
textBox->set_rich_text_data(currentRichText);
*cursor = currentCursor;
cursor->selectionBeginPos = cursor->selectionEndPos = cursor->pos = std::max(currentCursor.selectionEndPos, currentCursor.selectionBeginPos);
cursor->previousX = std::nullopt;
return true;
}});

View file

@ -92,10 +92,10 @@ struct InputManager {
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:
void add_textbox_undo(const RichText::TextBox::Cursor& prevCursor, const RichText::TextData& prevRichText);
void do_textbox_operation_with_undo(const std::function<void()>& func);
void add_textbox_undo(const RichText::TextBox::Cursor& prevCursor, const RichText::TextData& prevRichText);
void do_textbox_operation_with_undo(const std::function<void()>& func);
private:
std::shared_ptr<RichText::TextBox> textBox;
std::shared_ptr<RichText::TextBox::Cursor> cursor;
std::optional<RichText::TextStyleModifier::ModifierMap> modMap;

View file

@ -98,6 +98,14 @@ bool TextPosition::operator<=(const TextPosition& o) const {
return (*this < o) || (*this == o);
}
bool TextBox::Cursor::operator==(const Cursor& o) const {
return o.selectionBeginPos == selectionBeginPos && o.selectionEndPos == selectionEndPos && o.pos == pos;
}
bool TextBox::Cursor::operator!=(const Cursor& o) const {
return !(*this == o);
}
TextBox::TextBox() {
paragraphs.emplace_back();
}

View file

@ -83,8 +83,8 @@ class TextBox {
a(pos, selectionBeginPos, selectionEndPos);
}
std::optional<float> previousX;
bool operator==(const Cursor& o) const = default;
bool operator!=(const Cursor& o) const = default;
bool operator==(const Cursor& o) const;
bool operator!=(const Cursor& o) const;
};
struct PaintOpts {