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:
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);
|
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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue