mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 15:45:08 +00:00
PixelPaint: Allow reordering layer by dragging them in LayerListWidget
This is rather cool! :^)
This commit is contained in:
parent
dc3de47b03
commit
e4b11a23b7
6 changed files with 169 additions and 16 deletions
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue