Merge branch 'dev' into heal-locations

This commit is contained in:
GriffinR
2025-02-11 14:26:15 -05:00
committed by GitHub
52 changed files with 1294 additions and 412 deletions

View File

@@ -8,20 +8,21 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
## [Unreleased]
### Added
- Add the ability to edit layouts with no corresponding map.
- Add ``Duplicate Map`` / ``Duplicate Layout`` options, accessible by right-clicking a map or layout in the map list.
- Redesigned the Connections tab, adding a number of new features including the option to open or display diving maps and a list UI for easier edit access.
- Redesigned the map list, adding new features including opening/editing layouts with no associated map, duplicating maps or layouts (accessible via right-click), editing the names of map groups, rearranging maps and map groups, and hiding empty folders.
- Add a drop-down for changing the layout of the currently opened map.
- Redesigned the Connections tab, adding new features including the option to open or display diving maps and a list UI for easier edit access.
- Add a `Close Project` option
- Add charts to the `Wild Pokémon` tab that show species and level distributions.
- Add a search button to the `Wild Pokémon` tab that shows the encounter data for a species across all maps.
- Add charts to the `Wild Pokémon` tab that show species and level distributions for the current map.
- Add options for customizing the map grid under `View -> Grid Settings`.
- Add an option to display a dividing line between tilesets in the Tileset Editor.
- An alert will be displayed when attempting to open a seemingly invalid project.
- Add support for defining project values with `enum` where `#define` was expected.
- Add button to enable editing map groups including renaming groups and rearranging the maps within them.
- Add buttons to hide and show empty folders in each map tree view.
- Add a setting to specify the tile values to use for the unused metatile layer.
### Changed
- `Change Dimensions` now has an interactive resizing rectangle.
- Redesigned the new map dialog, including better error checking and a collapsible section for header data.
- Map groups and ``MAPSEC`` names specified when creating a new map will be added automatically if they don't already exist.
- Edits to map connections now have Undo/Redo and can be viewed in exported timelapses.
@@ -32,10 +33,12 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- It's now possible to cancel quitting if there are unsaved changes in sub-windows.
- The triple-layer metatiles setting can now be set automatically using a project constant.
- `Export Map Stitch Image` now shows a preview of the full image, not just the current map.
- Maps and layouts were internally separated.
- `Custom Attributes` tables now display numbers using spin boxes. The `type` column was removed, because `value`'s type is now obvious.
- Unrecognized map names in Event or Connections data will no longer be overwritten.
- Reduced diff noise when saving maps.
- Map names and ``MAP_NAME`` constants are no longer required to match.
- Porymap will no longer overwrite ``include/constants/map_groups.h`` or ``include/constants/layouts.h``.
- Primary/secondary metatile images are now kept on separate rows, rather than blending together if the primary size is not divisible by 8.
### Fixed
- Fix `Add Region Map...` not updating the region map settings file.
@@ -49,7 +52,8 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Fix the `Edit History` window not raising to the front when reactivated.
- New maps are now always inserted in map dropdowns at the correct position, rather than at the bottom of the list until the project is reloaded.
- Fix invalid species names clearing from wild pokémon data when revisited.
- Fix editing wild pokémon data not marking the map as edited.
- Fix editing wild pokémon data not marking the map as unsaved.
- Fix editing an event's `Custom Attributes` not marking the map as unsaved.
- Fix changes to map connections not marking connected maps as unsaved.
- Fix numerous issues related to connecting a map to itself.
- Fix incorrect map connections getting selected when opening a map by double-clicking a map connection.
@@ -72,8 +76,11 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Fix exported tile images containing garbage pixels after the end of the tiles.
- Fix fully transparent pixels rendering with the incorrect color.
- Fix the values for some config fields shuffling their order every save.
- Fix `key`s in `Custom Attributes` disappearing if given an empty name or the name of an existing field.
- Fix some problems with tileset detection when importing maps from AdvanceMap.
- Fix certain input fields allowing invalid identifiers, like names starting with numbers.
- Fix crash in the Shortcuts Editor when applying changes after closing certain windows.
- Fix `Display Metatile Usage Counts` sometimes changing the counts after repeated use.
## [5.4.1] - 2024-03-21
### Fixed

View File

@@ -153,9 +153,9 @@ Callbacks
.. js:function:: onMapOpened(mapName)
Called when a map is opened.
Called when a map or layout is opened.
:param mapName: the name of the opened map
:param mapName: the name of the opened map or layout
:type mapName: string
.. js:function:: onBlockChanged(x, y, prevBlock, newBlock)
@@ -1995,7 +1995,28 @@ All utility functions are callable via the global ``utility`` object.
Gets the list of map names.
:returns: the list of map names
:returns: the list of map names (e.g. `PetalburgCity`)
:rtype: array
.. js:function:: utility.getMapConstants()
Gets the list of map IDs (e.g. `MAP_PETALBURG_CITY`)
:returns: the list of map IDs
:rtype: array
.. js:function:: utility.getLayoutNames()
Gets the list of layout names.
:returns: the list of layout names (e.g. `PetalburgCity_Layout`)
:rtype: array
.. js:function:: utility.getLayoutConstants()
Gets the list of layout IDs (e.g. `LAYOUT_PETALBURG_CITY`)
:returns: the list of layout IDs
:rtype: array
.. js:function:: utility.getTilesetNames()

View File

@@ -1484,7 +1484,7 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="mapView_ObjectInfo">
<widget class="QWidget" name="mapView_EventInfo">
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>1</number>
@@ -1499,7 +1499,7 @@
<number>1</number>
</property>
<item row="0" column="0">
<widget class="QFrame" name="frame_Objects">
<widget class="QFrame" name="frame_Events">
<property name="enabled">
<bool>true</bool>
</property>
@@ -1582,7 +1582,7 @@
</widget>
</item>
<item>
<widget class="QToolButton" name="toolButton_deleteObject">
<widget class="QToolButton" name="toolButton_deleteEvent">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>

View File

@@ -29,7 +29,7 @@
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="label_MapSelection">
<property name="text">
<string>Map</string>
</property>

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,7 @@ 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, QMargins newLayoutMargins,
const Blockdata &oldMetatiles, const Blockdata &newMetatiles,
QSize oldBorderDimensions, QSize newBorderDimensions,
const Blockdata &oldBorder, const Blockdata &newBorder,
@@ -220,8 +221,7 @@ private:
int oldLayoutWidth;
int oldLayoutHeight;
int newLayoutWidth;
int newLayoutHeight;
QMargins newLayoutMargins;
int oldBorderWidth;
int oldBorderHeight;

View File

@@ -103,6 +103,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);
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

@@ -80,7 +80,7 @@ public:
Metatile* metatileAt(unsigned int i) const { return m_metatiles.at(i); }
void clearMetatiles();
void resizeMetatiles(unsigned int newNumMetatiles);
void resizeMetatiles(int newNumMetatiles);
int numMetatiles() const { return m_metatiles.length(); }
private:

View File

@@ -108,14 +108,15 @@ public:
void toggleBorderVisibility(bool visible, bool enableScriptCallback = true);
void updateCustomMapAttributes();
DraggablePixmapItem *addMapEvent(Event *event);
DraggablePixmapItem *addEventPixmapItem(Event *event);
void removeEventPixmapItem(Event *event);
bool eventLimitReached(Map *, Event::Type);
void selectMapEvent(DraggablePixmapItem *object, bool toggle = false);
void selectMapEvent(DraggablePixmapItem *item, bool toggle = false);
DraggablePixmapItem *addNewEvent(Event::Type type);
void updateSelectedEvents();
void duplicateSelectedEvents();
void redrawObject(DraggablePixmapItem *item);
QList<DraggablePixmapItem *> getObjects();
void redrawEventPixmapItem(DraggablePixmapItem *item);
QList<DraggablePixmapItem *> getEventPixmapItems();
void updateCursorRectPos(int x, int y);
void setCursorRectVisible(bool visible);
@@ -155,7 +156,7 @@ public:
enum class EditAction { None, Paint, Select, Fill, Shift, Pick, Move };
EditAction mapEditAction = EditAction::Paint;
EditAction objectEditAction = EditAction::Select;
EditAction eventEditAction = EditAction::Select;
enum class EditMode { None, Disabled, Metatiles, Collision, Header, Events, Connections, Encounters };
EditMode editMode = EditMode::None;
@@ -169,7 +170,7 @@ public:
void setEditingMetatiles();
void setEditingCollision();
void setEditingHeader();
void setEditingObjects();
void setEditingEvents();
void setEditingConnections();
void setEditingEncounters();
@@ -179,7 +180,9 @@ public:
qreal collisionOpacity = 0.5;
static QList<QList<const QImage*>> collisionIcons;
void objectsView_onMousePress(QMouseEvent *event);
int eventShiftActionId = 0;
void eventsView_onMousePress(QMouseEvent *event);
int getBorderDrawDistance(int dimension);
@@ -253,7 +256,7 @@ private slots:
void onWheelZoom(int);
signals:
void objectsChanged();
void eventsChanged();
void openConnectedMap(MapConnection*);
void wildMonTableOpened(EncounterTableModel*);
void wildMonTableClosed();

View File

@@ -96,6 +96,7 @@ namespace fex
case Type::kValuePair:
return pair_.first + " = " + pair_.second->ToString() + "\n";
}
return "";
}
static ArrayValue Empty()

View File

@@ -221,8 +221,8 @@ private slots:
void addNewEvent(Event::Type type);
void tryAddEventTab(QWidget * tab);
void displayEventTabs();
void updateSelectedObjects();
void updateObjects();
void updateSelectedEvents();
void updateEvents();
void on_toolButton_Paint_clicked();
void on_toolButton_Select_clicked();
@@ -325,6 +325,7 @@ private:
QAction *undoAction = nullptr;
QAction *redoAction = nullptr;
QPointer<QUndoView> undoView = nullptr;
QAction *copyAction = nullptr;
QAction *pasteAction = nullptr;
@@ -353,6 +354,7 @@ private:
bool setProjectUI();
void clearProjectUI();
void openEditHistory();
void openNewMapDialog();
void openDuplicateMapDialog(const QString &mapName);
NewLayoutDialog* createNewLayoutDialog(const Layout *layoutToCopy = nullptr);
@@ -380,7 +382,7 @@ private:
void updateMapList();
void openMapListItem(const QModelIndex &index);
void saveMapListTab(int index);
void onMapListTabChanged(int index);
void displayMapProperties();
void checkToolButtons();

View File

@@ -42,6 +42,9 @@ public:
Q_INVOKABLE QList<float> getMetatileLayerOpacity();
Q_INVOKABLE void setMetatileLayerOpacity(QList<float> order);
Q_INVOKABLE QList<QString> getMapNames();
Q_INVOKABLE QList<QString> getMapConstants();
Q_INVOKABLE QList<QString> getLayoutNames();
Q_INVOKABLE QList<QString> getLayoutConstants();
Q_INVOKABLE QList<QString> getTilesetNames();
Q_INVOKABLE QList<QString> getPrimaryTilesetNames();
Q_INVOKABLE QList<QString> getSecondaryTilesetNames();

View File

@@ -30,7 +30,7 @@ public:
WildMonInfo encounterData() const { return m_monInfo; }
EncounterField encounterField() const { return m_encounterField; }
QList<double> percentages() const { return m_slotPercentages; }
QVector<double> percentages() const { return m_slotPercentages; }
private:
int m_numRows = 0;
@@ -38,7 +38,7 @@ private:
WildMonInfo m_monInfo;
EncounterField m_encounterField;
QMap<int,QString> m_groupNames;
QList<double> m_slotPercentages;
QVector<double> m_slotPercentages;
signals:
void edited();

View File

@@ -3,15 +3,28 @@
#include <QSortFilterProxyModel>
class FilterChildrenProxyModel : public QSortFilterProxyModel
class NumericSortProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit FilterChildrenProxyModel(QObject *parent = nullptr);
void setHideEmpty(bool hidden) { this->hideEmpty = hidden; }
explicit NumericSortProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) {};
protected:
bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const;
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
};
class FilterChildrenProxyModel : public NumericSortProxyModel
{
Q_OBJECT
public:
explicit FilterChildrenProxyModel(QObject *parent = nullptr) : NumericSortProxyModel(parent) {};
void setHideEmpty(bool hidden) { this->hideEmpty = hidden; }
protected:
virtual bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override;
private:
bool hideEmpty = false;
};

View File

@@ -9,9 +9,9 @@
#include <QWidget>
#include <QPointer>
#include "mapheader.h"
class Project;
#include "mapheader.h"
#include "project.h"
namespace Ui {
class MapHeaderForm;

View File

@@ -79,7 +79,6 @@ protected:
QString activeItemName;
QString folderTypeName;
bool sortingEnabled = false;
bool editable = false;
QIcon mapGrayIcon;

View File

@@ -37,6 +37,8 @@ public:
void setFilterLocked(bool locked) { m_filterLocked = locked; }
bool isFilterLocked() const { return m_filterLocked; }
void setSearchFocus();
signals:
void filterCleared(MapTree*);
void addFolderClicked();

View File

@@ -79,6 +79,7 @@ private:
bool positionIsValid(const QPoint &pos) const;
bool selectionIsValid();
void hoverChanged();
int numPrimaryMetatilesRounded() const;
signals:
void hoveredMetatileSelectionChanged(uint16_t);

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,65 @@ 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);
void setLimit(QRect limit) { this->limit = limit; }
protected:
void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override;
void hoverLeaveEvent(QGraphicsSceneHoverEvent *) 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;
QRect limit = QRect();
int lineWidth = 8;
signals:
void rectUpdated(QRect rect);
};
#endif // MOVABLERECT_H

View File

@@ -5,7 +5,7 @@
#include <QAbstractButton>
#include <QPointer>
class Project;
#include "project.h"
namespace Ui {
class NewLocationDialog;

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,109 @@
#ifndef RESIZELAYOUTPOPUP_H
#define RESIZELAYOUTPOPUP_H
#include <QDialog>
#include <QPointer>
#include <QGraphicsScene>
#include <QGraphicsLineItem>
#include <QGraphicsRectItem>
#include <QDialogButtonBox>
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;
}
QRect getValidRect() { return this->validRect; }
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);
void on_buttonBox_clicked(QAbstractButton *button);
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

@@ -35,9 +35,9 @@ signals:
private:
Ui::ShortcutsEditor *ui;
QWidget *main_container;
QMultiMap<QString, const QObject *> labels_objects;
QMultiMap<QString, QPointer<const QObject>> labels_objects;
QHash<QString, QFormLayout *> contexts_layouts;
QHash<MultiKeyEdit *, const QObject *> multiKeyEdits_objects;
QHash<MultiKeyEdit *, QPointer<const QObject>> multiKeyEdits_objects;
void parseObjectList(const QObjectList &objectList);
QString getLabel(const QObject *object) const;

View File

@@ -51,6 +51,7 @@ private:
void drawCounts();
QImage buildAllMetatilesImage();
QImage buildImage(int metatileIdStart, int numMetatiles);
int numPrimaryMetatilesRounded() const;
signals:
void hoveredMetatileChanged(uint16_t);

View File

@@ -2,9 +2,24 @@
#define WILDMONSEARCH_H
#include <QDialog>
#include <QTableWidgetItem>
#include <QCollator>
class Project;
class NumericSortTableItem : public QTableWidgetItem
{
public:
explicit NumericSortTableItem(const QString &text) : QTableWidgetItem(text) {};
protected:
virtual bool operator<(const QTableWidgetItem &other) const override {
QCollator collator;
collator.setNumericMode(true);
return collator.compare(text(), other.text()) < 0;
}
};
namespace Ui {
class WildMonSearch;
}

View File

@@ -35,6 +35,7 @@ DEFINES += PORYMAP_VERSION=\\\"$$VERSION\\\"
SOURCES += src/core/advancemapparser.cpp \
src/core/block.cpp \
src/ui/resizelayoutpopup.cpp \
src/core/bitpacker.cpp \
src/core/blockdata.cpp \
src/core/events.cpp \
@@ -255,7 +256,8 @@ HEADERS += include/core/advancemapparser.h \
include/ui/uintspinbox.h \
include/ui/updatepromoter.h \
include/ui/wildmonchart.h \
include/ui/wildmonsearch.h
include/ui/wildmonsearch.h \
include/ui/resizelayoutpopup.h
FORMS += forms/mainwindow.ui \
forms/colorinputwidget.ui \
@@ -288,7 +290,8 @@ FORMS += forms/mainwindow.ui \
forms/customattributesdialog.ui \
forms/updatepromoter.ui \
forms/wildmonchart.ui \
forms/wildmonsearch.ui
forms/wildmonsearch.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);
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);
layout->blockdata = oldMetatiles;
layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight());
layout->lastCommitBlocks.borderDimensions = QSize(layout->getBorderWidth(), layout->getBorderHeight());
@@ -324,9 +327,7 @@ void EventCreate::redo() {
QUndoCommand::redo();
map->addEvent(event);
editor->project->setEventPixmap(event);
editor->addMapEvent(event);
editor->addEventPixmapItem(event);
// select this event
editor->selected_events->clear();
@@ -335,12 +336,7 @@ void EventCreate::redo() {
void EventCreate::undo() {
map->removeEvent(event);
if (editor->scene->items().contains(event->getPixmapItem())) {
editor->scene->removeItem(event->getPixmapItem());
}
editor->selected_events->removeOne(event->getPixmapItem());
editor->removeEventPixmapItem(event);
editor->shouldReselectEvents();
QUndoCommand::undo();
@@ -375,11 +371,7 @@ void EventDelete::redo() {
for (Event *event : selectedEvents) {
map->removeEvent(event);
if (editor->scene->items().contains(event->getPixmapItem())) {
editor->scene->removeItem(event->getPixmapItem());
}
editor->selected_events->removeOne(event->getPixmapItem());
editor->removeEventPixmapItem(event);
}
editor->selected_events->clear();
@@ -391,8 +383,7 @@ void EventDelete::redo() {
void EventDelete::undo() {
for (Event *event : selectedEvents) {
map->addEvent(event);
editor->project->setEventPixmap(event);
editor->addMapEvent(event);
editor->addEventPixmapItem(event);
}
// select these events
@@ -433,8 +424,7 @@ void EventDuplicate::redo() {
for (Event *event : selectedEvents) {
map->addEvent(event);
editor->project->setEventPixmap(event);
editor->addMapEvent(event);
editor->addEventPixmapItem(event);
}
// select these events
@@ -448,11 +438,7 @@ void EventDuplicate::redo() {
void EventDuplicate::undo() {
for (Event *event : selectedEvents) {
map->removeEvent(event);
if (editor->scene->items().contains(event->getPixmapItem())) {
editor->scene->removeItem(event->getPixmapItem());
}
editor->selected_events->removeOne(event->getPixmapItem());
editor->removeEventPixmapItem(event);
}
editor->shouldReselectEvents();

View File

@@ -176,6 +176,34 @@ void Layout::setDimensions(int newWidth, int newHeight, bool setNewBlockdata, bo
emit layoutDimensionsChanged(QSize(getWidth(), getHeight()));
}
void Layout::adjustDimensions(QMargins margins, bool setNewBlockdata) {
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
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

@@ -83,7 +83,8 @@ void Tileset::addMetatile(Metatile* metatile) {
m_metatiles.append(metatile);
}
void Tileset::resizeMetatiles(unsigned int newNumMetatiles) {
void Tileset::resizeMetatiles(int newNumMetatiles) {
if (newNumMetatiles < 0) newNumMetatiles = 0;
while (m_metatiles.length() > newNumMetatiles) {
delete m_metatiles.takeLast();
}

View File

@@ -135,36 +135,26 @@ void Editor::setEditorView() {
return;
}
map_item->setEditsEnabled(this->editMode != EditMode::Connections);
map_item->draw();
collision_item->draw();
current_view->setVisible(true);
updateBorderVisibility();
this->cursorMapTileRect->setSingleTileMode();
this->cursorMapTileRect->setActive(true);
switch (this->editMode) {
case EditMode::Metatiles:
case EditMode::Collision:
map_item->setEditsEnabled(true);
this->editGroup.setActiveStack(&this->layout->editHistory);
break;
case EditMode::Connections:
this->cursorMapTileRect->setActive(false);
map_item->setEditsEnabled(false);
case EditMode::Events:
if (this->map) {
this->editGroup.setActiveStack(this->map->editHistory());
}
break;
case EditMode::Header:
case EditMode::Encounters:
default:
this->editGroup.setActiveStack(nullptr);
break;
QUndoStack *editStack = this->map ? this->map->editHistory() : nullptr;
bool usesCursor = false;
if (this->editMode == EditMode::Metatiles || this->editMode == EditMode::Collision) {
if (this->layout) editStack = &this->layout->editHistory;
usesCursor = true;
}
this->cursorMapTileRect->setSingleTileMode();
this->cursorMapTileRect->setActive(usesCursor);
this->editGroup.setActiveStack(editStack);
if (this->events_group) {
this->events_group->setVisible(this->editMode == EditMode::Events);
}
@@ -189,7 +179,7 @@ void Editor::setEditingHeader() {
setEditorView();
}
void Editor::setEditingObjects() {
void Editor::setEditingEvents() {
this->editMode = EditMode::Events;
setEditorView();
@@ -1306,7 +1296,7 @@ void Editor::setStraightPathCursorMode(QGraphicsSceneMouseEvent *event) {
}
void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *item) {
// TODO: add event tab object painting tool buttons stuff here
// TODO: add event tab event painting tool buttons stuff here
if (!item->getEditsEnabled()) {
return;
}
@@ -1361,10 +1351,10 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *i
item->shift(event);
}
} else if (this->editMode == EditMode::Events) {
if (objectEditAction == EditAction::Paint && event->type() == QEvent::GraphicsSceneMousePress) {
if (eventEditAction == EditAction::Paint && event->type() == QEvent::GraphicsSceneMousePress) {
// Right-clicking while in paint mode will change mode to select.
if (event->buttons() & Qt::RightButton) {
this->objectEditAction = EditAction::Select;
this->eventEditAction = EditAction::Select;
this->settings->mapCursor = QCursor();
this->cursorMapTileRect->setSingleTileMode();
this->ui->toolButton_Paint->setChecked(false);
@@ -1380,18 +1370,17 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *i
DraggablePixmapItem *newEvent = addNewEvent(eventType);
if (newEvent) {
newEvent->move(pos.x(), pos.y());
emit objectsChanged();
emit eventsChanged();
selectMapEvent(newEvent);
}
}
} else if (objectEditAction == EditAction::Select) {
} else if (eventEditAction == EditAction::Select) {
// do nothing here, at least for now
} else if (objectEditAction == EditAction::Shift) {
} else if (eventEditAction == EditAction::Shift) {
static QPoint selection_origin;
static unsigned actionId = 0;
if (event->type() == QEvent::GraphicsSceneMouseRelease) {
actionId++;
this->eventShiftActionId++;
} else {
if (event->type() == QEvent::GraphicsSceneMousePress) {
selection_origin = QPoint(pos.x(), pos.y());
@@ -1402,12 +1391,12 @@ void Editor::mouseEvent_map(QGraphicsSceneMouseEvent *event, LayoutPixmapItem *i
QList<Event *> selectedEvents;
for (DraggablePixmapItem *pixmapItem : getObjects()) {
for (DraggablePixmapItem *pixmapItem : getEventPixmapItems()) {
selectedEvents.append(pixmapItem->event);
}
selection_origin = QPoint(pos.x(), pos.y());
map->commit(new EventShift(selectedEvents, xDelta, yDelta, actionId));
map->commit(new EventShift(selectedEvents, xDelta, yDelta, this->eventShiftActionId));
}
}
}
@@ -1702,15 +1691,13 @@ void Editor::displayMovementPermissionSelector() {
void Editor::clearMapEvents() {
if (events_group) {
if (events_group->scene()) {
events_group->scene()->removeItem(events_group);
}
for (QGraphicsItem *child : events_group->childItems()) {
events_group->removeFromGroup(child);
delete child;
}
if (events_group->scene()) {
events_group->scene()->removeItem(events_group);
}
delete events_group;
events_group = nullptr;
}
@@ -1724,18 +1711,29 @@ void Editor::displayMapEvents() {
scene->addItem(events_group);
for (const auto &event : map->getEvents()) {
project->setEventPixmap(event);
addMapEvent(event);
addEventPixmapItem(event);
}
//objects_group->setFiltersChildEvents(false);
events_group->setHandlesChildEvents(false);
}
DraggablePixmapItem *Editor::addMapEvent(Event *event) {
DraggablePixmapItem *object = new DraggablePixmapItem(event, this);
this->redrawObject(object);
events_group->addToGroup(object);
return object;
DraggablePixmapItem *Editor::addEventPixmapItem(Event *event) {
this->project->setEventPixmap(event);
auto item = new DraggablePixmapItem(event, this);
redrawEventPixmapItem(item);
this->events_group->addToGroup(item);
return item;
}
void Editor::removeEventPixmapItem(Event *event) {
auto item = event->getPixmapItem();
if (!item) return;
this->events_group->removeFromGroup(item);
this->selected_events->removeOne(item);
event->setPixmapItem(nullptr);
delete item;
}
void Editor::clearMapConnections() {
@@ -1987,7 +1985,7 @@ Tileset* Editor::getCurrentMapPrimaryTileset()
return project->getTileset(tilesetLabel);
}
QList<DraggablePixmapItem *> Editor::getObjects() {
QList<DraggablePixmapItem *> Editor::getEventPixmapItems() {
QList<DraggablePixmapItem *> list;
for (QGraphicsItem *child : events_group->childItems()) {
list.append(static_cast<DraggablePixmapItem *>(child));
@@ -1995,7 +1993,7 @@ QList<DraggablePixmapItem *> Editor::getObjects() {
return list;
}
void Editor::redrawObject(DraggablePixmapItem *item) {
void Editor::redrawEventPixmapItem(DraggablePixmapItem *item) {
if (item && item->event && !item->event->getPixmap().isNull()) {
qreal opacity = item->event->getUsingSprite() ? 1.0 : 0.7;
item->setOpacity(opacity);
@@ -2050,27 +2048,27 @@ void Editor::shouldReselectEvents() {
}
void Editor::updateSelectedEvents() {
for (DraggablePixmapItem *item : getObjects()) {
redrawObject(item);
for (DraggablePixmapItem *item : getEventPixmapItems()) {
redrawEventPixmapItem(item);
}
emit objectsChanged();
emit eventsChanged();
}
void Editor::selectMapEvent(DraggablePixmapItem *object, bool toggle) {
if (!selected_events || !object)
void Editor::selectMapEvent(DraggablePixmapItem *item, bool toggle) {
if (!selected_events || !item)
return;
if (!toggle) {
// Selecting just this event
selected_events->clear();
selected_events->append(object);
} else if (!selected_events->contains(object)) {
selected_events->append(item);
} else if (!selected_events->contains(item)) {
// Adding event to group selection
selected_events->append(object);
selected_events->append(item);
} else if (selected_events->length() > 1) {
// Removing event from group selection
selected_events->removeOne(object);
selected_events->removeOne(item);
} else {
// Attempting to toggle the only currently-selected event.
// Unselecting an event this way would be unexpected, so we ignore it.
@@ -2083,17 +2081,9 @@ void Editor::selectedEventIndexChanged(int index, Event::Group eventGroup) {
int event_offs = Event::getIndexOffset(eventGroup);
index = index - event_offs;
Event *event = this->map->getEvent(eventGroup, index);
DraggablePixmapItem *selectedEvent = nullptr;
for (QGraphicsItem *child : this->events_group->childItems()) {
DraggablePixmapItem *item = static_cast<DraggablePixmapItem *>(child);
if (item->event == event) {
selectedEvent = item;
break;
}
}
if (selectedEvent) {
this->selectMapEvent(selectedEvent);
if (event && event->getPixmapItem()) {
this->selectMapEvent(event->getPixmapItem());
} else {
updateSelectedEvents();
}
@@ -2295,13 +2285,13 @@ bool Editor::startDetachedProcess(const QString &command, const QString &working
// Since the DraggablePixmapItem's event fires first, we can set a temp
// variable "selectingEvent" so that we can detect whether or not the user
// is clicking on the background instead of an event.
void Editor::objectsView_onMousePress(QMouseEvent *event) {
// make sure we are in object editing mode
void Editor::eventsView_onMousePress(QMouseEvent *event) {
// make sure we are in event editing mode
if (map_item && this->editMode != EditMode::Events) {
return;
}
if (this->objectEditAction == EditAction::Paint && event->buttons() & Qt::RightButton) {
this->objectEditAction = EditAction::Select;
if (this->eventEditAction == EditAction::Paint && event->buttons() & Qt::RightButton) {
this->eventEditAction = EditAction::Select;
this->settings->mapCursor = QCursor();
this->cursorMapTileRect->setSingleTileMode();
this->ui->toolButton_Paint->setChecked(false);

View File

@@ -116,6 +116,10 @@ static void dump(const QString &value, QString &out, int *indent, bool isKey = f
static void dump(const Json::array &values, QString &out, int *indent) {
bool first = true;
if (!out.endsWith(": ")) out += QString(*indent * 2, ' ');
if (values.empty()) {
out += "[]";
return;
}
out += "[\n";
*indent += 1;
for (const auto &value : values) {

View File

@@ -23,6 +23,7 @@
#include "newmapconnectiondialog.h"
#include "config.h"
#include "filedialog.h"
#include "resizelayoutpopup.h"
#include "newmapdialog.h"
#include "newtilesetdialog.h"
#include "newmapgroupdialog.h"
@@ -92,6 +93,7 @@ MainWindow::~MainWindow()
saveGlobalConfigs();
delete label_MapRulerStatus;
delete undoView;
delete editor;
delete ui;
}
@@ -332,14 +334,14 @@ void MainWindow::checkForUpdates(bool) {}
void MainWindow::initEditor() {
this->editor = new Editor(ui);
connect(this->editor, &Editor::objectsChanged, this, &MainWindow::updateObjects);
connect(this->editor, &Editor::eventsChanged, this, &MainWindow::updateEvents);
connect(this->editor, &Editor::openConnectedMap, this, &MainWindow::onOpenConnectedMap);
connect(this->editor, &Editor::warpEventDoubleClicked, this, &MainWindow::openWarpMap);
connect(this->editor, &Editor::currentMetatilesSelectionChanged, this, &MainWindow::currentMetatilesSelectionChanged);
connect(this->editor, &Editor::wildMonTableEdited, [this] { this->markMapEdited(); });
connect(this->editor, &Editor::mapRulerStatusChanged, this, &MainWindow::onMapRulerStatusChanged);
connect(this->editor, &Editor::tilesetUpdated, this, &Scripting::cb_TilesetUpdated);
connect(ui->toolButton_deleteObject, &QAbstractButton::clicked, this->editor, &Editor::deleteSelectedEvents);
connect(ui->toolButton_deleteEvent, &QAbstractButton::clicked, this->editor, &Editor::deleteSelectedEvents);
this->loadUserSettings();
@@ -354,15 +356,15 @@ void MainWindow::initEditor() {
ui->menuEdit->addAction(undoAction);
ui->menuEdit->addAction(redoAction);
QUndoView *undoView = new QUndoView(&editor->editGroup);
undoView->setWindowTitle(tr("Edit History"));
undoView->setAttribute(Qt::WA_QuitOnClose, false);
this->undoView = new QUndoView(&editor->editGroup);
this->undoView->setWindowTitle(tr("Edit History"));
this->undoView->setAttribute(Qt::WA_QuitOnClose, false);
// Show the EditHistory dialog with Ctrl+E
QAction *showHistory = new QAction("Show Edit History...", this);
showHistory->setObjectName("action_ShowEditHistory");
showHistory->setShortcut(QKeySequence("Ctrl+E"));
connect(showHistory, &QAction::triggered, [this, undoView](){ openSubWindow(undoView); });
connect(showHistory, &QAction::triggered, this, &MainWindow::openEditHistory);
ui->menuEdit->addAction(showHistory);
@@ -387,6 +389,10 @@ void MainWindow::initEditor() {
});
}
void MainWindow::openEditHistory() {
openSubWindow(this->undoView);
}
void MainWindow::initMiscHeapObjects() {
ui->tabWidget_EventType->clear();
}
@@ -450,7 +456,7 @@ void MainWindow::initMapList() {
connect(ui->mapListToolBar_Locations, &MapListToolBar::addFolderClicked, this, &MainWindow::openNewLocationDialog);
connect(ui->mapListToolBar_Layouts, &MapListToolBar::addFolderClicked, this, &MainWindow::openNewLayoutDialog);
connect(ui->mapListContainer, &QTabWidget::currentChanged, this, &MainWindow::saveMapListTab);
connect(ui->mapListContainer, &QTabWidget::currentChanged, this, &MainWindow::onMapListTabChanged);
}
void MainWindow::updateWindowTitle() {
@@ -830,12 +836,7 @@ void MainWindow::on_action_Open_Project_triggered()
}
void MainWindow::on_action_Reload_Project_triggered() {
// TODO: when undo history is complete show only if has unsaved changes
WarningMessage msgBox(QStringLiteral("Reloading this project will discard any unsaved changes."), this);
msgBox.addButton(QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
if (msgBox.exec() == QMessageBox::Ok)
openProject(editor->project->root);
openProject(editor->project->root);
}
void MainWindow::on_action_Close_Project_triggered() {
@@ -953,6 +954,7 @@ bool MainWindow::setLayout(QString layoutId) {
connect(editor->layout, &Layout::needsRedrawing, this, &MainWindow::redrawMapScene, Qt::UniqueConnection);
Scripting::cb_MapOpened(layout->name);
updateTilesetEditor();
userConfig.recentMapOrLayout = layoutId;
@@ -961,8 +963,8 @@ bool MainWindow::setLayout(QString layoutId) {
}
void MainWindow::redrawMapScene() {
editor->displayMap();
editor->displayLayout();
editor->displayMap();
refreshMapScene();
}
@@ -1008,13 +1010,12 @@ void MainWindow::openWarpMap(QString map_name, int event_id, Event::Group event_
int index = event_id - Event::getIndexOffset(event_group);
Event* event = editor->map->getEvent(event_group, index);
if (event) {
for (DraggablePixmapItem *item : editor->getObjects()) {
if (item->event == event) {
editor->selected_events->clear();
editor->selected_events->append(item);
editor->updateSelectedEvents();
return;
}
auto item = event->getPixmapItem();
if (item) {
editor->selected_events->clear();
editor->selected_events->append(item);
editor->updateSelectedEvents();
return;
}
}
// Can still warp to this map, but can't select the specified event
@@ -1106,11 +1107,15 @@ bool MainWindow::setProjectUI() {
this->locationListProxyModel = new FilterChildrenProxyModel();
locationListProxyModel->setSourceModel(this->mapLocationModel);
ui->locationList->setModel(locationListProxyModel);
ui->locationList->setSortingEnabled(true);
ui->locationList->sortByColumn(0, Qt::SortOrder::AscendingOrder);
this->layoutTreeModel = new LayoutTreeModel(editor->project);
this->layoutListProxyModel = new FilterChildrenProxyModel();
this->layoutListProxyModel->setSourceModel(this->layoutTreeModel);
ui->layoutList->setModel(layoutListProxyModel);
ui->layoutList->setSortingEnabled(true);
ui->layoutList->sortByColumn(0, Qt::SortOrder::AscendingOrder);
ui->mapCustomAttributesFrame->table()->setRestrictedKeys(project->topLevelMapFields);
@@ -1465,8 +1470,15 @@ void MainWindow::currentMetatilesSelectionChanged() {
scrollMetatileSelectorToSelection();
}
void MainWindow::saveMapListTab(int index) {
void MainWindow::onMapListTabChanged(int index) {
// Save current tab for future sessions.
porymapConfig.mapListTab = index;
// After changing a map list tab the old tab's search widget can keep focus, which isn't helpful
// (and might be a little confusing to the user, because they don't know that each search bar is secretly a separate object).
// When we change tabs we'll automatically focus in on the search bar. This should also make finding maps a little quicker.
auto toolbar = getCurrentMapListToolBar();
if (toolbar) toolbar->setSearchFocus();
}
void MainWindow::openMapListItem(const QModelIndex &index) {
@@ -1737,7 +1749,7 @@ void MainWindow::paste() {
if (!newEvents.empty()) {
editor->map->commit(new EventPaste(this->editor, editor->map, newEvents));
updateObjects();
updateEvents();
}
break;
@@ -1793,8 +1805,8 @@ void MainWindow::on_mainTabBar_tabBarClicked(int index)
clickToolButtonFromEditAction(editor->mapEditAction);
} else if (index == MainTab::Events) {
ui->stackedWidget_MapEvents->setCurrentIndex(1);
editor->setEditingObjects();
clickToolButtonFromEditAction(editor->objectEditAction);
editor->setEditingEvents();
clickToolButtonFromEditAction(editor->eventEditAction);
} else if (index == MainTab::Connections) {
editor->setEditingConnections();
ui->graphicsView_Connections->setFocus(); // Avoid opening tab with focus on something editable
@@ -1935,13 +1947,13 @@ void MainWindow::resetMapViewScale() {
void MainWindow::addNewEvent(Event::Type type) {
if (editor && editor->project) {
DraggablePixmapItem *object = editor->addNewEvent(type);
if (object) {
DraggablePixmapItem *item = editor->addNewEvent(type);
if (item) {
auto halfSize = ui->graphicsView_Map->size() / 2;
auto centerPos = ui->graphicsView_Map->mapToScene(halfSize.width(), halfSize.height());
object->moveTo(Metatile::coordFromPixmapCoord(centerPos));
updateObjects();
editor->selectMapEvent(object);
item->moveTo(Metatile::coordFromPixmapCoord(centerPos));
updateEvents();
editor->selectMapEvent(item);
} else {
WarningMessage msgBox(QStringLiteral("Failed to add new event."), this);
if (Event::typeToGroup(type) == Event::Group::Object) {
@@ -1973,17 +1985,17 @@ void MainWindow::displayEventTabs() {
tryAddEventTab(ui->tab_HealLocations);
}
void MainWindow::updateObjects() {
QList<DraggablePixmapItem *> all_objects = editor->getObjects();
void MainWindow::updateEvents() {
QList<DraggablePixmapItem *> items = editor->getEventPixmapItems();
for (auto i = this->lastSelectedEvent.cbegin(), end = this->lastSelectedEvent.cend(); i != end; i++) {
if (i.value() && !all_objects.contains(i.value()))
if (i.value() && !items.contains(i.value()))
this->lastSelectedEvent.insert(i.key(), nullptr);
}
displayEventTabs();
updateSelectedObjects();
updateSelectedEvents();
}
void MainWindow::updateSelectedObjects() {
void MainWindow::updateSelectedEvents() {
QList<DraggablePixmapItem *> events;
if (editor->selected_events && editor->selected_events->length()) {
@@ -1998,7 +2010,7 @@ void MainWindow::updateSelectedObjects() {
DraggablePixmapItem *selectedEvent = all_events.first()->getPixmapItem();
if (selectedEvent) {
editor->selected_events->append(selectedEvent);
editor->redrawObject(selectedEvent);
editor->redrawEventPixmapItem(selectedEvent);
events.append(selectedEvent);
}
}
@@ -2143,7 +2155,7 @@ Event::Group MainWindow::getEventGroupFromTabWidget(QWidget *tab) {
void MainWindow::eventTabChanged(int index) {
if (editor->map) {
Event::Group group = getEventGroupFromTabWidget(ui->tabWidget_EventType->widget(index));
DraggablePixmapItem *selectedEvent = this->lastSelectedEvent.value(group, nullptr);
DraggablePixmapItem *selectedItem = this->lastSelectedEvent.value(group, nullptr);
switch (group) {
case Event::Group::Object:
@@ -2166,18 +2178,11 @@ void MainWindow::eventTabChanged(int index) {
}
if (!isProgrammaticEventTabChange) {
if (!selectedEvent && editor->map->getNumEvents(group)) {
if (!selectedItem) {
Event *event = editor->map->getEvent(group, 0);
for (QGraphicsItem *child : editor->events_group->childItems()) {
DraggablePixmapItem *item = static_cast<DraggablePixmapItem *>(child);
if (item->event == event) {
selectedEvent = item;
break;
}
}
if (event) selectedItem = event->getPixmapItem();
}
if (selectedEvent) editor->selectMapEvent(selectedEvent);
if (selectedItem) editor->selectMapEvent(selectedItem);
}
}
@@ -2246,7 +2251,7 @@ void MainWindow::on_toolButton_Paint_clicked()
if (ui->mainTabBar->currentIndex() == MainTab::Map)
editor->mapEditAction = Editor::EditAction::Paint;
else
editor->objectEditAction = Editor::EditAction::Paint;
editor->eventEditAction = Editor::EditAction::Paint;
editor->settings->mapCursor = QCursor(QPixmap(":/icons/pencil_cursor.ico"), 10, 10);
@@ -2267,7 +2272,7 @@ void MainWindow::on_toolButton_Select_clicked()
if (ui->mainTabBar->currentIndex() == MainTab::Map)
editor->mapEditAction = Editor::EditAction::Select;
else
editor->objectEditAction = Editor::EditAction::Select;
editor->eventEditAction = Editor::EditAction::Select;
editor->settings->mapCursor = QCursor();
editor->cursorMapTileRect->setSingleTileMode();
@@ -2286,7 +2291,7 @@ void MainWindow::on_toolButton_Fill_clicked()
if (ui->mainTabBar->currentIndex() == MainTab::Map)
editor->mapEditAction = Editor::EditAction::Fill;
else
editor->objectEditAction = Editor::EditAction::Fill;
editor->eventEditAction = Editor::EditAction::Fill;
editor->settings->mapCursor = QCursor(QPixmap(":/icons/fill_color_cursor.ico"), 10, 10);
editor->cursorMapTileRect->setSingleTileMode();
@@ -2305,7 +2310,7 @@ void MainWindow::on_toolButton_Dropper_clicked()
if (ui->mainTabBar->currentIndex() == MainTab::Map)
editor->mapEditAction = Editor::EditAction::Pick;
else
editor->objectEditAction = Editor::EditAction::Pick;
editor->eventEditAction = Editor::EditAction::Pick;
editor->settings->mapCursor = QCursor(QPixmap(":/icons/pipette_cursor.ico"), 10, 10);
editor->cursorMapTileRect->setSingleTileMode();
@@ -2324,7 +2329,7 @@ void MainWindow::on_toolButton_Move_clicked()
if (ui->mainTabBar->currentIndex() == MainTab::Map)
editor->mapEditAction = Editor::EditAction::Move;
else
editor->objectEditAction = Editor::EditAction::Move;
editor->eventEditAction = Editor::EditAction::Move;
editor->settings->mapCursor = QCursor(QPixmap(":/icons/move.ico"), 7, 7);
editor->cursorMapTileRect->setSingleTileMode();
@@ -2343,7 +2348,7 @@ void MainWindow::on_toolButton_Shift_clicked()
if (ui->mainTabBar->currentIndex() == MainTab::Map)
editor->mapEditAction = Editor::EditAction::Shift;
else
editor->objectEditAction = Editor::EditAction::Shift;
editor->eventEditAction = Editor::EditAction::Shift;
editor->settings->mapCursor = QCursor(QPixmap(":/icons/shift_cursor.ico"), 10, 10);
editor->cursorMapTileRect->setSingleTileMode();
@@ -2362,7 +2367,7 @@ void MainWindow::checkToolButtons() {
if (ui->mainTabBar->currentIndex() == MainTab::Map) {
editAction = editor->mapEditAction;
} else {
editAction = editor->objectEditAction;
editAction = editor->eventEditAction;
if (editAction == Editor::EditAction::Select && editor->map_ruler)
editor->map_ruler->setEnabled(true);
else if (editor->map_ruler)
@@ -2589,87 +2594,39 @@ 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);
ResizeLayoutPopup popup(this->ui->graphicsView_Map, this->editor);
popup.show();
popup.setupLayoutView();
if (popup.exec() == QDialog::Accepted) {
Layout *layout = this->editor->layout;
Map *map = this->editor->map;
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());
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
));
}
// If we're in map-editing mode, adjust the events' position by the same amount.
if (map) {
auto events = map->getEvents();
int deltaX = result.left();
int deltaY = result.top();
if ((deltaX || deltaY) && !events.isEmpty()) {
map->commit(new EventShift(events, deltaX, deltaY, this->editor->eventShiftActionId++));
}
}
}
}
@@ -2848,8 +2805,15 @@ void MainWindow::reloadScriptEngine() {
Scripting::populateGlobalObject(this);
// Lying to the scripts here, simulating a project reload
Scripting::cb_ProjectOpened(projectConfig.projectDir);
if (editor && editor->map)
Scripting::cb_MapOpened(editor->map->name()); // TODO: API should have equivalent for layout
if (this->editor) {
QString curName;
if (this->editor->map)
curName = this->editor->map->name();
else if (editor->layout)
curName = this->editor->layout->name;
Scripting::cb_MapOpened(curName);
}
}
void MainWindow::on_horizontalSlider_MetatileZoom_valueChanged(int value) {

View File

@@ -398,6 +398,7 @@ Map *Project::createNewMap(const Project::NewMapSettings &settings, const Map* t
this->mapNameToMapSectionName.insert(map->name(), map->header()->location());
map->setIsPersistedToFile(false);
this->mapCache.insert(map->name(), map);
emit mapCreated(map, settings.group);
@@ -966,7 +967,7 @@ bool Project::loadLayoutTilesets(Layout *layout) {
layout->tileset_primary = getTileset(layout->tileset_primary_label);
if (!layout->tileset_primary) {
QString defaultTileset = this->getDefaultPrimaryTilesetLabel();
logWarn(QString("Map layout %1 has invalid primary tileset '%2'. Using default '%3'").arg(layout->id).arg(layout->tileset_primary_label).arg(defaultTileset));
logWarn(QString("%1 has invalid primary tileset '%2'. Using default '%3'").arg(layout->name).arg(layout->tileset_primary_label).arg(defaultTileset));
layout->tileset_primary_label = defaultTileset;
layout->tileset_primary = getTileset(layout->tileset_primary_label);
if (!layout->tileset_primary) {
@@ -978,7 +979,7 @@ bool Project::loadLayoutTilesets(Layout *layout) {
layout->tileset_secondary = getTileset(layout->tileset_secondary_label);
if (!layout->tileset_secondary) {
QString defaultTileset = this->getDefaultSecondaryTilesetLabel();
logWarn(QString("Map layout %1 has invalid secondary tileset '%2'. Using default '%3'").arg(layout->id).arg(layout->tileset_secondary_label).arg(defaultTileset));
logWarn(QString("%1 has invalid secondary tileset '%2'. Using default '%3'").arg(layout->name).arg(layout->tileset_secondary_label).arg(defaultTileset));
layout->tileset_secondary_label = defaultTileset;
layout->tileset_secondary = getTileset(layout->tileset_secondary_label);
if (!layout->tileset_secondary) {
@@ -1047,7 +1048,8 @@ bool Project::loadBlockdata(Layout *layout) {
layout->lastCommitBlocks.layoutDimensions = QSize(layout->getWidth(), layout->getHeight());
if (layout->blockdata.count() != layout->getWidth() * layout->getHeight()) {
logWarn(QString("Layout blockdata length %1 does not match dimensions %2x%3 (should be %4). Resizing blockdata.")
logWarn(QString("%1 blockdata length %2 does not match dimensions %3x%4 (should be %5). Resizing blockdata.")
.arg(layout->name)
.arg(layout->blockdata.count())
.arg(layout->getWidth())
.arg(layout->getHeight())
@@ -1084,7 +1086,8 @@ bool Project::loadLayoutBorder(Layout *layout) {
int borderLength = layout->getBorderWidth() * layout->getBorderHeight();
if (layout->border.count() != borderLength) {
logWarn(QString("Layout border blockdata length %1 must be %2. Resizing border blockdata.")
logWarn(QString("%1 border blockdata length %2 must be %3. Resizing border blockdata.")
.arg(layout->name)
.arg(layout->border.count())
.arg(borderLength));
layout->border.resize(borderLength);
@@ -1207,7 +1210,7 @@ void Project::saveMap(Map *map) {
}
mapObj["connections"] = connectionsArr;
} else {
mapObj["connections"] = QJsonValue::Null;
mapObj["connections"] = OrderedJson();
}
if (map->sharedEventsMap().isEmpty()) {

View File

@@ -794,7 +794,7 @@ void MainWindow::setMetatileTile(int metatileId, int tileIndex, QJSValue tileObj
}
QJSValue MainWindow::getTilePixels(int tileId) {
if (tileId < 0 || !this->editor || !this->editor->project || !this->editor->map || !this->editor->layout)
if (tileId < 0 || !this->editor || !this->editor->layout)
return QJSValue();
QImage tileImage = getTileImage(tileId, this->editor->layout->tileset_primary, this->editor->layout->tileset_secondary);
if (tileImage.isNull() || tileImage.sizeInBytes() < 64)

View File

@@ -249,6 +249,27 @@ QList<QString> ScriptUtility::getMapNames() {
return window->editor->project->mapNames;
}
QList<QString> ScriptUtility::getMapConstants() {
if (!window || !window->editor || !window->editor->project)
return QList<QString>();
return window->editor->project->mapConstantsToMapNames.keys();
}
QList<QString> ScriptUtility::getLayoutNames() {
QList<QString> names;
if (!window || !window->editor || !window->editor->project)
return names;
for (const auto &layout : window->editor->project->mapLayouts)
names.append(layout->name);
return names;
}
QList<QString> ScriptUtility::getLayoutConstants() {
if (!window || !window->editor || !window->editor->project)
return QList<QString>();
return window->editor->project->layoutIds;
}
QList<QString> ScriptUtility::getTilesetNames() {
if (!window || !window->editor || !window->editor->project)
return QList<QString>();

View File

@@ -29,7 +29,7 @@ void DraggablePixmapItem::emitPositionChanged() {
void DraggablePixmapItem::updatePixmap() {
editor->project->setEventPixmap(event, true);
this->updatePosition();
editor->redrawObject(this);
editor->redrawEventPixmapItem(this);
emit spriteChanged(event->getPixmap());
}

View File

@@ -1,10 +1,6 @@
#include "filterchildrenproxymodel.h"
FilterChildrenProxyModel::FilterChildrenProxyModel(QObject *parent) :
QSortFilterProxyModel(parent)
{
}
#include <QCollator>
bool FilterChildrenProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
@@ -42,3 +38,16 @@ bool FilterChildrenProxyModel::filterAcceptsRow(int source_row, const QModelInde
// parent call for initial behaviour
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
}
bool NumericSortProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const {
QVariant l = (source_left.model() ? source_left.model()->data(source_left, sortRole()) : QVariant());
QVariant r = (source_right.model() ? source_right.model()->data(source_right, sortRole()) : QVariant());
if (l.canConvert<QString>() && r.canConvert<QString>()) {
// We need to override lexical comparison of strings to do a numeric sort.
QCollator collator;
collator.setNumericMode(true);
return collator.compare(l.toString(), r.toString()) < 0;
}
return QSortFilterProxyModel::lessThan(source_left, source_right);
}

View File

@@ -5,7 +5,7 @@
void GraphicsView::mousePressEvent(QMouseEvent *event) {
QGraphicsView::mousePressEvent(event);
if (editor) {
editor->objectsView_onMousePress(event);
editor->eventsView_onMousePress(event);
}
}

View File

@@ -55,6 +55,12 @@ MapImageExporter::MapImageExporter(QWidget *parent, Editor *editor, ImageExporte
ui->comboBox_MapSelection->addItems(editor->project->mapNames);
ui->comboBox_MapSelection->setCurrentText(m_map->name());
ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down
} else {
// Some settings only apply to maps. When exporting an image in layout-only mode we hide them.
ui->comboBox_MapSelection->setVisible(false);
ui->label_MapSelection->setVisible(false);
ui->groupBox_Events->setVisible(false);
ui->groupBox_Connections->setVisible(false);
}
connect(ui->pushButton_Save, &QPushButton::pressed, this, &MapImageExporter::saveImage);

View File

@@ -135,9 +135,6 @@ QStandardItem *MapListModel::insertMapItem(const QString &mapName, const QString
if (folder) {
folder->appendRow(map);
}
if (this->sortingEnabled)
this->sort(0, Qt::AscendingOrder);
return map;
}
@@ -147,8 +144,6 @@ QStandardItem *MapListModel::insertMapFolderItem(const QString &folderName) {
QStandardItem *item = createMapFolderItem(folderName);
this->root->appendRow(item);
if (this->sortingEnabled)
this->sort(0, Qt::AscendingOrder);
return item;
}
@@ -432,9 +427,6 @@ MapLocationModel::MapLocationModel(Project *project, QObject *parent) : MapListM
for (const auto &mapName : this->project->mapNames) {
insertMapItem(mapName, this->project->mapNameToMapSectionName.value(mapName));
}
this->sortingEnabled = true;
sort(0, Qt::AscendingOrder);
}
void MapLocationModel::removeItem(QStandardItem *item) {
@@ -459,9 +451,6 @@ LayoutTreeModel::LayoutTreeModel(Project *project, QObject *parent) : MapListMod
for (const auto &mapName : this->project->mapNames) {
insertMapItem(mapName, this->project->mapNameToLayoutId.value(mapName));
}
this->sortingEnabled = true;
sort(0, Qt::AscendingOrder);
}
void LayoutTreeModel::removeItem(QStandardItem *) {

View File

@@ -139,3 +139,7 @@ void MapListToolBar::applyFilter(const QString &filterText) {
void MapListToolBar::clearFilter() {
applyFilter("");
}
void MapListToolBar::setSearchFocus() {
ui->lineEdit_filterBox->setFocus();
}

View File

@@ -9,12 +9,17 @@ QPoint MetatileSelector::getSelectionDimensions() {
return SelectablePixmapItem::getSelectionDimensions();
}
int MetatileSelector::numPrimaryMetatilesRounded() const {
// We round up the number of primary metatiles to keep the tilesets on separate rows.
return ceil((double)this->primaryTileset->numMetatiles() / this->numMetatilesWide) * this->numMetatilesWide;
}
void MetatileSelector::draw() {
if (!this->primaryTileset || !this->secondaryTileset) {
this->setPixmap(QPixmap());
}
int primaryLength = this->primaryTileset->numMetatiles();
int primaryLength = this->numPrimaryMetatilesRounded();
int length_ = primaryLength + this->secondaryTileset->numMetatiles();
int height_ = length_ / this->numMetatilesWide;
if (length_ % this->numMetatilesWide != 0) {
@@ -149,7 +154,7 @@ void MetatileSelector::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
void MetatileSelector::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
QPoint pos = this->getCellPos(event->pos());
if (!positionIsValid(pos) || this->cellPos == pos)
if (this->cellPos == pos)
return;
this->cellPos = pos;
@@ -199,10 +204,11 @@ void MetatileSelector::updateExternalSelectedMetatiles() {
uint16_t MetatileSelector::getMetatileId(int x, int y) const {
int index = y * this->numMetatilesWide + x;
if (index < this->primaryTileset->numMetatiles()) {
int numPrimary = this->numPrimaryMetatilesRounded();
if (index < numPrimary) {
return static_cast<uint16_t>(index);
} else {
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - this->primaryTileset->numMetatiles());
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - numPrimary);
}
}
@@ -215,7 +221,7 @@ QPoint MetatileSelector::getMetatileIdCoords(uint16_t metatileId) {
int index = metatileId < Project::getNumMetatilesPrimary()
? metatileId
: metatileId - Project::getNumMetatilesPrimary() + this->primaryTileset->numMetatiles();
: metatileId - Project::getNumMetatilesPrimary() + this->numPrimaryMetatilesRounded();
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
}

View File

@@ -1,17 +1,175 @@
#include <QCursor>
#include <QGraphicsSceneHoverEvent>
#include <QMessageBox>
#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 *) {
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 limits: smallest possible size is 16x16 square
if (resizedRect.width() < 16) {
if (dx < 0) { // right sided adjustment made
resizedRect.setWidth(16);
} else { // left sided adjustment slightly more complicated
int dxMax = this->clickedRect.right() - this->clickedRect.left() - 16;
resizedRect.adjust(dxMax - dx, 0, 0, 0);
}
}
if (resizedRect.height() < 16) {
if (dy < 0) { // bottom
resizedRect.setHeight(16);
} else { // top
int dyMax = this->clickedRect.bottom() - this->clickedRect.top() - 16;
resizedRect.adjust(0, dyMax - dy, 0, 0);
}
}
// Upper limits: clip resized to limit rect
this->updatePosFromRect(resizedRect & this->limit);
}

View File

@@ -50,7 +50,7 @@ void NewEventToolButton::init()
this->newSecretBaseAction->setIcon(QIcon(":/icons/add.ico"));
connect(this->newSecretBaseAction, &QAction::triggered, this, &NewEventToolButton::newSecretBase);
QMenu *alignMenu = new QMenu();
QMenu *alignMenu = new QMenu(this);
alignMenu->addAction(this->newObjectAction);
alignMenu->addAction(this->newCloneObjectAction);
alignMenu->addAction(this->newWarpAction);

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

@@ -112,7 +112,7 @@ void ProjectSettingsEditor::initUi() {
// Validate that the border metatiles text is a comma-separated list of metatile values
static const QString regex_Hex = "(0[xX])?[A-Fa-f0-9]+";
static const QRegularExpression expression_HexList(QString("^(%1,)*%1$").arg(regex_Hex)); // Comma-separated list of hex values
QRegularExpressionValidator *validator_HexList = new QRegularExpressionValidator(expression_HexList);
QRegularExpressionValidator *validator_HexList = new QRegularExpressionValidator(expression_HexList, this);
ui->lineEdit_BorderMetatiles->setValidator(validator_HexList);
this->setBorderMetatilesUi(projectConfig.useCustomBorderSize);

View File

@@ -0,0 +1,210 @@
#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))
paintColor = QColor(132, 217, 165); // green light color
else
paintColor = 0xbcbcbc; // normal light color
}
else {
if (this->validRect.contains(x, y))
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 *, QWidget *) {
// 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->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()));
}
void ResizeLayoutPopup::on_buttonBox_clicked(QAbstractButton *button) {
if(button == this->ui->buttonBox->button(QDialogButtonBox::Reset) ) {
this->scene->clear();
setupLayoutView();
}
}
/// 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;
// 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);
} 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);
static bool layoutSizeRectVisible = true;
this->outline = new ResizableRect(this, &layoutSizeRectVisible, this->editor->layout->getWidth(), this->editor->layout->getHeight(), qRgb(255, 0, 255));
this->outline->setLimit(cover->rect().toAlignedRect());
connect(outline, &ResizableRect::rectUpdated, [=](QRect rect){
// Note: this extra limit check needs access to the project values, so it is done here and not ResizableRect::mouseMoveEvent
// Upper limits: maximum metatiles in a map formula:
// max = (width + 15) * (height + 14)
// This limit can be found in fieldmap.c in pokeruby/pokeemerald/pokefirered.
int numMetatiles = editor->project->getMapDataSize(rect.width() / 16, rect.height() / 16);
int maxMetatiles = editor->project->getMaxMapDataSize();
if (numMetatiles > maxMetatiles) {
QString errorText = QString("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(rect.width() / 16)
.arg(rect.height() / 16)
.arg(numMetatiles);
QMessageBox warning;
warning.setIcon(QMessageBox::Warning);
warning.setText("The specified width and height are too large.");
warning.setInformativeText(errorText);
warning.setStandardButtons(QMessageBox::Ok);
warning.setDefaultButton(QMessageBox::Ok);
warning.exec();
// adjust rect to last accepted size
rect = this->scene->getValidRect();
}
this->scene->setValidRect(rect);
this->outline->setRect(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());
// TODO: is this an ideal size for all maps, or should this adjust based on starting dimensions?
this->ui->graphicsView->setTransform(QTransform::fromScale(0.5, 0.5));
this->ui->graphicsView->centerOn(layoutPixmap);
}
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());
}

View File

@@ -2,6 +2,7 @@
#include "ui_shortcutseditor.h"
#include "config.h"
#include "multikeyedit.h"
#include "message.h"
#include "log.h"
#include <QGroupBox>
@@ -46,6 +47,15 @@ void ShortcutsEditor::setShortcutableObjects(const QObjectList &shortcutableObje
void ShortcutsEditor::saveShortcuts() {
QMultiMap<const QObject *, QKeySequence> objects_keySequences;
for (auto it = multiKeyEdits_objects.cbegin(); it != multiKeyEdits_objects.cend(); ++it) {
if (!it.value()) {
// Some shortcuts cannot be saved. Pointers in the object map can become null if they are
// deleted externally while the shortcuts editor is open. Ideally we should try to restore
// the original object so the shortcut can be saved. Alternatively this could generally be
// prevented by making the shortcuts editor modal. For now, saving these shortcuts is skipped,
// and we warn the user that this happened.
ErrorMessage::show(QStringLiteral("Some shortcuts failed to save. Please close the Shortcuts Editor and retry."), this);
return;
}
if (it.key()->keySequences().isEmpty())
objects_keySequences.insert(it.value(), QKeySequence());
for (auto keySequence : it.key()->keySequences())
@@ -60,6 +70,7 @@ void ShortcutsEditor::saveShortcuts() {
// Restores default shortcuts but doesn't save until Apply or OK is clicked.
void ShortcutsEditor::resetShortcuts() {
for (auto it = multiKeyEdits_objects.begin(); it != multiKeyEdits_objects.end(); ++it) {
if (!it.value()) continue;
it.key()->blockSignals(true);
const auto defaults = shortcutsConfig.defaultShortcuts(it.value());
it.key()->setKeySequences(defaults);
@@ -90,6 +101,7 @@ bool ShortcutsEditor::stringPropertyIsNotEmpty(const QObject *object, const char
void ShortcutsEditor::populateMainContainer() {
for (auto object : labels_objects) {
if (!object) continue;
const auto shortcutContext = getShortcutContext(object);
if (!contexts_layouts.contains(shortcutContext))
addNewContextGroup(shortcutContext);

View File

@@ -29,13 +29,7 @@ TilesetEditor::TilesetEditor(Project *project, Layout *layout, QWidget *parent)
this->tileYFlip = ui->checkBox_yFlip->isChecked();
this->paletteId = ui->spinBox_paletteSelector->value();
// TODO: The dividing line at the moment is only accurate if the number of primary metatiles is divisible by 8.
// If it's not, the secondary metatiles will wrap above the line. This has other problems (like skewing
// metatile groups the user may have designed) so this should be fixed by filling the primary metatiles
// image with invalid magenta metatiles until it's divisible by 8. Then the line can be re-enabled as-is.
ui->actionShow_Tileset_Divider->setChecked(/*porymapConfig.showTilesetEditorDivider*/false);
ui->actionShow_Tileset_Divider->setVisible(false);
ui->actionShow_Tileset_Divider->setChecked(porymapConfig.showTilesetEditorDivider);
ui->spinBox_paletteSelector->setMinimum(0);
ui->spinBox_paletteSelector->setMaximum(Project::getNumPalettesTotal() - 1);
ui->lineEdit_metatileLabel->setValidator(new IdentifierValidator(this));
@@ -759,7 +753,7 @@ void TilesetEditor::on_actionChange_Metatiles_Count_triggered()
{
QDialog dialog(this, Qt::WindowTitleHint | Qt::WindowCloseButtonHint);
dialog.setWindowTitle("Change Number of Metatiles");
dialog.setWindowModality(Qt::NonModal);
dialog.setWindowModality(Qt::WindowModal);
QFormLayout form(&dialog);
@@ -1051,20 +1045,19 @@ void TilesetEditor::countMetatileUsage() {
// do not double count
metatileSelector->usedMetatiles.fill(0);
for (auto layout : this->project->mapLayouts.values()) {
bool usesPrimary = false;
bool usesSecondary = false;
for (auto layout : this->project->mapLayouts) {
// It's possible for a layout's tileset labels to change if they are invalid,
// so we need to load all the tilesets even if they aren't the tileset we're looking for.
// Otherwise the metatile usage counts may change because the layouts with invalid tilesets
// were updated to use a tileset we were looking for.
this->project->loadLayoutTilesets(layout);
if (layout->tileset_primary_label == this->primaryTileset->name) {
usesPrimary = true;
}
if (layout->tileset_secondary_label == this->secondaryTileset->name) {
usesSecondary = true;
}
bool usesPrimary = (layout->tileset_primary_label == this->primaryTileset->name);
bool usesSecondary = (layout->tileset_secondary_label == this->secondaryTileset->name);
if (usesPrimary || usesSecondary) {
this->project->loadLayout(layout);
if (!this->project->loadLayout(layout))
continue;
// for each block in the layout, mark in the vector that it is used
for (int i = 0; i < layout->blockdata.length(); i++) {
@@ -1097,9 +1090,9 @@ void TilesetEditor::countTileUsage() {
QSet<Tileset*> secondaryTilesets;
for (auto layout : this->project->mapLayouts.values()) {
this->project->loadLayoutTilesets(layout);
if (layout->tileset_primary_label == this->primaryTileset->name
|| layout->tileset_secondary_label == this->secondaryTileset->name) {
this->project->loadLayoutTilesets(layout);
// need to check metatiles
if (layout->tileset_primary && layout->tileset_secondary) {
primaryTilesets.insert(layout->tileset_primary);

View File

@@ -22,11 +22,16 @@ int TilesetEditorMetatileSelector::numRows(int numMetatiles) {
}
int TilesetEditorMetatileSelector::numRows() {
return this->numRows(this->primaryTileset->numMetatiles() + this->secondaryTileset->numMetatiles());
return this->numRows(this->numPrimaryMetatilesRounded() + this->secondaryTileset->numMetatiles());
}
int TilesetEditorMetatileSelector::numPrimaryMetatilesRounded() const {
// We round up the number of primary metatiles to keep the tilesets on separate rows.
return ceil((double)this->primaryTileset->numMetatiles() / this->numMetatilesWide) * this->numMetatilesWide;
}
QImage TilesetEditorMetatileSelector::buildAllMetatilesImage() {
return this->buildImage(0, this->primaryTileset->numMetatiles() + this->secondaryTileset->numMetatiles());
return this->buildImage(0, this->numPrimaryMetatilesRounded() + this->secondaryTileset->numMetatiles());
}
QImage TilesetEditorMetatileSelector::buildPrimaryMetatilesImage() {
@@ -39,11 +44,11 @@ QImage TilesetEditorMetatileSelector::buildSecondaryMetatilesImage() {
QImage TilesetEditorMetatileSelector::buildImage(int metatileIdStart, int numMetatiles) {
int numMetatilesHigh = this->numRows(numMetatiles);
int numPrimary = this->primaryTileset->numMetatiles();
int numPrimary = this->numPrimaryMetatilesRounded();
int maxPrimary = Project::getNumMetatilesPrimary();
bool includesPrimary = metatileIdStart < maxPrimary;
QImage image(this->numMetatilesWide * 32, numMetatilesHigh * 32, QImage::Format_RGBA8888);
QImage image(this->numMetatilesWide * this->cellWidth, numMetatilesHigh * this->cellHeight, QImage::Format_RGBA8888);
image.fill(Qt::magenta);
QPainter painter(&image);
for (int i = 0; i < numMetatiles; i++) {
@@ -57,10 +62,10 @@ QImage TilesetEditorMetatileSelector::buildImage(int metatileIdStart, int numMet
this->layout->metatileLayerOrder,
this->layout->metatileLayerOpacity,
true)
.scaled(32, 32);
.scaled(this->cellWidth, this->cellHeight);
int map_y = i / this->numMetatilesWide;
int map_x = i % this->numMetatilesWide;
QPoint metatile_origin = QPoint(map_x * 32, map_y * 32);
QPoint metatile_origin = QPoint(map_x * this->cellWidth, map_y * this->cellHeight);
painter.drawImage(metatile_origin, metatile_image);
}
painter.end();
@@ -107,10 +112,11 @@ uint16_t TilesetEditorMetatileSelector::getSelectedMetatileId() {
uint16_t TilesetEditorMetatileSelector::getMetatileId(int x, int y) {
int index = y * this->numMetatilesWide + x;
if (index < this->primaryTileset->numMetatiles()) {
int numPrimary = numPrimaryMetatilesRounded();
if (index < numPrimary) {
return static_cast<uint16_t>(index);
} else {
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - this->primaryTileset->numMetatiles());
return static_cast<uint16_t>(Project::getNumMetatilesPrimary() + index - numPrimary);
}
}
@@ -156,7 +162,7 @@ QPoint TilesetEditorMetatileSelector::getMetatileIdCoords(uint16_t metatileId) {
}
int index = metatileId < Project::getNumMetatilesPrimary()
? metatileId
: metatileId - Project::getNumMetatilesPrimary() + this->primaryTileset->numMetatiles();
: metatileId - Project::getNumMetatilesPrimary() + this->numPrimaryMetatilesRounded();
return QPoint(index % this->numMetatilesWide, index / this->numMetatilesWide);
}
@@ -176,12 +182,12 @@ void TilesetEditorMetatileSelector::drawGrid() {
const int numColumns = this->numMetatilesWide;
const int numRows = this->numRows();
for (int column = 1; column < numColumns; column++) {
int x = column * 32;
painter.drawLine(x, 0, x, numRows * 32);
int x = column * this->cellWidth;
painter.drawLine(x, 0, x, numRows * this->cellHeight);
}
for (int row = 1; row < numRows; row++) {
int y = row * 32;
painter.drawLine(0, y, numColumns * 32, y);
int y = row * this->cellHeight;
painter.drawLine(0, y, numColumns * this->cellWidth, y);
}
painter.end();
this->setPixmap(pixmap);
@@ -191,12 +197,12 @@ void TilesetEditorMetatileSelector::drawDivider() {
if (!this->showDivider)
return;
const int y = this->numRows(this->primaryTileset->numMetatiles()) * 32;
const int y = this->numRows(this->numPrimaryMetatilesRounded()) * this->cellHeight;
QPixmap pixmap = this->pixmap();
QPainter painter(&pixmap);
painter.setPen(Qt::white);
painter.drawLine(0, y, this->numMetatilesWide * 32, y);
painter.drawLine(0, y, this->numMetatilesWide * this->cellWidth, y);
painter.end();
this->setPixmap(pixmap);
}
@@ -212,7 +218,7 @@ void TilesetEditorMetatileSelector::drawFilters() {
void TilesetEditorMetatileSelector::drawUnused() {
// setup the circle with a line through it image to layer above unused metatiles
QPixmap redX(32, 32);
QPixmap redX(this->cellWidth, this->cellHeight);
redX.fill(Qt::transparent);
QPen whitePen(Qt::white);
@@ -223,21 +229,21 @@ void TilesetEditorMetatileSelector::drawUnused() {
QPainter oPainter(&redX);
oPainter.setPen(whitePen);
oPainter.drawEllipse(QRect(1, 1, 30, 30));
oPainter.drawEllipse(QRect(1, 1, this->cellWidth - 2, this->cellHeight - 2));
oPainter.setPen(pinkPen);
oPainter.drawEllipse(QRect(2, 2, 28, 28));
oPainter.drawEllipse(QRect(3, 3, 26, 26));
oPainter.drawEllipse(QRect(2, 2, this->cellWidth - 4, this->cellHeight - 4));
oPainter.drawEllipse(QRect(3, 3, this->cellWidth - 6, this->cellHeight - 6));
oPainter.setPen(whitePen);
oPainter.drawEllipse(QRect(4, 4, 24, 24));
oPainter.drawEllipse(QRect(4, 4, this->cellHeight - 8, this->cellHeight - 8));
whitePen.setWidth(5);
oPainter.setPen(whitePen);
oPainter.drawLine(0, 0, 31, 31);
oPainter.drawLine(0, 0, this->cellWidth - 1, this->cellHeight - 1);
pinkPen.setWidth(3);
oPainter.setPen(pinkPen);
oPainter.drawLine(2, 2, 29, 29);
oPainter.drawLine(2, 2, this->cellWidth - 3, this->cellHeight - 3);
oPainter.end();
@@ -247,19 +253,13 @@ void TilesetEditorMetatileSelector::drawUnused() {
QPainter unusedPainter(&metatilesPixmap);
unusedPainter.setOpacity(0.5);
int primaryLength = this->primaryTileset->numMetatiles();
int length_ = primaryLength + this->secondaryTileset->numMetatiles();
for (int i = 0; i < length_; i++) {
int tile = i;
if (i >= primaryLength) {
tile += Project::getNumMetatilesPrimary() - primaryLength;
}
if (!usedMetatiles[tile]) {
unusedPainter.drawPixmap((i % 8) * 32, (i / 8) * 32, redX);
}
for (int metatileId = 0; metatileId < this->usedMetatiles.size(); metatileId++) {
if (this->usedMetatiles.at(metatileId) || !Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
continue;
// Adjust position from center to top-left corner
QPoint pos = getMetatileIdCoordsOnWidget(metatileId) - QPoint(this->cellWidth / 2, this->cellHeight / 2);
unusedPainter.drawPixmap(pos.x(), pos.y(), redX);
}
unusedPainter.end();
this->setPixmap(metatilesPixmap);
@@ -268,38 +268,28 @@ void TilesetEditorMetatileSelector::drawUnused() {
void TilesetEditorMetatileSelector::drawCounts() {
QPen blackPen(Qt::black);
blackPen.setWidth(1);
QPixmap metatilesPixmap = this->pixmap();
QPainter countPainter(&metatilesPixmap);
countPainter.setPen(blackPen);
for (int tile = 0; tile < this->usedMetatiles.size(); tile++) {
int count = usedMetatiles[tile];
QString countText = QString::number(count);
if (count > 1000) countText = ">1k";
countPainter.drawText((tile % 8) * 32, (tile / 8) * 32 + 32, countText);
}
// write in white and black for contrast
QPen whitePen(Qt::white);
whitePen.setWidth(1);
countPainter.setPen(whitePen);
int primaryLength = this->primaryTileset->numMetatiles();
int length_ = primaryLength + this->secondaryTileset->numMetatiles();
QPixmap metatilesPixmap = this->pixmap();
QPainter countPainter(&metatilesPixmap);
for (int i = 0; i < length_; i++) {
int tile = i;
if (i >= primaryLength) {
tile += Project::getNumMetatilesPrimary() - primaryLength;
}
int count = usedMetatiles[tile];
QString countText = QString::number(count);
if (count > 1000) countText = ">1k";
countPainter.drawText((i % 8) * 32 + 1, (i / 8) * 32 + 32 - 1, countText);
for (int metatileId = 0; metatileId < this->usedMetatiles.size(); metatileId++) {
if (!Tileset::metatileIsValid(metatileId, this->primaryTileset, this->secondaryTileset))
continue;
int count = this->usedMetatiles.at(metatileId);
QString countText = (count > 1000) ? QStringLiteral(">1k") : QString::number(count);
// Adjust position from center to bottom-left corner
QPoint pos = getMetatileIdCoordsOnWidget(metatileId) + QPoint(-(this->cellWidth / 2), this->cellHeight / 2);
// write in black and white for contrast
countPainter.setPen(blackPen);
countPainter.drawText(pos.x(), pos.y(), countText);
countPainter.setPen(whitePen);
countPainter.drawText(pos.x() + 1, pos.y() - 1, countText);
}
countPainter.end();
this->setPixmap(metatilesPixmap);

View File

@@ -109,7 +109,7 @@ void WildMonChart::readTable() {
}
// Read data from the table, combining data for duplicate entries
const QList<double> tableFrequencies = this->table->percentages();
const QVector<double> tableFrequencies = this->table->percentages();
const QVector<WildPokemon> tablePokemon = this->table->encounterData().wildPokemon;
const int numRows = qMin(tableFrequencies.length(), tablePokemon.length());
const QString speciesPrefix = projectConfig.getIdentifier(ProjectIdentifier::define_species_prefix);

View File

@@ -58,13 +58,13 @@ void WildMonSearch::addTableEntry(const RowData &rowData) {
int row = ui->table_Results->rowCount();
ui->table_Results->insertRow(row);
auto groupItem = new QTableWidgetItem(rowData.groupName);
auto groupItem = new NumericSortTableItem(rowData.groupName);
groupItem->setData(ResultsDataRole::MapName, rowData.mapName);
ui->table_Results->setItem(row, ResultsColumn::Group, groupItem);
ui->table_Results->setItem(row, ResultsColumn::Field, new QTableWidgetItem(rowData.fieldName));
ui->table_Results->setItem(row, ResultsColumn::Level, new QTableWidgetItem(rowData.levelRange));
ui->table_Results->setItem(row, ResultsColumn::Chance, new QTableWidgetItem(rowData.chance));
ui->table_Results->setItem(row, ResultsColumn::Field, new NumericSortTableItem(rowData.fieldName));
ui->table_Results->setItem(row, ResultsColumn::Level, new NumericSortTableItem(rowData.levelRange));
ui->table_Results->setItem(row, ResultsColumn::Chance, new NumericSortTableItem(rowData.chance));
}
QList<WildMonSearch::RowData> WildMonSearch::search(const QString &species) const {
@@ -137,7 +137,6 @@ void WildMonSearch::updateResults(const QString &species) {
}
}
// TODO: This does a lexical sort... We might need custom item delegates to get proper numerical sorting in this table.
ui->table_Results->setSortingEnabled(true);
this->resultsCache.insert(species, results);