From 103d780e8c782eec29f971d6de5a7ba036cd694e Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Sat, 29 Nov 2025 19:36:21 +0100 Subject: [PATCH] Added status labels in image editor and implemented flood fill --- src/editor/dialogs/editspritedialog.cpp | 4 +- src/editor/dialogs/editspritedialog.h | 2 +- src/editor/dialogs/imageeditordialog.cpp | 114 ++++++++++++++--- src/editor/dialogs/imageeditordialog.h | 15 ++- src/editor/mainwindow.cpp | 30 ++--- src/editor/mainwindow.h | 4 +- src/editor/widgets/drawingcanvaswidget.cpp | 142 +++++++++++++++++---- src/editor/widgets/drawingcanvaswidget.h | 48 +++++-- src/editor/widgets/zoomablescrollarea.cpp | 8 +- 9 files changed, 284 insertions(+), 83 deletions(-) diff --git a/src/editor/dialogs/editspritedialog.cpp b/src/editor/dialogs/editspritedialog.cpp index dccfe42..4545e8a 100644 --- a/src/editor/dialogs/editspritedialog.cpp +++ b/src/editor/dialogs/editspritedialog.cpp @@ -35,7 +35,7 @@ EditSpriteDialog::EditSpriteDialog(const std::vector &pixmaps, const QS connect(m_ui->actionNew, &QAction::triggered, this, &EditSpriteDialog::newSprite); - connect(m_ui->listView, &QListView::doubleClicked, this, &EditSpriteDialog::doubleClicked); + connect(m_ui->listView, &QAbstractItemView::activated, this, &EditSpriteDialog::activated); } EditSpriteDialog::~EditSpriteDialog() = default; @@ -99,7 +99,7 @@ void EditSpriteDialog::newSprite() } } -void EditSpriteDialog::doubleClicked(const QModelIndex &index) +void EditSpriteDialog::activated(const QModelIndex &index) { if (!index.isValid()) { diff --git a/src/editor/dialogs/editspritedialog.h b/src/editor/dialogs/editspritedialog.h index 4947d78..bf2428a 100644 --- a/src/editor/dialogs/editspritedialog.h +++ b/src/editor/dialogs/editspritedialog.h @@ -25,7 +25,7 @@ public: private slots: void newSprite(); - void doubleClicked(const QModelIndex &index); + void activated(const QModelIndex &index); void changed(); diff --git a/src/editor/dialogs/imageeditordialog.cpp b/src/editor/dialogs/imageeditordialog.cpp index 515e1fa..412d763 100644 --- a/src/editor/dialogs/imageeditordialog.cpp +++ b/src/editor/dialogs/imageeditordialog.cpp @@ -10,8 +10,11 @@ ImageEditorDialog::ImageEditorDialog(const QPixmap &pixmap, const QString &title, QWidget *parent) : QDialog{parent}, m_ui{std::make_unique()}, - m_pixmap{pixmap}, - m_title{title} + m_title{title}, + m_posLabel{*new QLabel(tr("(%0,%1)").arg(0).arg(0))}, + m_zoomLabel{*new QLabel}, + m_sizeLabel{*new QLabel}, + m_memoryLabel{*new QLabel} { m_ui->setupUi(this); @@ -54,21 +57,25 @@ ImageEditorDialog::ImageEditorDialog(const QPixmap &pixmap, const QString &title { auto group = new QButtonGroup{this}; group->setExclusive(true); - group->addButton(m_ui->toolButtonDraw); - group->addButton(m_ui->toolButtonSpray); - group->addButton(m_ui->toolButtonErase); - group->addButton(m_ui->toolButtonPick); - group->addButton(m_ui->toolButtonLine); - group->addButton(m_ui->toolButtonPolygon); - group->addButton(m_ui->toolButtonRectangle); - group->addButton(m_ui->toolButtonEllipse); - group->addButton(m_ui->toolButtonRoundedRectangle); - group->addButton(m_ui->toolButtonSelectRegion); - group->addButton(m_ui->toolButtonSelectWand); - group->addButton(m_ui->toolButtonSelectSpray); - group->addButton(m_ui->toolButtonText); - group->addButton(m_ui->toolButtonFill); - group->addButton(m_ui->toolButtonReplace); + group->addButton(m_ui->toolButtonDraw, std::to_underlying(DrawingCanvasWidget::Mode::Draw)); + group->addButton(m_ui->toolButtonSpray, std::to_underlying(DrawingCanvasWidget::Mode::Spray)); + group->addButton(m_ui->toolButtonErase, std::to_underlying(DrawingCanvasWidget::Mode::Erase)); + group->addButton(m_ui->toolButtonPick, std::to_underlying(DrawingCanvasWidget::Mode::Pick)); + group->addButton(m_ui->toolButtonLine, std::to_underlying(DrawingCanvasWidget::Mode::Line)); + group->addButton(m_ui->toolButtonPolygon, std::to_underlying(DrawingCanvasWidget::Mode::Polygon)); + group->addButton(m_ui->toolButtonRectangle, std::to_underlying(DrawingCanvasWidget::Mode::Rectangle)); + group->addButton(m_ui->toolButtonEllipse, std::to_underlying(DrawingCanvasWidget::Mode::Ellipse)); + group->addButton(m_ui->toolButtonRoundedRectangle, std::to_underlying(DrawingCanvasWidget::Mode::RoundedRectangle)); + group->addButton(m_ui->toolButtonSelectRegion, std::to_underlying(DrawingCanvasWidget::Mode::SelectRegion)); + group->addButton(m_ui->toolButtonSelectWand, std::to_underlying(DrawingCanvasWidget::Mode::SelectWand)); + group->addButton(m_ui->toolButtonSelectSpray, std::to_underlying(DrawingCanvasWidget::Mode::SelectSpray)); + group->addButton(m_ui->toolButtonText, std::to_underlying(DrawingCanvasWidget::Mode::Text)); + group->addButton(m_ui->toolButtonFill, std::to_underlying(DrawingCanvasWidget::Mode::Fill)); + group->addButton(m_ui->toolButtonReplace, std::to_underlying(DrawingCanvasWidget::Mode::Replace)); + if (auto button = group->button(std::to_underlying(m_ui->canvas->mode()))) + button->click(); + connect(group, &QButtonGroup::idClicked, + m_ui->canvas, [canvas=m_ui->canvas](int id){ canvas->setMode(DrawingCanvasWidget::Mode(id)); }); } { @@ -96,16 +103,39 @@ ImageEditorDialog::ImageEditorDialog(const QPixmap &pixmap, const QString &title group->addButton(m_ui->toolButtonAlignRight); } - m_ui->toolButtonDraw->click(); - updateLeftButtonColor(m_ui->canvas->leftButtonColor()); updateRightButtonColor(m_ui->canvas->rightButtonColor()); - m_ui->canvas->setPixmap(m_pixmap); + m_ui->canvas->setPixmap(pixmap); + + connect(m_ui->canvas, &DrawingCanvasWidget::cursorMoved, + this, &ImageEditorDialog::cursorMoved); + + connect(m_ui->canvas, &DrawingCanvasWidget::modeChanged, + this, &ImageEditorDialog::modeChanged); + modeChanged(m_ui->canvas->mode()); + + connect(m_ui->canvas, &DrawingCanvasWidget::zoomChanged, + this, &ImageEditorDialog::zoomChanged); + zoomChanged(m_ui->canvas->zoom()); + + updateSize(pixmap); + + for (QLabel *label : { &m_posLabel, &m_zoomLabel, &m_sizeLabel, &m_memoryLabel }) + { + label->setFrameShape(QFrame::Panel); + label->setFrameShadow(QFrame::Sunken); + m_ui->statusBar->addPermanentWidget(label); + } } ImageEditorDialog::~ImageEditorDialog() = default; +QPixmap ImageEditorDialog::pixmap() const +{ + return m_ui->canvas->pixmap(); +} + void ImageEditorDialog::accept() { // TODO @@ -191,6 +221,44 @@ void ImageEditorDialog::updateRightButtonColor(const QColor &rightButtonColor) frame->setPalette(palette); } +void ImageEditorDialog::modeChanged(DrawingCanvasWidget::Mode mode) +{ + QString msg; + switch (mode) + { + case DrawingCanvasWidget::Mode::Draw: msg = tr("Paint with the mouse. for hor/vert"); break; + case DrawingCanvasWidget::Mode::Spray: msg = tr("Spray with the mouse. for hor/vert"); break; + case DrawingCanvasWidget::Mode::Erase: msg = tr("Erase with the mouse (based on transparency)"); break; + case DrawingCanvasWidget::Mode::Pick: msg = tr("Pick a color with the mouse"); break; + case DrawingCanvasWidget::Mode::Line: msg = tr("Draw a line. for diagonal"); break; + case DrawingCanvasWidget::Mode::Polygon: msg = tr("Draw a polygon. to end"); break; + case DrawingCanvasWidget::Mode::Rectangle: msg = tr("Draw a rectangle. for square"); break; + case DrawingCanvasWidget::Mode::Ellipse: msg = tr("Draw an ellipse. for circle"); break; + case DrawingCanvasWidget::Mode::RoundedRectangle: msg = tr("Draw a rounded rectangle. for square"); break; + case DrawingCanvasWidget::Mode::SelectRegion: msg = tr("Select a region with the mouse"); break; + case DrawingCanvasWidget::Mode::SelectWand: msg = tr("Click to select with magic wand"); break; + case DrawingCanvasWidget::Mode::SelectSpray: msg = tr("Spray selection with the mouse"); break; + case DrawingCanvasWidget::Mode::Text: msg = tr("Click the mouse to start a new text"); break; + case DrawingCanvasWidget::Mode::Fill: msg = tr("Flood filling a region"); break; + case DrawingCanvasWidget::Mode::Replace: msg = tr("Click on a pixel whose color to change"); break; + }; + + if (msg.isNull()) + qWarning() << "unknown mode" << mode; + + m_ui->statusBar->showMessage(msg); +} + +void ImageEditorDialog::cursorMoved(const QPoint &pos) +{ + m_posLabel.setText(tr("(%0,%1)").arg(pos.x()).arg(pos.y())); +} + +void ImageEditorDialog::zoomChanged(float zoom) +{ + m_zoomLabel.setText(tr("Zoom: %0%").arg(zoom * 100.f, 0, 'f', 0)); +} + void ImageEditorDialog::updateTitle() { setWindowTitle(tr("%0%1") @@ -198,3 +266,9 @@ void ImageEditorDialog::updateTitle() .arg(m_unsavedChanges ? tr("*") : QString{}) ); } + +void ImageEditorDialog::updateSize(const QPixmap &pixmap) +{ + m_sizeLabel.setText(tr("Size: %0 x %1").arg(pixmap.width()).arg(pixmap.height())); + m_memoryLabel.setText(tr("Memory: %0 KB").arg(pixmap.width() * pixmap.height() * 4 / 1000)); +} diff --git a/src/editor/dialogs/imageeditordialog.h b/src/editor/dialogs/imageeditordialog.h index 08f61c9..1e610f7 100644 --- a/src/editor/dialogs/imageeditordialog.h +++ b/src/editor/dialogs/imageeditordialog.h @@ -6,6 +6,9 @@ #include +#include "widgets/drawingcanvaswidget.h" + +class QLabel; namespace Ui { class ImageEditorDialog; } class ImageEditorDialog : public QDialog @@ -16,7 +19,7 @@ public: explicit ImageEditorDialog(const QPixmap &pixmap, const QString &title, QWidget *parent = nullptr); ~ImageEditorDialog(); - const QPixmap &pixmap() const { return m_pixmap; } + QPixmap pixmap() const; void accept() override; void reject() override; @@ -28,14 +31,22 @@ private slots: void selectRightButtonColor(); void updateLeftButtonColor(const QColor &leftButtonColor); void updateRightButtonColor(const QColor &rightButtonColor); + void modeChanged(DrawingCanvasWidget::Mode mode); + void cursorMoved(const QPoint &pos); + void zoomChanged(float zoom); private: void updateTitle(); + void updateSize(const QPixmap &pixmap); const std::unique_ptr m_ui; - QPixmap m_pixmap; const QString m_title; + QLabel &m_posLabel; + QLabel &m_zoomLabel; + QLabel &m_sizeLabel; + QLabel &m_memoryLabel; + bool m_unsavedChanges{}; }; diff --git a/src/editor/mainwindow.cpp b/src/editor/mainwindow.cpp index 9cb88f6..afee794 100644 --- a/src/editor/mainwindow.cpp +++ b/src/editor/mainwindow.cpp @@ -143,8 +143,8 @@ MainWindow::MainWindow(const QString &filePath, QWidget *parent) : connect(m_ui->treeView, &QTreeView::customContextMenuRequested, this, &MainWindow::contextMenuRequested); - connect(m_ui->treeView, &QTreeView::doubleClicked, - this, &MainWindow::doubleClicked); + connect(m_ui->treeView, &QTreeView::activated, + this, &MainWindow::activated); connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &MainWindow::selectionChanged); @@ -342,7 +342,7 @@ void MainWindow::contextMenuRequested(const QPoint &pos) menu.exec(m_ui->treeView->viewport()->mapToGlobal(pos)); } -void MainWindow::doubleClicked(const QModelIndex &index) +void MainWindow::activated(const QModelIndex &index) { if (m_projectTreeModel->nodeType(index) == ProjectTreeModel::NodeType::Root) { @@ -354,15 +354,15 @@ void MainWindow::doubleClicked(const QModelIndex &index) showExtensionPackages(); return; } - if (doubleClickedFor(index)) return; - if (doubleClickedFor(index)) return; - if (doubleClickedFor(index)) return; - if (doubleClickedFor(index)) return; - if (doubleClickedFor