Selaa lähdekoodia

PixelPaint: Move selection from ImageEditor to Image

This is preparation for making selection state undoable.
Andreas Kling 2 vuotta sitten
vanhempi
commit
d571159aeb

+ 1 - 0
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)
 {
 }
 

+ 6 - 0
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 {

+ 12 - 6
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();
+}
+
 }

+ 4 - 6
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;

+ 10 - 10
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);
         }));
 

+ 16 - 3
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);
+}
+
 }

+ 17 - 3
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 };
 };
 

+ 4 - 4
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();
     }
 }