1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-22 22:15:08 +00:00

PixelPaint: Allow reordering layer by dragging them in LayerListWidget

This is rather cool! :^)
This commit is contained in:
Andreas Kling 2020-05-25 23:48:09 +02:00
parent dc3de47b03
commit e4b11a23b7
6 changed files with 169 additions and 16 deletions

View file

@ -94,6 +94,8 @@ void Image::move_layer_to_back(Layer& layer)
auto index = index_of(layer); auto index = index_of(layer);
m_layers.remove(index); m_layers.remove(index);
m_layers.prepend(layer); m_layers.prepend(layer);
did_modify_layer_stack();
} }
void Image::move_layer_to_front(Layer& layer) 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); auto index = index_of(layer);
m_layers.remove(index); m_layers.remove(index);
m_layers.append(layer); m_layers.append(layer);
did_modify_layer_stack();
} }
void Image::move_layer_down(Layer& layer) void Image::move_layer_down(Layer& layer)
@ -112,6 +116,8 @@ void Image::move_layer_down(Layer& layer)
return; return;
m_layers.remove(index); m_layers.remove(index);
m_layers.insert(index - 1, layer); m_layers.insert(index - 1, layer);
did_modify_layer_stack();
} }
void Image::move_layer_up(Layer& layer) void Image::move_layer_up(Layer& layer)
@ -122,6 +128,25 @@ void Image::move_layer_up(Layer& layer)
return; return;
m_layers.remove(index); m_layers.remove(index);
m_layers.insert(index + 1, layer); 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) void Image::remove_layer(Layer& layer)
@ -132,6 +157,8 @@ void Image::remove_layer(Layer& layer)
for (auto* client : m_clients) for (auto* client : m_clients)
client->image_did_remove_layer(index); client->image_did_remove_layer(index);
did_modify_layer_stack();
} }
void Image::add_client(ImageClient& client) void Image::add_client(ImageClient& client)

View file

@ -45,6 +45,7 @@ public:
virtual void image_did_add_layer(size_t) { } virtual void image_did_add_layer(size_t) { }
virtual void image_did_remove_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(size_t) { }
virtual void image_did_modify_layer_stack() {}
virtual void image_did_change() { } virtual void image_did_change() { }
}; };
@ -68,6 +69,7 @@ public:
void move_layer_to_back(Layer&); void move_layer_to_back(Layer&);
void move_layer_up(Layer&); void move_layer_up(Layer&);
void move_layer_down(Layer&); void move_layer_down(Layer&);
void change_layer_index(size_t old_index, size_t new_index);
void remove_layer(Layer&); void remove_layer(Layer&);
void add_client(ImageClient&); void add_client(ImageClient&);
@ -79,6 +81,7 @@ private:
explicit Image(const Gfx::Size&); explicit Image(const Gfx::Size&);
void did_change(); void did_change();
void did_modify_layer_stack();
size_t index_of(const Layer&) const; size_t index_of(const Layer&) const;

View file

@ -39,10 +39,22 @@ ImageEditor::ImageEditor()
{ {
} }
ImageEditor::~ImageEditor()
{
if (m_image)
m_image->remove_client(*this);
}
void ImageEditor::set_image(RefPtr<Image> image) void ImageEditor::set_image(RefPtr<Image> image)
{ {
if (m_image)
m_image->remove_client(*this);
m_image = move(image); m_image = move(image);
update(); update();
if (m_image)
m_image->add_client(*this);
} }
void ImageEditor::paint_event(GUI::PaintEvent& event) void ImageEditor::paint_event(GUI::PaintEvent& event)

View file

@ -41,6 +41,8 @@ class ImageEditor final
C_OBJECT(ImageEditor); C_OBJECT(ImageEditor);
public: public:
virtual ~ImageEditor() override;
const Image* image() const { return m_image; } const Image* image() const { return m_image; }
Image* image() { return m_image; } Image* image() { return m_image; }

View file

@ -53,14 +53,17 @@ void LayerListWidget::set_image(Image* image)
if (m_image) if (m_image)
m_image->add_client(*this); m_image->add_client(*this);
m_gadgets.clear(); rebuild_gadgets();
}
void LayerListWidget::rebuild_gadgets()
{
m_gadgets.clear();
if (m_image) { if (m_image) {
for (size_t layer_index = 0; layer_index < m_image->layer_count(); ++layer_index) { 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(); relayout_gadgets();
} }
@ -82,45 +85,112 @@ void LayerListWidget::paint_event(GUI::PaintEvent& event)
painter.fill_rect(event.rect(), palette().button()); painter.fill_rect(event.rect(), palette().button());
for (auto& gadget : m_gadgets) { auto paint_gadget = [&](auto& gadget) {
painter.draw_rect(gadget.rect, Color::Black);
auto& layer = m_image->layer(gadget.layer_index); 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); thumbnail_rect.shrink(8, 8);
painter.draw_scaled_bitmap(thumbnail_rect, layer.bitmap(), layer.bitmap().rect()); 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() }; Gfx::Rect text_rect { thumbnail_rect.right() + 10, adjusted_rect.y(), adjusted_rect.width(), adjusted_rect.height() };
text_rect.intersect(gadget.rect); text_rect.intersect(adjusted_rect);
painter.draw_text(text_rect, layer.name(), Gfx::TextAlignment::CenterLeft); 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 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 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 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) 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)); m_gadgets.insert(layer_index, move(gadget));
relayout_gadgets(); relayout_gadgets();
} }
void LayerListWidget::image_did_remove_layer(size_t layer_index) 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); m_gadgets.remove(layer_index);
relayout_gadgets(); relayout_gadgets();
} }
@ -130,15 +200,40 @@ void LayerListWidget::image_did_modify_layer(size_t layer_index)
update(m_gadgets[layer_index].rect); 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() void LayerListWidget::relayout_gadgets()
{ {
constexpr int gadget_height = 30;
constexpr int gadget_spacing = 1;
int y = 0; 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) { 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 }; gadget.rect = { 0, y, width(), gadget_height };
y += gadget_height + gadget_spacing; y += vertical_step;
++index;
} }
update(); update();

View file

@ -52,17 +52,31 @@ private:
virtual void image_did_add_layer(size_t) override; virtual void image_did_add_layer(size_t) override;
virtual void image_did_remove_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(); void relayout_gadgets();
size_t hole_index_during_move() const;
struct Gadget { struct Gadget {
size_t layer_index { 0 }; size_t layer_index { 0 };
Gfx::Rect rect; 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; Vector<Gadget> m_gadgets;
RefPtr<Image> m_image; RefPtr<Image> m_image;
Optional<size_t> m_moving_gadget_index;
Gfx::Point m_moving_event_origin;
}; };
} }