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

PixelPaint: Show more specific Undo/Redo action text

The Undo/Redo actions now tell you what kind of action will be
undone/redone. This is achieved by adding an "action text" field to the
ImageUndoCommand and having everyone who calls did_complete_action()
provide this text.
This commit is contained in:
Andreas Kling 2022-08-21 20:33:03 +02:00
parent 101eb53de5
commit bf25b0a0b5
15 changed files with 57 additions and 20 deletions

View file

@ -43,7 +43,7 @@ void Filter::apply() const
if (auto* layer = m_editor->active_layer()) { if (auto* layer = m_editor->active_layer()) {
apply(layer->content_bitmap(), layer->content_bitmap()); apply(layer->content_bitmap(), layer->content_bitmap());
layer->did_modify_bitmap(layer->rect()); layer->did_modify_bitmap(layer->rect());
m_editor->did_complete_action(); m_editor->did_complete_action(String::formatted("Filter {}", filter_name()));
} }
} }

View file

@ -481,9 +481,10 @@ void Image::did_change_rect(Gfx::IntRect const& a_modified_rect)
client->image_did_change_rect(modified_rect); client->image_did_change_rect(modified_rect);
} }
ImageUndoCommand::ImageUndoCommand(Image& image) ImageUndoCommand::ImageUndoCommand(Image& image, String action_text)
: m_snapshot(image.take_snapshot().release_value_but_fixme_should_propagate_errors()) : m_snapshot(image.take_snapshot().release_value_but_fixme_should_propagate_errors())
, m_image(image) , m_image(image)
, m_action_text(move(action_text))
{ {
} }

View file

@ -116,14 +116,16 @@ private:
class ImageUndoCommand : public GUI::Command { class ImageUndoCommand : public GUI::Command {
public: public:
ImageUndoCommand(Image& image); ImageUndoCommand(Image&, String action_text);
virtual void undo() override; virtual void undo() override;
virtual void redo() override; virtual void redo() override;
virtual String action_text() const override { return m_action_text; }
private: private:
RefPtr<Image> m_snapshot; RefPtr<Image> m_snapshot;
Image& m_image; Image& m_image;
String m_action_text;
}; };
} }

View file

@ -31,7 +31,7 @@ ImageEditor::ImageEditor(NonnullRefPtr<Image> image)
, m_selection(*this) , m_selection(*this)
{ {
set_focus_policy(GUI::FocusPolicy::StrongFocus); set_focus_policy(GUI::FocusPolicy::StrongFocus);
m_undo_stack.push(make<ImageUndoCommand>(*m_image)); m_undo_stack.push(make<ImageUndoCommand>(*m_image, String()));
m_image->add_client(*this); m_image->add_client(*this);
set_original_rect(m_image->rect()); set_original_rect(m_image->rect());
set_scale_bounds(0.1f, 100.0f); set_scale_bounds(0.1f, 100.0f);
@ -48,11 +48,11 @@ ImageEditor::~ImageEditor()
m_image->remove_client(*this); m_image->remove_client(*this);
} }
void ImageEditor::did_complete_action() void ImageEditor::did_complete_action(String action_text)
{ {
if (on_modified_change) if (on_modified_change)
on_modified_change(true); on_modified_change(true);
m_undo_stack.push(make<ImageUndoCommand>(*m_image)); m_undo_stack.push(make<ImageUndoCommand>(*m_image, move(action_text)));
} }
bool ImageEditor::is_modified() bool ImageEditor::is_modified()

View file

@ -40,7 +40,7 @@ public:
void set_active_tool(Tool*); void set_active_tool(Tool*);
void update_tool_cursor(); void update_tool_cursor();
void did_complete_action(); void did_complete_action(String action_text);
bool undo(); bool undo();
bool redo(); bool redo();

View file

@ -60,7 +60,7 @@ LevelsDialog::LevelsDialog(GUI::Window* parent_window, ImageEditor* editor)
apply_button->on_click = [this](auto) { apply_button->on_click = [this](auto) {
if (m_did_change) { if (m_did_change) {
m_editor->on_modified_change(true); m_editor->on_modified_change(true);
m_editor->did_complete_action(); m_editor->did_complete_action("Levels"sv);
} }
cleanup_resources(); cleanup_resources();

View file

@ -100,9 +100,41 @@ MainWidget::MainWidget()
m_show_guides_action->set_checked(image_editor.guide_visibility()); m_show_guides_action->set_checked(image_editor.guide_visibility());
m_show_rulers_action->set_checked(image_editor.ruler_visibility()); m_show_rulers_action->set_checked(image_editor.ruler_visibility());
image_editor.on_scale_change(image_editor.scale()); image_editor.on_scale_change(image_editor.scale());
image_editor.undo_stack().on_state_change = [this] {
image_editor_did_update_undo_stack();
};
// Ensure that our undo/redo actions are in sync with the current editor.
image_editor_did_update_undo_stack();
}; };
} }
void MainWidget::image_editor_did_update_undo_stack()
{
auto* image_editor = current_image_editor();
if (!image_editor) {
m_undo_action->set_enabled(false);
m_redo_action->set_enabled(false);
return;
}
auto make_action_text = [](auto prefix, auto suffix) {
StringBuilder builder;
builder.append(prefix);
if (suffix.has_value()) {
builder.append(' ');
builder.append(suffix.value());
}
return builder.to_string();
};
auto& undo_stack = image_editor->undo_stack();
m_undo_action->set_enabled(undo_stack.can_undo());
m_redo_action->set_enabled(undo_stack.can_redo());
m_undo_action->set_text(make_action_text("&Undo"sv, undo_stack.undo_action_text()));
m_redo_action->set_text(make_action_text("&Redo"sv, undo_stack.redo_action_text()));
}
// Note: Update these together! v // Note: Update these together! v
static Vector<String> const s_suggested_zoom_levels { "25%", "50%", "100%", "200%", "300%", "400%", "800%", "1600%", "Fit to width", "Fit to height", "Fit entire image" }; static Vector<String> const s_suggested_zoom_levels { "25%", "50%", "100%", "200%", "300%", "400%", "800%", "1600%", "Fit to width", "Fit to height", "Fit entire image" };
static constexpr int s_zoom_level_fit_width = 8; static constexpr int s_zoom_level_fit_width = 8;
@ -642,7 +674,7 @@ void MainWidget::initialize_menubar(GUI::Window& window)
auto* editor = current_image_editor(); auto* editor = current_image_editor();
VERIFY(editor); VERIFY(editor);
editor->image().flatten_all_layers(); editor->image().flatten_all_layers();
editor->did_complete_action(); editor->did_complete_action("Flatten Image"sv);
})); }));
m_layer_menu->add_action(GUI::Action::create( m_layer_menu->add_action(GUI::Action::create(
@ -650,7 +682,7 @@ void MainWidget::initialize_menubar(GUI::Window& window)
auto* editor = current_image_editor(); auto* editor = current_image_editor();
VERIFY(editor); VERIFY(editor);
editor->image().merge_visible_layers(); editor->image().merge_visible_layers();
editor->did_complete_action(); editor->did_complete_action("Merge Visible"sv);
})); }));
m_layer_menu->add_action(GUI::Action::create( m_layer_menu->add_action(GUI::Action::create(
@ -661,7 +693,7 @@ void MainWidget::initialize_menubar(GUI::Window& window)
if (!active_layer) if (!active_layer)
return; return;
editor->image().merge_active_layer_up(*active_layer); editor->image().merge_active_layer_up(*active_layer);
editor->did_complete_action(); editor->did_complete_action("Merge Active Layer Up"sv);
})); }));
m_layer_menu->add_action(GUI::Action::create( m_layer_menu->add_action(GUI::Action::create(
@ -672,7 +704,7 @@ void MainWidget::initialize_menubar(GUI::Window& window)
if (!active_layer) if (!active_layer)
return; return;
editor->image().merge_active_layer_down(*active_layer); editor->image().merge_active_layer_down(*active_layer);
editor->did_complete_action(); editor->did_complete_action("Merge Active Layer Down"sv);
})); }));
m_filter_menu = window.add_menu("&Filter"); m_filter_menu = window.add_menu("&Filter");
@ -694,7 +726,7 @@ void MainWidget::initialize_menubar(GUI::Window& window)
if (auto parameters = PixelPaint::FilterParameters<Gfx::GenericConvolutionFilter<5>>::get(&window)) { if (auto parameters = PixelPaint::FilterParameters<Gfx::GenericConvolutionFilter<5>>::get(&window)) {
filter.apply(layer->content_bitmap(), layer->rect(), layer->content_bitmap(), layer->rect(), *parameters); filter.apply(layer->content_bitmap(), layer->rect(), layer->content_bitmap(), layer->rect(), *parameters);
layer->did_modify_bitmap(layer->rect()); layer->did_modify_bitmap(layer->rect());
editor->did_complete_action(); editor->did_complete_action("Generic 5x5 Convolution"sv);
} }
} }
})); }));

View file

@ -51,6 +51,8 @@ private:
ImageEditor& create_new_editor(NonnullRefPtr<Image>); ImageEditor& create_new_editor(NonnullRefPtr<Image>);
void create_image_from_clipboard(); void create_image_from_clipboard();
void image_editor_did_update_undo_stack();
void set_actions_enabled(bool enabled); void set_actions_enabled(bool enabled);
virtual void drop_event(GUI::DropEvent&) override; virtual void drop_event(GUI::DropEvent&) override;

View file

@ -67,7 +67,7 @@ void BrushTool::on_mousemove(Layer* layer, MouseEvent& event)
void BrushTool::on_mouseup(Layer*, MouseEvent&) void BrushTool::on_mouseup(Layer*, MouseEvent&)
{ {
if (m_was_drawing) { if (m_was_drawing) {
m_editor->did_complete_action(); m_editor->did_complete_action(tool_name());
m_was_drawing = false; m_was_drawing = false;
} }
} }

View file

@ -134,7 +134,7 @@ void BucketTool::on_mousedown(Layer* layer, MouseEvent& event)
flood_fill(layer->currently_edited_bitmap(), layer_event.position(), target_color, m_editor->color_for(layer_event), m_threshold); flood_fill(layer->currently_edited_bitmap(), layer_event.position(), target_color, m_editor->color_for(layer_event), m_threshold);
layer->did_modify_bitmap(); layer->did_modify_bitmap();
m_editor->did_complete_action(); m_editor->did_complete_action(tool_name());
} }
GUI::Widget* BucketTool::get_properties_widget() GUI::Widget* BucketTool::get_properties_widget()

View file

@ -88,7 +88,7 @@ void EllipseTool::on_mouseup(Layer* layer, MouseEvent& event)
m_drawing_button = GUI::MouseButton::None; m_drawing_button = GUI::MouseButton::None;
layer->did_modify_bitmap(); layer->did_modify_bitmap();
m_editor->update(); m_editor->update();
m_editor->did_complete_action(); m_editor->did_complete_action(tool_name());
} }
} }

View file

@ -81,7 +81,7 @@ void LineTool::on_mouseup(Layer* layer, MouseEvent& event)
m_drawing_button = GUI::MouseButton::None; m_drawing_button = GUI::MouseButton::None;
layer->did_modify_bitmap(); layer->did_modify_bitmap();
m_editor->update(); m_editor->update();
m_editor->did_complete_action(); m_editor->did_complete_action(tool_name());
} }
} }

View file

@ -69,7 +69,7 @@ void MoveTool::on_mouseup(Layer* layer, MouseEvent& event)
if (layer_event.button() != GUI::MouseButton::Primary) if (layer_event.button() != GUI::MouseButton::Primary)
return; return;
m_layer_being_moved = nullptr; m_layer_being_moved = nullptr;
m_editor->did_complete_action(); m_editor->did_complete_action(tool_name());
} }
void MoveTool::on_keydown(GUI::KeyEvent& event) void MoveTool::on_keydown(GUI::KeyEvent& event)

View file

@ -92,7 +92,7 @@ void RectangleTool::on_mouseup(Layer* layer, MouseEvent& event)
m_drawing_button = GUI::MouseButton::None; m_drawing_button = GUI::MouseButton::None;
layer->did_modify_bitmap(); layer->did_modify_bitmap();
m_editor->update(); m_editor->update();
m_editor->did_complete_action(); m_editor->did_complete_action(tool_name());
} }
} }

View file

@ -88,7 +88,7 @@ void SprayTool::on_mouseup(Layer*, MouseEvent&)
{ {
if (m_timer->is_active()) { if (m_timer->is_active()) {
m_timer->stop(); m_timer->stop();
m_editor->did_complete_action(); m_editor->did_complete_action(tool_name());
} }
} }