diff options
author | Andreas Kling <kling@serenityos.org> | 2022-08-25 20:50:15 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-08-26 01:04:52 +0200 |
commit | d571159aebbf1e7442622efb8346d5c0657c1e03 (patch) | |
tree | 8786143c2d9cb3d0520b17aeaab742ded1f04c44 | |
parent | 67596d9546fc2a08b5022dc60e5a30e3ea2c2f18 (diff) | |
download | serenity-d571159aebbf1e7442622efb8346d5c0657c1e03.zip |
PixelPaint: Move selection from ImageEditor to Image
This is preparation for making selection state undoable.
-rw-r--r-- | Userland/Applications/PixelPaint/Image.cpp | 1 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/Image.h | 6 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/ImageEditor.cpp | 18 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/ImageEditor.h | 10 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/MainWidget.cpp | 20 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/Selection.cpp | 19 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/Selection.h | 20 | ||||
-rw-r--r-- | Userland/Applications/PixelPaint/Tools/RectangleSelectTool.cpp | 8 |
8 files changed, 70 insertions, 32 deletions
diff --git a/Userland/Applications/PixelPaint/Image.cpp b/Userland/Applications/PixelPaint/Image.cpp index 9d5a04e290..ce7e16f094 100644 --- a/Userland/Applications/PixelPaint/Image.cpp +++ b/Userland/Applications/PixelPaint/Image.cpp @@ -37,6 +37,7 @@ ErrorOr<NonnullRefPtr<Image>> Image::try_create_with_size(Gfx::IntSize const& si Image::Image(Gfx::IntSize const& size) : m_size(size) + , m_selection(*this) { } diff --git a/Userland/Applications/PixelPaint/Image.h b/Userland/Applications/PixelPaint/Image.h index c5b5dd49e8..3140b8a53c 100644 --- a/Userland/Applications/PixelPaint/Image.h +++ b/Userland/Applications/PixelPaint/Image.h @@ -8,6 +8,7 @@ #pragma once +#include "Selection.h" #include <AK/HashTable.h> #include <AK/JsonObjectSerializer.h> #include <AK/NonnullRefPtrVector.h> @@ -55,6 +56,9 @@ public: ErrorOr<NonnullRefPtr<Gfx::Bitmap>> try_compose_bitmap(Gfx::BitmapFormat format) const; RefPtr<Gfx::Bitmap> try_copy_bitmap(Selection const&) const; + Selection& selection() { return m_selection; } + Selection const& selection() const { return m_selection; } + size_t layer_count() const { return m_layers.size(); } Layer const& layer(size_t index) const { return m_layers.at(index); } Layer& layer(size_t index) { return m_layers.at(index); } @@ -114,6 +118,8 @@ private: NonnullRefPtrVector<Layer> m_layers; HashTable<ImageClient*> m_clients; + + Selection m_selection; }; class ImageUndoCommand : public GUI::Command { diff --git a/Userland/Applications/PixelPaint/ImageEditor.cpp b/Userland/Applications/PixelPaint/ImageEditor.cpp index 637d11a9c8..23908092fd 100644 --- a/Userland/Applications/PixelPaint/ImageEditor.cpp +++ b/Userland/Applications/PixelPaint/ImageEditor.cpp @@ -30,11 +30,11 @@ constexpr int marching_ant_length = 4; ImageEditor::ImageEditor(NonnullRefPtr<Image> image) : m_image(move(image)) , m_title("Untitled") - , m_selection(*this) { set_focus_policy(GUI::FocusPolicy::StrongFocus); m_undo_stack.push(make<ImageUndoCommand>(*m_image, String())); m_image->add_client(*this); + m_image->selection().add_client(*this); set_original_rect(m_image->rect()); set_scale_bounds(0.1f, 100.0f); @@ -47,7 +47,7 @@ ImageEditor::ImageEditor(NonnullRefPtr<Image> image) m_marching_ants_timer = Core::Timer::create_repeating(80, [this] { ++m_marching_ants_offset; m_marching_ants_offset %= (marching_ant_length * 2); - if (!m_selection.is_empty() || m_selection.in_interactive_selection()) + if (!m_image->selection().is_empty() || m_image->selection().in_interactive_selection()) update(); }); m_marching_ants_timer->start(); @@ -55,6 +55,7 @@ ImageEditor::ImageEditor(NonnullRefPtr<Image> image) ImageEditor::~ImageEditor() { + m_image->selection().remove_client(*this); m_image->remove_client(*this); } @@ -372,8 +373,8 @@ void ImageEditor::context_menu_event(GUI::ContextMenuEvent& event) void ImageEditor::keydown_event(GUI::KeyEvent& event) { - if (event.key() == Key_Delete && !selection().is_empty() && active_layer()) { - active_layer()->erase_selection(selection()); + if (event.key() == Key_Delete && !m_image->selection().is_empty() && active_layer()) { + active_layer()->erase_selection(m_image->selection()); return; } @@ -662,10 +663,10 @@ void ImageEditor::set_loaded_from_image(bool loaded_from_image) void ImageEditor::paint_selection(Gfx::Painter& painter) { - if (m_selection.is_empty()) + if (m_image->selection().is_empty()) return; - draw_marching_ants(painter, m_selection.mask()); + draw_marching_ants(painter, m_image->selection().mask()); } void ImageEditor::draw_marching_ants(Gfx::Painter& painter, Gfx::IntRect const& rect) const @@ -751,4 +752,9 @@ void ImageEditor::draw_marching_ants_pixel(Gfx::Painter& painter, int x, int y) } } +void ImageEditor::selection_did_change() +{ + update(); +} + } diff --git a/Userland/Applications/PixelPaint/ImageEditor.h b/Userland/Applications/PixelPaint/ImageEditor.h index 5e9fb8e616..b7d5888d43 100644 --- a/Userland/Applications/PixelPaint/ImageEditor.h +++ b/Userland/Applications/PixelPaint/ImageEditor.h @@ -24,7 +24,8 @@ class Tool; class ImageEditor final : public GUI::AbstractZoomPanWidget - , public ImageClient { + , public ImageClient + , public SelectionClient { C_OBJECT(ImageEditor); public: @@ -71,9 +72,6 @@ public: Color secondary_color() const { return m_secondary_color; } void set_secondary_color(Color); - Selection& selection() { return m_selection; } - Selection const& selection() const { return m_selection; } - Color color_for(GUI::MouseEvent const&) const; Color color_for(GUI::MouseButton) const; @@ -134,6 +132,8 @@ private: virtual void image_did_change_rect(Gfx::IntRect const&) override; virtual void image_select_layer(Layer*) override; + virtual void selection_did_change() override; + GUI::MouseEvent event_adjusted_for_layer(GUI::MouseEvent const&, Layer const&) const; GUI::MouseEvent event_with_pan_and_scale_applied(GUI::MouseEvent const&) const; @@ -173,8 +173,6 @@ private: Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap>> m_active_cursor { Gfx::StandardCursor::None }; - Selection m_selection; - bool m_loaded_from_image { true }; RefPtr<Core::Timer> m_marching_ants_timer; diff --git a/Userland/Applications/PixelPaint/MainWidget.cpp b/Userland/Applications/PixelPaint/MainWidget.cpp index 0d63b9ed4b..00c0f91051 100644 --- a/Userland/Applications/PixelPaint/MainWidget.cpp +++ b/Userland/Applications/PixelPaint/MainWidget.cpp @@ -267,13 +267,13 @@ void MainWidget::initialize_menubar(GUI::Window& window) dbgln("Cannot cut with no active layer selected"); return; } - auto bitmap = editor->active_layer()->try_copy_bitmap(editor->selection()); + auto bitmap = editor->active_layer()->try_copy_bitmap(editor->image().selection()); if (!bitmap) { dbgln("try_copy_bitmap() from Layer failed"); return; } GUI::Clipboard::the().set_bitmap(*bitmap); - editor->active_layer()->erase_selection(editor->selection()); + editor->active_layer()->erase_selection(editor->image().selection()); }); m_copy_action = GUI::CommonActions::make_copy_action([&](auto&) { @@ -284,7 +284,7 @@ void MainWidget::initialize_menubar(GUI::Window& window) dbgln("Cannot copy with no active layer selected"); return; } - auto bitmap = editor->active_layer()->try_copy_bitmap(editor->selection()); + auto bitmap = editor->active_layer()->try_copy_bitmap(editor->image().selection()); if (!bitmap) { dbgln("try_copy_bitmap() from Layer failed"); return; @@ -297,7 +297,7 @@ void MainWidget::initialize_menubar(GUI::Window& window) auto* editor = current_image_editor(); VERIFY(editor); - auto bitmap = editor->image().try_copy_bitmap(editor->selection()); + auto bitmap = editor->image().try_copy_bitmap(editor->image().selection()); if (!bitmap) { dbgln("try_copy_bitmap() from Image failed"); return; @@ -319,7 +319,7 @@ void MainWidget::initialize_menubar(GUI::Window& window) auto layer = PixelPaint::Layer::try_create_with_bitmap(editor->image(), *bitmap, "Pasted layer").release_value_but_fixme_should_propagate_errors(); editor->image().add_layer(*layer); editor->set_active_layer(layer); - editor->selection().clear(); + editor->image().selection().clear(); }); GUI::Clipboard::the().on_change = [&](auto& mime_type) { m_paste_action->set_enabled(mime_type == "image/x-serenityos"); @@ -351,13 +351,13 @@ void MainWidget::initialize_menubar(GUI::Window& window) VERIFY(editor); if (!editor->active_layer()) return; - editor->selection().merge(editor->active_layer()->relative_rect(), PixelPaint::Selection::MergeMode::Set); + editor->image().selection().merge(editor->active_layer()->relative_rect(), PixelPaint::Selection::MergeMode::Set); })); m_edit_menu->add_action(GUI::Action::create( "Clear &Selection", g_icon_bag.clear_selection, [&](auto&) { auto* editor = current_image_editor(); VERIFY(editor); - editor->selection().clear(); + editor->image().selection().clear(); })); m_edit_menu->add_separator(); @@ -552,11 +552,11 @@ void MainWidget::initialize_menubar(GUI::Window& window) auto* editor = current_image_editor(); VERIFY(editor); // FIXME: disable this action if there is no selection - if (editor->selection().is_empty()) + if (editor->image().selection().is_empty()) return; - auto crop_rect = editor->image().rect().intersected(editor->selection().bounding_rect()); + auto crop_rect = editor->image().rect().intersected(editor->image().selection().bounding_rect()); editor->image().crop(crop_rect); - editor->selection().clear(); + editor->image().selection().clear(); editor->did_complete_action("Crop Image to Selection"sv); })); diff --git a/Userland/Applications/PixelPaint/Selection.cpp b/Userland/Applications/PixelPaint/Selection.cpp index 57d81ea5fc..41863c2c14 100644 --- a/Userland/Applications/PixelPaint/Selection.cpp +++ b/Userland/Applications/PixelPaint/Selection.cpp @@ -10,15 +10,16 @@ namespace PixelPaint { -Selection::Selection(ImageEditor& editor) - : m_editor(editor) +Selection::Selection(Image& image) + : m_image(image) { } void Selection::clear() { m_mask = {}; - m_editor.update(); + for (auto* client : m_clients) + client->selection_did_change(); } void Selection::merge(Mask const& mask, MergeMode mode) @@ -41,4 +42,16 @@ void Selection::merge(Mask const& mask, MergeMode mode) } } +void Selection::add_client(SelectionClient& client) +{ + VERIFY(!m_clients.contains(&client)); + m_clients.set(&client); +} + +void Selection::remove_client(SelectionClient& client) +{ + VERIFY(m_clients.contains(&client)); + m_clients.remove(&client); +} + } diff --git a/Userland/Applications/PixelPaint/Selection.h b/Userland/Applications/PixelPaint/Selection.h index 7f269cc1e0..c47f87690e 100644 --- a/Userland/Applications/PixelPaint/Selection.h +++ b/Userland/Applications/PixelPaint/Selection.h @@ -13,7 +13,15 @@ namespace PixelPaint { -class ImageEditor; +class Image; + +class SelectionClient { +public: + virtual void selection_did_change() = 0; + +protected: + virtual ~SelectionClient() = default; +}; // Coordinates are image-relative. class Selection { @@ -26,7 +34,7 @@ public: __Count, }; - explicit Selection(ImageEditor&); + explicit Selection(Image&); bool is_empty() const { return m_mask.is_null(); } void clear(); @@ -47,9 +55,15 @@ public: bool in_interactive_selection() { return m_in_interactive_selection; } + void add_client(SelectionClient&); + void remove_client(SelectionClient&); + private: - ImageEditor& m_editor; + Image& m_image; Mask m_mask; + + HashTable<SelectionClient*> m_clients; + bool m_in_interactive_selection { false }; }; diff --git a/Userland/Applications/PixelPaint/Tools/RectangleSelectTool.cpp b/Userland/Applications/PixelPaint/Tools/RectangleSelectTool.cpp index 890aa9bc4f..14a268c46b 100644 --- a/Userland/Applications/PixelPaint/Tools/RectangleSelectTool.cpp +++ b/Userland/Applications/PixelPaint/Tools/RectangleSelectTool.cpp @@ -26,7 +26,7 @@ void RectangleSelectTool::on_mousedown(Layer*, MouseEvent& event) return; m_selecting = true; - m_editor->selection().begin_interactive_selection(); + m_editor->image().selection().begin_interactive_selection(); m_selection_start = image_event.position(); m_selection_end = image_event.position(); @@ -58,7 +58,7 @@ void RectangleSelectTool::on_mouseup(Layer*, MouseEvent& event) return; m_selecting = false; - m_editor->selection().end_interactive_selection(); + m_editor->image().selection().end_interactive_selection(); m_editor->update(); @@ -98,7 +98,7 @@ void RectangleSelectTool::on_mouseup(Layer*, MouseEvent& event) } } - m_editor->selection().merge(mask, m_merge_mode); + m_editor->image().selection().merge(mask, m_merge_mode); } void RectangleSelectTool::on_keydown(GUI::KeyEvent& key_event) @@ -113,7 +113,7 @@ void RectangleSelectTool::on_keydown(GUI::KeyEvent& key_event) if (m_selecting) m_selecting = false; else - m_editor->selection().clear(); + m_editor->image().selection().clear(); } } |