redesign layout dimension change window

This commit is contained in:
garak 2024-11-12 01:09:43 -05:00
parent c9695521c7
commit c83474b6bc
13 changed files with 854 additions and 107 deletions

276
forms/resizelayoutpopup.ui Normal file
View file

@ -0,0 +1,276 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ResizeLayoutPopup</class>
<widget class="QDialog" name="ResizeLayoutPopup">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>598</width>
<height>378</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Resize Layout</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="margin">
<number>8</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set>
</property>
<property name="centerButtons">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QGraphicsView" name="graphicsView"/>
</item>
<item row="1" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Width</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="NoScrollSpinBox" name="spinBox_width">
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Height</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="NoScrollSpinBox" name="spinBox_height">
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QFrame" name="frame_border">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Border Width</string>
</property>
</widget>
</item>
<item>
<widget class="NoScrollSpinBox" name="spinBox_borderWidth">
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Border Height</string>
</property>
</widget>
</item>
<item>
<widget class="NoScrollSpinBox" name="spinBox_borderHeight">
<property name="minimumSize">
<size>
<width>64</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>NoScrollSpinBox</class>
<extends>QSpinBox</extends>
<header>noscrollspinbox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ResizeLayoutPopup</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ResizeLayoutPopup</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -8,6 +8,7 @@
#include <QUndoCommand>
#include <QList>
#include <QPointer>
#include <QMargins>
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;

View file

@ -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);

View file

@ -5,12 +5,13 @@
#include <QPainter>
#include <QRgb>
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

View file

@ -12,6 +12,8 @@ public:
void wheelEvent(QWheelEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
void setLineEditEnabled(bool enabled);
unsigned getActionId();
private:

View file

@ -0,0 +1,106 @@
#ifndef RESIZELAYOUTPOPUP_H
#define RESIZELAYOUTPOPUP_H
#include <QDialog>
#include <QPointer>
#include <QGraphicsScene>
#include <QGraphicsLineItem>
#include <QGraphicsRectItem>
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<CheckeredBgScene> scene = nullptr;
};
#endif // RESIZELAYOUTPOPUP_H

View file

@ -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 \

View file

@ -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());

View file

@ -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);

View file

@ -23,6 +23,7 @@
#include "newmapconnectiondialog.h"
#include "config.h"
#include "filedialog.h"
#include "resizelayoutpopup.h"
#include <QClipboard>
#include <QDirIterator>
@ -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)

View file

@ -1,17 +1,163 @@
#include <QCursor>
#include <QGraphicsSceneHoverEvent>
#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);
}

View file

@ -1,5 +1,6 @@
#include "noscrollspinbox.h"
#include <QWheelEvent>
#include <QLineEdit>
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;
}

View file

@ -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());
}