diff options
author | Andreas Kling <kling@serenityos.org> | 2020-05-25 23:48:09 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-05-26 10:18:03 +0200 |
commit | e4b11a23b709b9c811087b65fe2a33b88a32c7f3 (patch) | |
tree | 17fa8e70e52d43390bd0358d8d90003d8763f70b | |
parent | dc3de47b039ffa84c397991a386fb54786bfe53e (diff) | |
download | serenity-e4b11a23b709b9c811087b65fe2a33b88a32c7f3.zip |
PixelPaint: Allow reordering layer by dragging them in LayerListWidget
This is rather cool! :^)
-rw-r--r-- | Applications/PixelPaint/Image.cpp | 27 | ||||
-rw-r--r-- | Applications/PixelPaint/Image.h | 3 | ||||
-rw-r--r-- | Applications/PixelPaint/ImageEditor.cpp | 12 | ||||
-rw-r--r-- | Applications/PixelPaint/ImageEditor.h | 2 | ||||
-rw-r--r-- | Applications/PixelPaint/LayerListWidget.cpp | 125 | ||||
-rw-r--r-- | Applications/PixelPaint/LayerListWidget.h | 16 |
6 files changed, 169 insertions, 16 deletions
diff --git a/Applications/PixelPaint/Image.cpp b/Applications/PixelPaint/Image.cpp index fa55c563d6..f9ee42dac9 100644 --- a/Applications/PixelPaint/Image.cpp +++ b/Applications/PixelPaint/Image.cpp @@ -94,6 +94,8 @@ void Image::move_layer_to_back(Layer& layer) auto index = index_of(layer); m_layers.remove(index); m_layers.prepend(layer); + + did_modify_layer_stack(); } void Image::move_layer_to_front(Layer& layer) @@ -102,6 +104,8 @@ void Image::move_layer_to_front(Layer& layer) auto index = index_of(layer); m_layers.remove(index); m_layers.append(layer); + + did_modify_layer_stack(); } void Image::move_layer_down(Layer& layer) @@ -112,6 +116,8 @@ void Image::move_layer_down(Layer& layer) return; m_layers.remove(index); m_layers.insert(index - 1, layer); + + did_modify_layer_stack(); } void Image::move_layer_up(Layer& layer) @@ -122,6 +128,25 @@ void Image::move_layer_up(Layer& layer) return; m_layers.remove(index); m_layers.insert(index + 1, layer); + + did_modify_layer_stack(); +} + +void Image::change_layer_index(size_t old_index, size_t new_index) +{ + ASSERT(old_index < m_layers.size()); + ASSERT(new_index < m_layers.size()); + auto layer = m_layers.take(old_index); + m_layers.insert(new_index, move(layer)); + did_modify_layer_stack(); +} + +void Image::did_modify_layer_stack() +{ + for (auto* client : m_clients) + client->image_did_modify_layer_stack(); + + did_change(); } void Image::remove_layer(Layer& layer) @@ -132,6 +157,8 @@ void Image::remove_layer(Layer& layer) for (auto* client : m_clients) client->image_did_remove_layer(index); + + did_modify_layer_stack(); } void Image::add_client(ImageClient& client) diff --git a/Applications/PixelPaint/Image.h b/Applications/PixelPaint/Image.h index 56adb3a089..28d3ae9cb4 100644 --- a/Applications/PixelPaint/Image.h +++ b/Applications/PixelPaint/Image.h @@ -45,6 +45,7 @@ public: virtual void image_did_add_layer(size_t) { } virtual void image_did_remove_layer(size_t) { } virtual void image_did_modify_layer(size_t) { } + virtual void image_did_modify_layer_stack() {} virtual void image_did_change() { } }; @@ -68,6 +69,7 @@ public: void move_layer_to_back(Layer&); void move_layer_up(Layer&); void move_layer_down(Layer&); + void change_layer_index(size_t old_index, size_t new_index); void remove_layer(Layer&); void add_client(ImageClient&); @@ -79,6 +81,7 @@ private: explicit Image(const Gfx::Size&); void did_change(); + void did_modify_layer_stack(); size_t index_of(const Layer&) const; diff --git a/Applications/PixelPaint/ImageEditor.cpp b/Applications/PixelPaint/ImageEditor.cpp index 6a503d3bb3..5959eb80fe 100644 --- a/Applications/PixelPaint/ImageEditor.cpp +++ b/Applications/PixelPaint/ImageEditor.cpp @@ -39,10 +39,22 @@ ImageEditor::ImageEditor() { } +ImageEditor::~ImageEditor() +{ + if (m_image) + m_image->remove_client(*this); +} + void ImageEditor::set_image(RefPtr<Image> image) { + if (m_image) + m_image->remove_client(*this); + m_image = move(image); update(); + + if (m_image) + m_image->add_client(*this); } void ImageEditor::paint_event(GUI::PaintEvent& event) diff --git a/Applications/PixelPaint/ImageEditor.h b/Applications/PixelPaint/ImageEditor.h index ce28d3b38e..0156bd4b12 100644 --- a/Applications/PixelPaint/ImageEditor.h +++ b/Applications/PixelPaint/ImageEditor.h @@ -41,6 +41,8 @@ class ImageEditor final C_OBJECT(ImageEditor); public: + virtual ~ImageEditor() override; + const Image* image() const { return m_image; } Image* image() { return m_image; } diff --git a/Applications/PixelPaint/LayerListWidget.cpp b/Applications/PixelPaint/LayerListWidget.cpp index 42b14aee84..716f089d11 100644 --- a/Applications/PixelPaint/LayerListWidget.cpp +++ b/Applications/PixelPaint/LayerListWidget.cpp @@ -53,14 +53,17 @@ void LayerListWidget::set_image(Image* image) if (m_image) m_image->add_client(*this); - m_gadgets.clear(); + rebuild_gadgets(); +} +void LayerListWidget::rebuild_gadgets() +{ + m_gadgets.clear(); if (m_image) { for (size_t layer_index = 0; layer_index < m_image->layer_count(); ++layer_index) { - m_gadgets.append({ layer_index, {} }); + m_gadgets.append({ layer_index, {}, {}, false, {} }); } } - relayout_gadgets(); } @@ -82,45 +85,112 @@ void LayerListWidget::paint_event(GUI::PaintEvent& event) painter.fill_rect(event.rect(), palette().button()); - for (auto& gadget : m_gadgets) { - painter.draw_rect(gadget.rect, Color::Black); + auto paint_gadget = [&](auto& gadget) { auto& layer = m_image->layer(gadget.layer_index); - Gfx::Rect thumbnail_rect { gadget.rect.x(), gadget.rect.y(), gadget.rect.height(), gadget.rect.height() }; + auto adjusted_rect = gadget.rect; + + if (gadget.is_moving) { + adjusted_rect.move_by(0, gadget.movement_delta.y()); + painter.fill_rect(adjusted_rect, palette().threed_shadow1()); + } + + painter.draw_rect(adjusted_rect, Color::Black); + + Gfx::Rect thumbnail_rect { adjusted_rect.x(), adjusted_rect.y(), adjusted_rect.height(), adjusted_rect.height() }; thumbnail_rect.shrink(8, 8); painter.draw_scaled_bitmap(thumbnail_rect, layer.bitmap(), layer.bitmap().rect()); - Gfx::Rect text_rect { thumbnail_rect.right() + 10, gadget.rect.y(), gadget.rect.width(), gadget.rect.height() }; - text_rect.intersect(gadget.rect); + Gfx::Rect text_rect { thumbnail_rect.right() + 10, adjusted_rect.y(), adjusted_rect.width(), adjusted_rect.height() }; + text_rect.intersect(adjusted_rect); painter.draw_text(text_rect, layer.name(), Gfx::TextAlignment::CenterLeft); + }; + + for (auto& gadget : m_gadgets) { + if (!gadget.is_moving) + paint_gadget(gadget); + } + + if (m_moving_gadget_index.has_value()) + paint_gadget(m_gadgets[m_moving_gadget_index.value()]); +} + +Optional<size_t> LayerListWidget::gadget_at(const Gfx::Point& position) +{ + for (size_t i = 0; i < m_gadgets.size(); ++i) { + if (m_gadgets[i].rect.contains(position)) + return i; } + return {}; } void LayerListWidget::mousedown_event(GUI::MouseEvent& event) { - (void)event; + if (!m_image) + return; + if (event.button() != GUI::MouseButton::Left) + return; + auto gadget_index = gadget_at(event.position()); + if (!gadget_index.has_value()) + return; + m_moving_gadget_index = gadget_index; + m_moving_event_origin = event.position(); + auto& gadget = m_gadgets[m_moving_gadget_index.value()]; + gadget.is_moving = true; + gadget.movement_delta = {}; + update(); } void LayerListWidget::mousemove_event(GUI::MouseEvent& event) { - (void)event; + if (!m_image) + return; + if (!m_moving_gadget_index.has_value()) + return; + + auto delta = event.position() - m_moving_event_origin; + auto& gadget = m_gadgets[m_moving_gadget_index.value()]; + ASSERT(gadget.is_moving); + gadget.movement_delta = delta; + relayout_gadgets(); } void LayerListWidget::mouseup_event(GUI::MouseEvent& event) { - (void)event; + if (!m_image) + return; + if (event.button() != GUI::MouseButton::Left) + return; + if (!m_moving_gadget_index.has_value()) + return; + + size_t old_index = m_moving_gadget_index.value(); + size_t new_index = hole_index_during_move(); + if (new_index >= m_image->layer_count()) + new_index = m_image->layer_count() - 1; + + m_moving_gadget_index = {}; + m_image->change_layer_index(old_index, new_index); } void LayerListWidget::image_did_add_layer(size_t layer_index) { - Gadget gadget { layer_index, {} }; + if (m_moving_gadget_index.has_value()) { + m_gadgets[m_moving_gadget_index.value()].is_moving = false; + m_moving_gadget_index = {}; + } + Gadget gadget { layer_index, {}, {}, false, {} }; m_gadgets.insert(layer_index, move(gadget)); relayout_gadgets(); } void LayerListWidget::image_did_remove_layer(size_t layer_index) { + if (m_moving_gadget_index.has_value()) { + m_gadgets[m_moving_gadget_index.value()].is_moving = false; + m_moving_gadget_index = {}; + } m_gadgets.remove(layer_index); relayout_gadgets(); } @@ -130,15 +200,40 @@ void LayerListWidget::image_did_modify_layer(size_t layer_index) update(m_gadgets[layer_index].rect); } +void LayerListWidget::image_did_modify_layer_stack() +{ + rebuild_gadgets(); +} + +static constexpr int gadget_height = 30; +static constexpr int gadget_spacing = 1; +static constexpr int vertical_step = gadget_height + gadget_spacing; + +size_t LayerListWidget::hole_index_during_move() const +{ + ASSERT(is_moving_gadget()); + auto& moving_gadget = m_gadgets[m_moving_gadget_index.value()]; + int center_y_of_moving_gadget = moving_gadget.rect.translated(0, moving_gadget.movement_delta.y()).center().y(); + return center_y_of_moving_gadget / vertical_step; +} + void LayerListWidget::relayout_gadgets() { - constexpr int gadget_height = 30; - constexpr int gadget_spacing = 1; int y = 0; + Optional<size_t> hole_index; + if (is_moving_gadget()) + hole_index = hole_index_during_move(); + + size_t index = 0; for (auto& gadget : m_gadgets) { + if (gadget.is_moving) + continue; + if (hole_index.has_value() && index == hole_index.value()) + y += vertical_step; gadget.rect = { 0, y, width(), gadget_height }; - y += gadget_height + gadget_spacing; + y += vertical_step; + ++index; } update(); diff --git a/Applications/PixelPaint/LayerListWidget.h b/Applications/PixelPaint/LayerListWidget.h index a1f86dc37c..22cff6d635 100644 --- a/Applications/PixelPaint/LayerListWidget.h +++ b/Applications/PixelPaint/LayerListWidget.h @@ -52,17 +52,31 @@ private: virtual void image_did_add_layer(size_t) override; virtual void image_did_remove_layer(size_t) override; - virtual void image_did_modify_layer(size_t); + virtual void image_did_modify_layer(size_t) override; + virtual void image_did_modify_layer_stack() override; + void rebuild_gadgets(); void relayout_gadgets(); + size_t hole_index_during_move() const; + struct Gadget { size_t layer_index { 0 }; Gfx::Rect rect; + Gfx::Rect temporary_rect_during_move; + bool is_moving { false }; + Gfx::Point movement_delta; }; + bool is_moving_gadget() const { return m_moving_gadget_index.has_value(); } + + Optional<size_t> gadget_at(const Gfx::Point&); + Vector<Gadget> m_gadgets; RefPtr<Image> m_image; + + Optional<size_t> m_moving_gadget_index; + Gfx::Point m_moving_event_origin; }; } |