1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:57:35 +00:00

FontEditor: Add action text to Undo and Redo

This commit is contained in:
thankyouverycool 2023-05-16 08:39:02 -04:00 committed by Andreas Kling
parent e6935cbaaf
commit c10b1e3aea
5 changed files with 63 additions and 28 deletions

View file

@ -84,8 +84,6 @@ void GlyphEditorWidget::mousedown_event(GUI::MouseEvent& event)
if (mode() == Move && is_glyph_empty())
return;
m_is_clicking_valid_cell = true;
if (on_undo_event)
on_undo_event();
if (mode() == Paint) {
draw_at_mouse(event);
} else {
@ -146,6 +144,8 @@ void GlyphEditorWidget::draw_at_mouse(GUI::MouseEvent const& event)
return;
if (bitmap.bit_at(x, y) == set)
return;
if (on_undo_event && event.type() == GUI::MouseEvent::MouseDown)
on_undo_event("Paint Glyph"sv);
bitmap.set_bit_at(x, y, set);
if (on_glyph_altered)
on_glyph_altered(m_glyph);
@ -154,6 +154,9 @@ void GlyphEditorWidget::draw_at_mouse(GUI::MouseEvent const& event)
void GlyphEditorWidget::move_at_mouse(GUI::MouseEvent const& event)
{
if (on_undo_event && event.type() == GUI::MouseEvent::MouseDown)
on_undo_event("Move Glyph"sv);
int x_delta = ((event.x() - 1) / m_scale) - m_scaled_offset_x;
int y_delta = ((event.y() - 1) / m_scale) - m_scaled_offset_y;
auto bitmap = m_font->raw_glyph(m_glyph).glyph_bitmap();
@ -190,12 +193,13 @@ static Vector<Vector<u8>> glyph_as_matrix(Gfx::GlyphBitmap const& bitmap)
void GlyphEditorWidget::rotate_90(Gfx::RotationDirection direction)
{
auto clockwise = direction == Gfx::RotationDirection::Clockwise;
auto action_text = clockwise ? "Rotate Glyph Clockwise"sv : "Rotate Glyph Counterclockwise"sv;
if (on_undo_event)
on_undo_event();
on_undo_event(action_text);
auto bitmap = m_font->raw_glyph(m_glyph).glyph_bitmap();
auto matrix = glyph_as_matrix(bitmap);
auto clockwise = direction == Gfx::RotationDirection::Clockwise;
for (int y = 0; y < bitmap.height(); y++) {
for (int x = 0; x < bitmap.width(); x++) {
@ -216,12 +220,13 @@ void GlyphEditorWidget::rotate_90(Gfx::RotationDirection direction)
void GlyphEditorWidget::flip(Gfx::Orientation orientation)
{
auto vertical = orientation == Gfx::Orientation::Vertical;
auto action_text = vertical ? "Flip Glyph Vertically"sv : "Flip Glyph Horizontally"sv;
if (on_undo_event)
on_undo_event();
on_undo_event(action_text);
auto bitmap = m_font->raw_glyph(m_glyph).glyph_bitmap();
auto matrix = glyph_as_matrix(bitmap);
auto vertical = orientation == Gfx::Orientation::Vertical;
for (int y = 0; y < bitmap.height(); y++) {
for (int x = 0; x < bitmap.width(); x++) {

View file

@ -42,7 +42,7 @@ public:
void set_mode(Mode mode) { m_mode = mode; }
Function<void(int)> on_glyph_altered;
Function<void()> on_undo_event;
Function<void(StringView action_text)> on_undo_event;
private:
GlyphEditorWidget() = default;

View file

@ -450,6 +450,7 @@ ErrorOr<void> MainWidget::create_undo_stack()
m_undo_stack->on_state_change = [this] {
m_undo_action->set_enabled(m_undo_stack->can_undo());
m_redo_action->set_enabled(m_undo_stack->can_redo());
update_action_text();
if (m_undo_stack->is_current_modified())
did_modify_font();
};
@ -457,6 +458,24 @@ ErrorOr<void> MainWidget::create_undo_stack()
return {};
}
void MainWidget::update_action_text()
{
auto text_or_error = [](auto prefix, auto suffix) -> ErrorOr<String> {
StringBuilder builder;
TRY(builder.try_append(prefix));
if (suffix.has_value()) {
TRY(builder.try_append(' '));
TRY(builder.try_append(suffix.value()));
}
return builder.to_string();
};
if (auto maybe_text = text_or_error("&Undo"sv, m_undo_stack->undo_action_text()); !maybe_text.is_error())
m_undo_action->set_text(maybe_text.release_value().to_deprecated_string());
if (auto maybe_text = text_or_error("&Redo"sv, m_undo_stack->redo_action_text()); !maybe_text.is_error())
m_redo_action->set_text(maybe_text.release_value().to_deprecated_string());
}
ErrorOr<void> MainWidget::create_widgets()
{
TRY(load_from_gml(font_editor_window_gml));
@ -474,8 +493,9 @@ ErrorOr<void> MainWidget::create_widgets()
did_modify_font();
};
m_glyph_editor_widget->on_undo_event = [this] {
reset_selection_and_push_undo();
m_glyph_editor_widget->on_undo_event = [this](auto action_text) {
reset_selection();
push_undo(action_text);
};
m_glyph_editor_width_spinbox = find_descendant_of_type_named<GUI::SpinBox>("glyph_editor_width_spinbox");
@ -530,7 +550,8 @@ ErrorOr<void> MainWidget::create_widgets()
};
m_glyph_editor_width_spinbox->on_change = [this](int value) {
reset_selection_and_push_undo();
reset_selection();
push_undo("Resize Glyph"sv);
m_font->set_glyph_width(m_glyph_map_widget->active_glyph(), value);
m_glyph_editor_widget->update();
m_glyph_map_widget->update_glyph(m_glyph_map_widget->active_glyph());
@ -540,7 +561,8 @@ ErrorOr<void> MainWidget::create_widgets()
};
m_glyph_editor_present_checkbox->on_checked = [this](bool checked) {
reset_selection_and_push_undo();
reset_selection();
push_undo("Resize Glyph"sv);
m_font->set_glyph_width(m_glyph_map_widget->active_glyph(), checked ? m_font->glyph_fixed_width() : 0);
m_glyph_editor_widget->update();
m_glyph_map_widget->update_glyph(m_glyph_map_widget->active_glyph());
@ -788,29 +810,31 @@ ErrorOr<void> MainWidget::open_file(StringView path, NonnullOwnPtr<Core::File> f
return {};
}
void MainWidget::push_undo()
void MainWidget::push_undo(StringView action_text)
{
auto maybe_state = m_undo_selection->save_state();
if (maybe_state.is_error())
return show_error(maybe_state.release_error(), "Saving undo state failed"sv);
auto maybe_command = try_make<SelectionUndoCommand>(*m_undo_selection, move(maybe_state.value()));
auto maybe_text = String::from_utf8(action_text);
if (maybe_text.is_error())
return show_error(maybe_text.release_error(), "Creating action text failed"sv);
auto maybe_command = try_make<SelectionUndoCommand>(*m_undo_selection, move(maybe_state.value()), move(maybe_text.value()));
if (maybe_command.is_error())
return show_error(maybe_command.release_error(), "Making undo command failed"sv);
if (auto maybe_push = m_undo_stack->try_push(move(maybe_command.value())); maybe_push.is_error())
show_error(maybe_push.release_error(), "Pushing undo stack failed"sv);
}
void MainWidget::reset_selection_and_push_undo()
void MainWidget::reset_selection()
{
auto selection = m_glyph_map_widget->selection().normalized();
if (selection.size() != 1) {
if (selection.size() == 1)
return;
auto start = m_glyph_map_widget->active_glyph();
m_undo_selection->set_start(start);
m_undo_selection->set_size(1);
m_glyph_map_widget->set_selection(start, 1);
m_glyph_map_widget->update();
}
push_undo();
}
void MainWidget::restore_state()
@ -1023,7 +1047,8 @@ void MainWidget::paste_glyphs()
auto selection = m_glyph_map_widget->selection().normalized();
auto range_bound_glyph_count = min(glyph_count, 1 + m_range.last - selection.start());
m_undo_selection->set_size(range_bound_glyph_count);
push_undo();
auto action_text = range_bound_glyph_count == 1 ? "Paste Glyph"sv : "Paste Glyphs"sv;
push_undo(action_text);
size_t bytes_per_glyph = Gfx::GlyphBitmap::bytes_per_row() * m_font->glyph_height();
size_t bytes_per_copied_glyph = Gfx::GlyphBitmap::bytes_per_row() * height;
@ -1055,9 +1080,10 @@ void MainWidget::paste_glyphs()
void MainWidget::delete_selected_glyphs()
{
push_undo();
auto selection = m_glyph_map_widget->selection().normalized();
auto action_text = selection.size() == 1 ? "Delete Glyph"sv : "Delete Glyphs"sv;
push_undo(action_text);
size_t bytes_per_glyph = Gfx::GlyphBitmap::bytes_per_row() * m_font->glyph_height();
auto* rows = m_font->rows() + selection.start() * bytes_per_glyph;
auto* widths = m_font->widths() + selection.start();

View file

@ -59,6 +59,7 @@ private:
void restore_state();
void did_modify_font();
void update_action_text();
void update_statusbar();
void update_preview();
void update_title();
@ -72,8 +73,8 @@ private:
void paste_glyphs();
void delete_selected_glyphs();
void push_undo();
void reset_selection_and_push_undo();
void push_undo(StringView action_text);
void reset_selection();
RefPtr<GUI::GlyphMapWidget> m_glyph_map_widget;
RefPtr<GlyphEditorWidget> m_glyph_editor_widget;

View file

@ -74,9 +74,10 @@ private:
class SelectionUndoCommand : public GUI::Command {
public:
SelectionUndoCommand(UndoSelection& selection, NonnullRefPtr<UndoSelection> undo_state)
SelectionUndoCommand(UndoSelection& selection, NonnullRefPtr<UndoSelection> undo_state, String action_text)
: m_undo_state(undo_state)
, m_undo_selection(selection)
, m_action_text(move(action_text))
{
}
virtual void undo() override
@ -96,9 +97,11 @@ public:
else
warnln("Restoring state failed");
}
virtual DeprecatedString action_text() const override { return m_action_text.to_deprecated_string(); }
private:
NonnullRefPtr<UndoSelection> m_undo_state;
RefPtr<UndoSelection> m_redo_state;
UndoSelection& m_undo_selection;
String m_action_text;
};