diff --git a/forms/resizelayoutpopup.ui b/forms/resizelayoutpopup.ui
new file mode 100644
index 00000000..c98fe10f
--- /dev/null
+++ b/forms/resizelayoutpopup.ui
@@ -0,0 +1,276 @@
+
+
+ ResizeLayoutPopup
+
+
+
+ 0
+ 0
+ 598
+ 378
+
+
+
+ Dialog
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ Resize Layout
+
+
+ Qt::AlignCenter
+
+
+ 8
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset
+
+
+ true
+
+
+
+ -
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Width
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 64
+ 0
+
+
+
+
+ -
+
+
+ Height
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 64
+ 0
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Border Width
+
+
+
+ -
+
+
+
+ 64
+ 0
+
+
+
+
+ -
+
+
+ Border Height
+
+
+
+ -
+
+
+
+ 64
+ 0
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+ NoScrollSpinBox
+ QSpinBox
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ ResizeLayoutPopup
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ ResizeLayoutPopup
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/include/core/editcommands.h b/include/core/editcommands.h
index 5bf99784..83e33d41 100644
--- a/include/core/editcommands.h
+++ b/include/core/editcommands.h
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
class Map;
class Layout;
@@ -203,7 +204,12 @@ private:
/// Implements a command to commit a map or border resize action.
class ResizeLayout : public QUndoCommand {
public:
- ResizeLayout(Layout *layout, QSize oldLayoutDimensions, QSize newLayoutDimensions,
+ // ResizeLayout(Layout *layout, QSize oldLayoutDimensions, QSize newLayoutDimensions,
+ // const Blockdata &oldMetatiles, const Blockdata &newMetatiles,
+ // QSize oldBorderDimensions, QSize newBorderDimensions,
+ // const Blockdata &oldBorder, const Blockdata &newBorder,
+ // QUndoCommand *parent = nullptr);
+ ResizeLayout(Layout *layout, QSize oldLayoutDimensions, QMargins newLayoutMargins,
const Blockdata &oldMetatiles, const Blockdata &newMetatiles,
QSize oldBorderDimensions, QSize newBorderDimensions,
const Blockdata &oldBorder, const Blockdata &newBorder,
@@ -220,8 +226,7 @@ private:
int oldLayoutWidth;
int oldLayoutHeight;
- int newLayoutWidth;
- int newLayoutHeight;
+ QMargins newLayoutMargins;
int oldBorderWidth;
int oldBorderHeight;
diff --git a/include/core/maplayout.h b/include/core/maplayout.h
index b617002f..0e9bc243 100644
--- a/include/core/maplayout.h
+++ b/include/core/maplayout.h
@@ -96,6 +96,7 @@ public:
void setBlock(int x, int y, Block block, bool enableScriptCallback = false);
void setBlockdata(Blockdata blockdata, bool enableScriptCallback = false);
+ void adjustDimensions(QMargins margins, bool setNewBlockdata = true, bool enableScriptCallback = false);
void setDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false);
void setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata = true, bool enableScriptCallback = false);
diff --git a/include/ui/movablerect.h b/include/ui/movablerect.h
index efd85847..c5ca00ab 100644
--- a/include/ui/movablerect.h
+++ b/include/ui/movablerect.h
@@ -5,12 +5,13 @@
#include
#include
-class MovableRect : public QGraphicsItem
+
+
+class MovableRect : public QGraphicsRectItem
{
public:
MovableRect(bool *enabled, int width, int height, QRgb color);
- QRectF boundingRect() const override
- {
+ QRectF boundingRect() const override {
qreal penWidth = 4;
return QRectF(-penWidth,
-penWidth,
@@ -18,21 +19,62 @@ public:
20 * 8 + penWidth * 2);
}
- void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
- {
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override {
if (!(*enabled)) return;
painter->setPen(this->color);
- painter->drawRect(x() - 2, y() - 2, this->width + 3, this->height + 3);
+ painter->drawRect(this->rect().x() - 2, this->rect().y() - 2, this->rect().width() + 3, this->rect().height() + 3);
painter->setPen(QColor(0, 0, 0));
- painter->drawRect(x() - 3, y() - 3, this->width + 5, this->height + 5);
- painter->drawRect(x() - 1, y() - 1, this->width + 1, this->height + 1);
+ painter->drawRect(this->rect().x() - 3, this->rect().y() - 3, this->rect().width() + 5, this->rect().height() + 5);
+ painter->drawRect(this->rect().x() - 1, this->rect().y() - 1, this->rect().width() + 1, this->rect().height() + 1);
}
void updateLocation(int x, int y);
bool *enabled;
-private:
- int width;
- int height;
+
+protected:
QRgb color;
};
+
+
+/// A MovableRect with the addition of being resizable.
+class ResizableRect : public QObject, public MovableRect
+{
+ Q_OBJECT
+public:
+ ResizableRect(QObject *parent, bool *enabled, int width, int height, QRgb color);
+
+ QRectF boundingRect() const override {
+ return QRectF(this->rect() + QMargins(lineWidth, lineWidth, lineWidth, lineWidth));
+ }
+
+ QPainterPath shape() const override {
+ QPainterPath path;
+ path.addRect(this->rect() + QMargins(lineWidth, lineWidth, lineWidth, lineWidth));
+ path.addRect(this->rect() - QMargins(lineWidth, lineWidth, lineWidth, lineWidth));
+ return path;
+ }
+
+ void updatePosFromRect(QRect newPos);
+
+protected:
+ void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
+ void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
+ void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
+
+private:
+ enum class Edge { None, Left, Right, Top, Bottom, TopLeft, BottomLeft, TopRight, BottomRight };
+ ResizableRect::Edge detectEdge(int x, int y);
+
+ // Variables for keeping state of original rect while resizing
+ ResizableRect::Edge clickedEdge = ResizableRect::Edge::None;
+ QPointF clickedPos = QPointF();
+ QRect clickedRect;
+
+ int lineWidth = 8;
+
+signals:
+ void rectUpdated(QRect rect);
+};
+
#endif // MOVABLERECT_H
diff --git a/include/ui/noscrollspinbox.h b/include/ui/noscrollspinbox.h
index 0cc11043..0615da5a 100644
--- a/include/ui/noscrollspinbox.h
+++ b/include/ui/noscrollspinbox.h
@@ -12,6 +12,8 @@ public:
void wheelEvent(QWheelEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
+ void setLineEditEnabled(bool enabled);
+
unsigned getActionId();
private:
diff --git a/include/ui/resizelayoutpopup.h b/include/ui/resizelayoutpopup.h
new file mode 100644
index 00000000..de48c993
--- /dev/null
+++ b/include/ui/resizelayoutpopup.h
@@ -0,0 +1,106 @@
+#ifndef RESIZELAYOUTPOPUP_H
+#define RESIZELAYOUTPOPUP_H
+
+#include
+#include
+#include
+#include
+#include
+
+class ResizableRect;
+class Editor;
+namespace Ui {
+ class ResizeLayoutPopup;
+}
+
+
+
+/// Custom scene that paints its background a gray checkered pattern.
+/// Additionally there is a definable "valid" area which will paint the checkerboard green inside.
+class CheckeredBgScene : public QGraphicsScene {
+ Q_OBJECT
+
+public:
+ CheckeredBgScene(QObject *parent = nullptr);
+ void setValidRect(int x, int y, int width, int height) {
+ this->validRect = QRect(x * this->gridSize, y * this->gridSize, width * this->gridSize, height * this->gridSize);
+ }
+ void setValidRect(QRect rect) {
+ this->validRect = rect;
+ }
+
+protected:
+ void drawBackground(QPainter *painter, const QRectF &rect) override;
+
+private:
+ int gridSize = 16; // virtual pixels
+ QRect validRect = QRect();
+};
+
+
+
+/// PixmapItem subclass which allows for creating a boundary which determine whether
+/// the pixmap paints normally or with a black tint.
+/// This item is movable and snaps on a 16x16 grid.
+class BoundedPixmapItem : public QGraphicsPixmapItem {
+public:
+ BoundedPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent = nullptr);
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override;
+
+ void setBoundary(ResizableRect *rect) { this->boundary = rect; }
+
+protected:
+ QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
+
+private:
+ ResizableRect *boundary = nullptr;
+ QPointF clickedPos = QPointF();
+};
+
+
+
+/// The main (modal) dialog window for resizing layout and border dimensions.
+/// The dialog itself is minimal, and is connected to the parent widget's geometry.
+class ResizeLayoutPopup : public QDialog
+{
+ Q_OBJECT
+
+public:
+ ResizeLayoutPopup(QWidget *parent, Editor *editor);
+ ~ResizeLayoutPopup();
+
+ void setupLayoutView();
+
+ void resetPosition();
+
+ QMargins getResult();
+ QSize getBorderResult();
+
+protected:
+ void moveEvent(QMoveEvent *) override {
+ // Prevent the dialog from being moved
+ this->resetPosition();
+ }
+
+ void resizeEvent(QResizeEvent *) override {
+ // Prevent the dialog from being resized
+ this->resetPosition();
+ }
+
+private slots:
+ void on_spinBox_width_valueChanged(int value);
+ void on_spinBox_height_valueChanged(int value);
+
+private:
+ QWidget *parent = nullptr;
+ Editor *editor = nullptr;
+
+ Ui::ResizeLayoutPopup *ui;
+
+ ResizableRect *outline = nullptr;
+ BoundedPixmapItem *layoutPixmap = nullptr;
+
+ QPointer scene = nullptr;
+};
+
+#endif // RESIZELAYOUTPOPUP_H
diff --git a/porymap.pro b/porymap.pro
index 1b1c693e..ce2d1e84 100644
--- a/porymap.pro
+++ b/porymap.pro
@@ -22,6 +22,7 @@ VERSION = 5.4.1
DEFINES += PORYMAP_VERSION=\\\"$$VERSION\\\"
SOURCES += src/core/block.cpp \
+ src/ui/resizelayoutpopup.cpp \
src/core/bitpacker.cpp \
src/core/blockdata.cpp \
src/core/events.cpp \
@@ -225,7 +226,8 @@ HEADERS += include/core/block.h \
include/log.h \
include/ui/uintspinbox.h \
include/ui/updatepromoter.h \
- include/ui/wildmonchart.h
+ include/ui/wildmonchart.h \
+ include/ui/resizelayoutpopup.h
FORMS += forms/mainwindow.ui \
forms/colorinputwidget.ui \
@@ -250,7 +252,8 @@ FORMS += forms/mainwindow.ui \
forms/customscriptseditor.ui \
forms/customscriptslistitem.ui \
forms/updatepromoter.ui \
- forms/wildmonchart.ui
+ forms/wildmonchart.ui \
+ forms/resizelayoutpopup.ui
RESOURCES += \
resources/images.qrc \
diff --git a/src/core/editcommands.cpp b/src/core/editcommands.cpp
index c500c8c0..17cec2df 100644
--- a/src/core/editcommands.cpp
+++ b/src/core/editcommands.cpp
@@ -177,7 +177,7 @@ bool ShiftMetatiles::mergeWith(const QUndoCommand *command) {
************************************************************************
******************************************************************************/
-ResizeLayout::ResizeLayout(Layout *layout, QSize oldLayoutDimensions, QSize newLayoutDimensions,
+ResizeLayout::ResizeLayout(Layout *layout, QSize oldLayoutDimensions, QMargins newLayoutMargins,
const Blockdata &oldMetatiles, const Blockdata &newMetatiles,
QSize oldBorderDimensions, QSize newBorderDimensions,
const Blockdata &oldBorder, const Blockdata &newBorder,
@@ -189,8 +189,7 @@ ResizeLayout::ResizeLayout(Layout *layout, QSize oldLayoutDimensions, QSize newL
this->oldLayoutWidth = oldLayoutDimensions.width();
this->oldLayoutHeight = oldLayoutDimensions.height();
- this->newLayoutWidth = newLayoutDimensions.width();
- this->newLayoutHeight = newLayoutDimensions.height();
+ this->newLayoutMargins = newLayoutMargins;
this->oldMetatiles = oldMetatiles;
this->newMetatiles = newMetatiles;
@@ -210,12 +209,14 @@ void ResizeLayout::redo() {
if (!layout) return;
- layout->blockdata = newMetatiles;
- layout->setDimensions(newLayoutWidth, newLayoutHeight, false, true);
-
layout->border = newBorder;
layout->setBorderDimensions(newBorderWidth, newBorderHeight, false, true);
+ layout->width = oldLayoutWidth;
+ layout->height = oldLayoutHeight;
+ layout->adjustDimensions(this->newLayoutMargins, false, true);
+ layout->blockdata = newMetatiles;
+
layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight());
layout->lastCommitBlocks.borderDimensions = QSize(layout->getBorderWidth(), layout->getBorderHeight());
@@ -225,12 +226,14 @@ void ResizeLayout::redo() {
void ResizeLayout::undo() {
if (!layout) return;
- layout->blockdata = oldMetatiles;
- layout->setDimensions(oldLayoutWidth, oldLayoutHeight, false, true);
-
layout->border = oldBorder;
layout->setBorderDimensions(oldBorderWidth, oldBorderHeight, false, true);
+ layout->width = oldLayoutWidth + newLayoutMargins.left() + newLayoutMargins.right();
+ layout->height = oldLayoutHeight + newLayoutMargins.top() + newLayoutMargins.bottom();
+ layout->adjustDimensions(-this->newLayoutMargins, false, true);
+ layout->blockdata = oldMetatiles;
+
layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight());
layout->lastCommitBlocks.borderDimensions = QSize(layout->getBorderWidth(), layout->getBorderHeight());
diff --git a/src/core/maplayout.cpp b/src/core/maplayout.cpp
index 34033ac5..fa55ed77 100644
--- a/src/core/maplayout.cpp
+++ b/src/core/maplayout.cpp
@@ -183,6 +183,34 @@ void Layout::setDimensions(int newWidth, int newHeight, bool setNewBlockdata, bo
emit layoutDimensionsChanged(QSize(getWidth(), getHeight()));
}
+void Layout::adjustDimensions(QMargins margins, bool setNewBlockdata, bool enableScriptCallback) {
+ int oldWidth = this->width;
+ int oldHeight = this->height;
+ int newWidth = this->width + margins.left() + margins.right();
+ int newHeight = this->height + margins.top() + margins.bottom();
+
+ if (setNewBlockdata) {
+ // Fill new blockdata TODO: replace old functions, scripting support, undo etc
+ Blockdata newBlockdata;
+ for (int y = 0; y < newHeight; y++)
+ for (int x = 0; x < newWidth; x++) {
+ if ((x < margins.left()) || (x >= newWidth - margins.right()) || (y < margins.top()) || (y >= newHeight - margins.bottom())) {
+ newBlockdata.append(0);
+ } else {
+ int index = (y - margins.top()) * oldWidth + (x - margins.left());
+ newBlockdata.append(this->blockdata.value(index));
+ }
+ }
+ this->blockdata = newBlockdata;
+ }
+
+ this->width = newWidth;
+ this->height = newHeight;
+
+ emit layoutChanged(this);
+ emit layoutDimensionsChanged(QSize(getWidth(), getHeight()));
+}
+
void Layout::setBorderDimensions(int newWidth, int newHeight, bool setNewBlockdata, bool enableScriptCallback) {
if (setNewBlockdata) {
setNewBorderDimensionsBlockdata(newWidth, newHeight);
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index d9e65eb3..d1d50088 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -23,6 +23,7 @@
#include "newmapconnectiondialog.h"
#include "config.h"
#include "filedialog.h"
+#include "resizelayoutpopup.h"
#include
#include
@@ -2961,88 +2962,31 @@ void MainWindow::on_comboBox_SecondaryTileset_currentTextChanged(const QString &
void MainWindow::on_pushButton_ChangeDimensions_clicked() {
if (!editor || !editor->layout) return;
- QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
- dialog.setWindowTitle("Change Map Dimensions");
- dialog.setWindowModality(Qt::NonModal);
-
- QFormLayout form(&dialog);
-
- QSpinBox *widthSpinBox = new QSpinBox();
- QSpinBox *heightSpinBox = new QSpinBox();
- QSpinBox *bwidthSpinBox = new QSpinBox();
- QSpinBox *bheightSpinBox = new QSpinBox();
- widthSpinBox->setMinimum(1);
- heightSpinBox->setMinimum(1);
- bwidthSpinBox->setMinimum(1);
- bheightSpinBox->setMinimum(1);
- widthSpinBox->setMaximum(editor->project->getMaxMapWidth());
- heightSpinBox->setMaximum(editor->project->getMaxMapHeight());
- bwidthSpinBox->setMaximum(MAX_BORDER_WIDTH);
- bheightSpinBox->setMaximum(MAX_BORDER_HEIGHT);
- widthSpinBox->setValue(editor->layout->getWidth());
- heightSpinBox->setValue(editor->layout->getHeight());
- bwidthSpinBox->setValue(editor->layout->getBorderWidth());
- bheightSpinBox->setValue(editor->layout->getBorderHeight());
- if (projectConfig.useCustomBorderSize) {
- form.addRow(new QLabel("Map Width"), widthSpinBox);
- form.addRow(new QLabel("Map Height"), heightSpinBox);
- form.addRow(new QLabel("Border Width"), bwidthSpinBox);
- form.addRow(new QLabel("Border Height"), bheightSpinBox);
- } else {
- form.addRow(new QLabel("Width"), widthSpinBox);
- form.addRow(new QLabel("Height"), heightSpinBox);
- }
-
- QLabel *errorLabel = new QLabel();
- errorLabel->setStyleSheet("QLabel { color: red }");
- errorLabel->setVisible(false);
-
- QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog);
- form.addRow(&buttonBox);
- connect(&buttonBox, &QDialogButtonBox::accepted, [&dialog, &widthSpinBox, &heightSpinBox, &errorLabel, this](){
- // Ensure width and height are an acceptable size.
- // The maximum number of metatiles in a map is the following:
- // max = (width + 15) * (height + 14)
- // This limit can be found in fieldmap.c in pokeruby/pokeemerald/pokefirered.
- int numMetatiles = editor->project->getMapDataSize(widthSpinBox->value(), heightSpinBox->value());
- int maxMetatiles = editor->project->getMaxMapDataSize();
- if (numMetatiles <= maxMetatiles) {
- dialog.accept();
- } else {
- QString errorText = QString("Error: The specified width and height are too large.\n"
- "The maximum layout width and height is the following: (width + 15) * (height + 14) <= %1\n"
- "The specified layout width and height was: (%2 + 15) * (%3 + 14) = %4")
- .arg(maxMetatiles)
- .arg(widthSpinBox->value())
- .arg(heightSpinBox->value())
- .arg(numMetatiles);
- errorLabel->setText(errorText);
- errorLabel->setVisible(true);
- }
- });
- connect(&buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject);
-
- form.addRow(errorLabel);
-
- if (dialog.exec() == QDialog::Accepted) {
- Layout *layout = editor->layout;
- Blockdata oldMetatiles = layout->blockdata;
- Blockdata oldBorder = layout->border;
- QSize oldMapDimensions(layout->getWidth(), layout->getHeight());
+ ResizeLayoutPopup popup(this->ui->graphicsView_Map, this->editor);
+ popup.show();
+ popup.setupLayoutView();
+ if (popup.exec() == QDialog::Accepted) {
+ Layout *layout = this->editor->layout;
+ QMargins result = popup.getResult();
+ QSize borderResult = popup.getBorderResult();
+ QSize oldLayoutDimensions(layout->getWidth(), layout->getHeight());
QSize oldBorderDimensions(layout->getBorderWidth(), layout->getBorderHeight());
- QSize newMapDimensions(widthSpinBox->value(), heightSpinBox->value());
- QSize newBorderDimensions(bwidthSpinBox->value(), bheightSpinBox->value());
- if (oldMapDimensions != newMapDimensions || oldBorderDimensions != newBorderDimensions) {
- layout->setDimensions(newMapDimensions.width(), newMapDimensions.height(), true, true);
- layout->setBorderDimensions(newBorderDimensions.width(), newBorderDimensions.height(), true, true);
- editor->layout->editHistory.push(new ResizeLayout(layout,
- oldMapDimensions, newMapDimensions,
+ if (!result.isNull() || (borderResult != oldBorderDimensions)) {
+ Blockdata oldMetatiles = layout->blockdata;
+ Blockdata oldBorder = layout->border;
+
+ layout->adjustDimensions(result);
+ layout->setBorderDimensions(borderResult.width(), borderResult.height(), true, true);
+ layout->editHistory.push(new ResizeLayout(layout,
+ oldLayoutDimensions, result,
oldMetatiles, layout->blockdata,
- oldBorderDimensions, newBorderDimensions,
+ oldBorderDimensions, borderResult,
oldBorder, layout->border
));
}
}
+
+ return;
}
void MainWindow::on_checkBox_smartPaths_stateChanged(int selected)
diff --git a/src/ui/movablerect.cpp b/src/ui/movablerect.cpp
index 55327dba..e9e373e0 100644
--- a/src/ui/movablerect.cpp
+++ b/src/ui/movablerect.cpp
@@ -1,17 +1,163 @@
+#include
+#include
+
#include "movablerect.h"
MovableRect::MovableRect(bool *enabled, int width, int height, QRgb color)
+ : QGraphicsRectItem(0, 0, width, height)
{
this->enabled = enabled;
- this->width = width;
- this->height = height;
this->color = color;
this->setVisible(*enabled);
}
-void MovableRect::updateLocation(int x, int y)
-{
- this->setX((x * 16) - this->width / 2 + 8);
- this->setY((y * 16) - this->height / 2 + 8);
+/// Center rect on grid position (x, y)
+void MovableRect::updateLocation(int x, int y) {
+ this->setRect((x * 16) - this->rect().width() / 2 + 8, (y * 16) - this->rect().height() / 2 + 8, this->rect().width(), this->rect().height());
this->setVisible(*this->enabled);
}
+
+/******************************************************************************
+ ************************************************************************
+ ******************************************************************************/
+
+int roundUp(int numToRound, int multiple) {
+ return (numToRound + multiple - 1) & -multiple;
+}
+
+ResizableRect::ResizableRect(QObject *parent, bool *enabled, int width, int height, QRgb color)
+ : QObject(parent),
+ MovableRect(enabled, width * 16, height * 16, color)
+{
+ setZValue(0xFFFFFFFF); // ensure on top of view
+ setAcceptHoverEvents(true);
+ setFlags(this->flags() | QGraphicsItem::ItemIsMovable);
+}
+
+ResizableRect::Edge ResizableRect::detectEdge(int x, int y) {
+ QRectF edge = this->boundingRect();
+ if (x <= edge.left() + this->lineWidth) {
+ if (y >= edge.top() + 2 * this->lineWidth) {
+ if (y <= edge.bottom() - 2 * this->lineWidth) {
+ return ResizableRect::Edge::Left;
+ }
+ else {
+ return ResizableRect::Edge::BottomLeft;
+ }
+ }
+ else {
+ return ResizableRect::Edge::TopLeft;
+ }
+ }
+ else if (x >= edge.right() - this->lineWidth) {
+ if (y >= edge.top() + 2 * this->lineWidth) {
+ if (y <= edge.bottom() - 2 * this->lineWidth) {
+ return ResizableRect::Edge::Right;
+ }
+ else {
+ return ResizableRect::Edge::BottomRight;
+ }
+ }
+ else {
+ return ResizableRect::Edge::TopRight;
+ }
+ }
+ else {
+ if (y <= edge.top() + this->lineWidth) {
+ return ResizableRect::Edge::Top;
+ }
+ else if (y >= edge.bottom() - this->lineWidth) {
+ return ResizableRect::Edge::Bottom;
+ }
+ }
+ return ResizableRect::Edge::None;
+}
+
+void ResizableRect::updatePosFromRect(QRect newRect) {
+ prepareGeometryChange();
+ this->setRect(newRect);
+ emit this->rectUpdated(newRect);
+}
+
+void ResizableRect::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
+ switch (this->detectEdge(event->pos().x(), event->pos().y())) {
+ case ResizableRect::Edge::None:
+ default:
+ break;
+ case ResizableRect::Edge::Left:
+ case ResizableRect::Edge::Right:
+ this->setCursor(Qt::SizeHorCursor);
+ break;
+ case ResizableRect::Edge::Top:
+ case ResizableRect::Edge::Bottom:
+ this->setCursor(Qt::SizeVerCursor);
+ break;
+ case ResizableRect::Edge::TopRight:
+ case ResizableRect::Edge::BottomLeft:
+ this->setCursor(Qt::SizeBDiagCursor);
+ break;
+ case ResizableRect::Edge::TopLeft:
+ case ResizableRect::Edge::BottomRight:
+ this->setCursor(Qt::SizeFDiagCursor);
+ break;
+ }
+}
+
+void ResizableRect::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
+ this->unsetCursor();
+}
+
+void ResizableRect::mousePressEvent(QGraphicsSceneMouseEvent *event) {
+ int x = event->pos().x();
+ int y = event->pos().y();
+ this->clickedPos = event->scenePos();
+ this->clickedRect = this->rect().toAlignedRect();
+ this->clickedEdge = this->detectEdge(x, y);
+}
+
+void ResizableRect::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
+ int dx = roundUp(event->scenePos().x() - this->clickedPos.x(), 16);
+ int dy = roundUp(event->scenePos().y() - this->clickedPos.y(), 16);
+
+ QRect resizedRect = this->clickedRect;
+
+ switch (this->clickedEdge) {
+ case ResizableRect::Edge::None:
+ default:
+ return;
+ case ResizableRect::Edge::Left:
+ resizedRect.adjust(dx, 0, 0, 0);
+ break;
+ case ResizableRect::Edge::Right:
+ resizedRect.adjust(0, 0, dx, 0);
+ break;
+ case ResizableRect::Edge::Top:
+ resizedRect.adjust(0, dy, 0, 0);
+ break;
+ case ResizableRect::Edge::Bottom:
+ resizedRect.adjust(0, 0, 0, dy);
+ break;
+ case ResizableRect::Edge::TopRight:
+ resizedRect.adjust(0, dy, dx, 0);
+ break;
+ case ResizableRect::Edge::BottomLeft:
+ resizedRect.adjust(dx, 0, 0, dy);
+ break;
+ case ResizableRect::Edge::TopLeft:
+ resizedRect.adjust(dx, dy, 0, 0);
+ break;
+ case ResizableRect::Edge::BottomRight:
+ resizedRect.adjust(0, 0, dx, dy);
+ break;
+ }
+
+ // lower bounds limits
+ if (resizedRect.width() < 16)
+ resizedRect.setWidth(16);
+ if (resizedRect.height() < 16)
+ resizedRect.setHeight(16);
+
+ // TODO: upper bound limits
+
+ this->updatePosFromRect(resizedRect);
+}
diff --git a/src/ui/noscrollspinbox.cpp b/src/ui/noscrollspinbox.cpp
index f8d1d444..3493d4ee 100644
--- a/src/ui/noscrollspinbox.cpp
+++ b/src/ui/noscrollspinbox.cpp
@@ -1,5 +1,6 @@
#include "noscrollspinbox.h"
#include
+#include
unsigned actionId = 0xffff;
@@ -25,6 +26,10 @@ void NoScrollSpinBox::focusOutEvent(QFocusEvent *event) {
QSpinBox::focusOutEvent(event);
}
+void NoScrollSpinBox::setLineEditEnabled(bool enabled) {
+ this->lineEdit()->setReadOnly(!enabled);
+}
+
unsigned NoScrollSpinBox::getActionId() {
return actionId;
}
diff --git a/src/ui/resizelayoutpopup.cpp b/src/ui/resizelayoutpopup.cpp
new file mode 100644
index 00000000..c6c48db7
--- /dev/null
+++ b/src/ui/resizelayoutpopup.cpp
@@ -0,0 +1,186 @@
+#include "resizelayoutpopup.h"
+#include "editor.h"
+#include "movablerect.h"
+#include "config.h"
+
+#include "ui_resizelayoutpopup.h"
+
+// TODO: put this in a util file or something
+extern int roundUp(int, int);
+
+CheckeredBgScene::CheckeredBgScene(QObject *parent) : QGraphicsScene(parent) { }
+
+void CheckeredBgScene::drawBackground(QPainter *painter, const QRectF &rect) {
+ QRect r = rect.toRect();
+ int xMin = r.left() - r.left() % this->gridSize - this->gridSize;
+ int yMin = r.top() - r.top() % this->gridSize - this->gridSize;
+ int xMax = r.right() - r.right() % this->gridSize + this->gridSize;
+ int yMax = r.bottom() - r.bottom() % this->gridSize + this->gridSize;
+
+ // draw grid 16x16 from top to bottom of scene
+ QColor paintColor(0x00ff00);
+ for (int x = xMin, xTile = 0; x <= xMax; x += this->gridSize, xTile++) {
+ for (int y = yMin, yTile = 0; y <= yMax; y += this->gridSize, yTile++) {
+ if (!((xTile ^ yTile) & 1)) { // tile numbers have same parity (evenness)
+ if (this->validRect.contains(x, y)) // check if inside validRect
+ paintColor = QColor(132, 217, 165); // green light color
+ else
+ paintColor = 0xbcbcbc; // normal light color
+ }
+ else {
+ if (this->validRect.contains(x, y)) // check if inside validRect
+ paintColor = QColor(76, 178, 121); // green dark color
+ else
+ paintColor = 0x969696; // normal dark color
+ }
+ painter->fillRect(QRect(x, y, this->gridSize, this->gridSize), paintColor);
+ }
+ }
+}
+
+/******************************************************************************
+ ************************************************************************
+ ******************************************************************************/
+
+BoundedPixmapItem::BoundedPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent) : QGraphicsPixmapItem(pixmap, parent) {
+ setFlags(this->flags() | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsGeometryChanges | QGraphicsItem::ItemIsSelectable);
+}
+
+void BoundedPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem * item, QWidget *widget) {
+ // Draw the pixmap darkened in the background
+ painter->fillRect(this->boundingRect().toAlignedRect(), QColor(0x444444));
+ painter->setCompositionMode(QPainter::CompositionMode_Multiply);
+ painter->drawPixmap(this->boundingRect().toAlignedRect(), this->pixmap());
+
+ // draw the normal pixmap on top, cropping to validRect as needed
+ painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
+ QRect intersection = this->mapRectFromScene(this->boundary->rect()).toAlignedRect() & this->boundingRect().toAlignedRect();
+ QPixmap cropped = this->pixmap().copy(intersection);
+ painter->drawPixmap(intersection, cropped);
+}
+
+QVariant BoundedPixmapItem::itemChange(GraphicsItemChange change, const QVariant &value) {
+ if (change == ItemPositionChange && scene()) {
+ QPointF newPos = value.toPointF();
+ return QPointF(roundUp(newPos.x(), 16), roundUp(newPos.y(), 16));
+ }
+ else
+ return QGraphicsItem::itemChange(change, value);
+}
+
+/******************************************************************************
+ ************************************************************************
+ ******************************************************************************/
+
+ResizeLayoutPopup::ResizeLayoutPopup(QWidget *parent, Editor *editor) :
+ QDialog(parent),
+ parent(parent),
+ editor(editor),
+ ui(new Ui::ResizeLayoutPopup)
+{
+ ui->setupUi(this);
+ this->resetPosition();
+ this->setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint);
+ this->setWindowModality(Qt::ApplicationModal);
+
+ this->scene = new CheckeredBgScene(this);
+ //this->ui->graphicsView->setAlignment(Qt::AlignTop|Qt::AlignLeft);
+ this->ui->graphicsView->setScene(this->scene);
+ this->ui->graphicsView->setRenderHints(QPainter::Antialiasing);
+ this->ui->graphicsView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
+}
+
+ResizeLayoutPopup::~ResizeLayoutPopup()
+{
+ delete ui;
+}
+
+/// Reset position of the dialog to cover the MainWindow's layout metatile scene
+void ResizeLayoutPopup::resetPosition() {
+ this->setGeometry(QRect(parent->mapToGlobal(QPoint(0, 0)), parent->size()));
+}
+
+/// Custom scene contains
+/// (1) pixmap representing the current layout / not resizable / drag-movable
+/// (1) layout outline / resizable / not movable
+void ResizeLayoutPopup::setupLayoutView() {
+ if (!this->editor || !this->editor->layout) return;
+ // TODO: this should be a more robust check probably
+
+ // Border stuff
+ bool bordersEnabled = projectConfig.useCustomBorderSize;
+ if (bordersEnabled) {
+ this->ui->spinBox_borderWidth->setMinimum(1);
+ this->ui->spinBox_borderHeight->setMinimum(1);
+ this->ui->spinBox_borderWidth->setMaximum(MAX_BORDER_WIDTH);
+ this->ui->spinBox_borderHeight->setMaximum(MAX_BORDER_HEIGHT);
+ this->ui->spinBox_borderWidth->setLineEditEnabled(false);
+ this->ui->spinBox_borderHeight->setLineEditEnabled(false);
+ } else {
+ this->ui->frame_border->setVisible(false);
+ }
+ this->ui->spinBox_borderWidth->setValue(this->editor->layout->getBorderWidth());
+ this->ui->spinBox_borderHeight->setValue(this->editor->layout->getBorderHeight());
+
+ // Layout stuff
+ QPixmap pixmap = this->editor->layout->pixmap;
+ this->layoutPixmap = new BoundedPixmapItem(pixmap);
+ this->scene->addItem(layoutPixmap);
+ int maxWidth = this->editor->project->getMaxMapWidth();
+ int maxHeight = this->editor->project->getMaxMapHeight();
+ QGraphicsRectItem *cover = new QGraphicsRectItem(-maxWidth * 8, -maxHeight * 8, maxWidth * 16, maxHeight * 16);
+ this->scene->addItem(cover);
+
+ this->ui->spinBox_width->setMinimum(1);
+ this->ui->spinBox_width->setMaximum(maxWidth);
+ this->ui->spinBox_height->setMinimum(1);
+ this->ui->spinBox_height->setMaximum(maxHeight);
+
+ this->ui->spinBox_width->setLineEditEnabled(false);
+ this->ui->spinBox_height->setLineEditEnabled(false);
+
+ static bool layoutSizeRectVisible = true;
+
+ this->outline = new ResizableRect(this, &layoutSizeRectVisible, this->editor->layout->getWidth(), this->editor->layout->getHeight(), qRgb(255, 0, 255));
+ connect(outline, &ResizableRect::rectUpdated, [=](QRect rect){
+ this->scene->setValidRect(rect);
+ this->ui->spinBox_width->setValue(rect.width() / 16);
+ this->ui->spinBox_height->setValue(rect.height() / 16);
+ });
+ scene->addItem(outline);
+
+ layoutPixmap->setBoundary(outline);
+ this->outline->rectUpdated(outline->rect().toAlignedRect());
+
+ this->ui->graphicsView->scale(0.5, 0.5);
+ this->ui->graphicsView->centerOn(layoutPixmap);
+ // this->ui->graphicsView->fitInView(cover->rect(), Qt::KeepAspectRatio);
+}
+
+void ResizeLayoutPopup::on_spinBox_width_valueChanged(int value) {
+ if (!this->outline) return;
+ QRectF rect = this->outline->rect();
+ this->outline->updatePosFromRect(QRect(rect.x(), rect.y(), value * 16, rect.height()));
+}
+
+void ResizeLayoutPopup::on_spinBox_height_valueChanged(int value) {
+ if (!this->outline) return;
+ QRectF rect = this->outline->rect();
+ this->outline->updatePosFromRect(QRect(rect.x(), rect.y(), rect.width(), value * 16));
+}
+
+/// Result is the number of metatiles to add (or subtract) to each side of the map after dimension changes
+QMargins ResizeLayoutPopup::getResult() {
+ QMargins result = QMargins();
+
+ result.setLeft(this->layoutPixmap->x() - this->outline->rect().left());
+ result.setTop(this->layoutPixmap->y() - this->outline->rect().top());
+ result.setRight(this->outline->rect().right() - (this->layoutPixmap->x() + this->layoutPixmap->pixmap().width()));
+ result.setBottom(this->outline->rect().bottom() - (this->layoutPixmap->y() + this->layoutPixmap->pixmap().height()));
+
+ return result / 16;
+}
+
+QSize ResizeLayoutPopup::getBorderResult() {
+ return QSize(this->ui->spinBox_borderWidth->value(), this->ui->spinBox_borderHeight->value());
+}