Merge pull request #617 from GriffinRichards/custom-grid

Add map grid settings
This commit is contained in:
GriffinR 2024-10-09 11:10:17 -04:00 committed by GitHub
commit 931b471cf1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1450 additions and 5400 deletions

310
forms/colorinputwidget.ui Normal file
View file

@ -0,0 +1,310 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ColorInputWidget</class>
<widget class="QGroupBox" name="ColorInputWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>221</width>
<height>212</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="2">
<widget class="QFrame" name="frame_InputMain">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<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>
<widget class="QFrame" name="frame_Labels">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<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>
<widget class="QLabel" name="label_Red">
<property name="text">
<string>Red</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_Green">
<property name="text">
<string>Green</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_Blue">
<property name="text">
<string>Blue</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_Sliders">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<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>
<widget class="QSlider" name="slider_Red">
<property name="maximum">
<number>31</number>
</property>
<property name="pageStep">
<number>4</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slider_Green">
<property name="maximum">
<number>31</number>
</property>
<property name="pageStep">
<number>4</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="slider_Blue">
<property name="maximum">
<number>31</number>
</property>
<property name="pageStep">
<number>4</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_SpinBoxes">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<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>
<widget class="QSpinBox" name="spinBox_Red">
<property name="maximum">
<number>255</number>
</property>
<property name="singleStep">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_Green">
<property name="maximum">
<number>255</number>
</property>
<property name="singleStep">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_Blue">
<property name="maximum">
<number>255</number>
</property>
<property name="singleStep">
<number>8</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QFrame" name="frame_ColorDisplay">
<property name="minimumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QFrame" name="frame_InputTop">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<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="QFrame" name="frame_Hex">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<layout class="QFormLayout" name="formLayout_3">
<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_Hex">
<property name="text">
<string>#</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_Hex"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QToolButton" name="button_Eyedrop">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/pipette.ico</normaloff>:/icons/pipette.ico</iconset>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../resources/images.qrc"/>
</resources>
<connections/>
</ui>

275
forms/gridsettingsdialog.ui Normal file
View file

@ -0,0 +1,275 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GridSettingsDialog</class>
<widget class="QDialog" name="GridSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>331</width>
<height>467</height>
</rect>
</property>
<property name="windowTitle">
<string>Grid Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>305</width>
<height>401</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_Color">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Color</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="NoScrollComboBox" name="comboBox_Style">
<property name="editable">
<bool>false</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::SizeAdjustPolicy::AdjustToContentsOnFirstShow</enum>
</property>
<property name="minimumContentsLength">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_Style">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Style</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_Dimensions">
<property name="title">
<string>Dimensions (in pixels)</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,1,5">
<item row="0" column="0" rowspan="2">
<widget class="QToolButton" name="button_LinkDimensions">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/link_broken.ico</normaloff>
<normalon>:/icons/link.ico</normalon>:/icons/link_broken.ico</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="popupMode">
<enum>QToolButton::ToolButtonPopupMode::InstantPopup</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="NoScrollSpinBox" name="spinBox_Height">
<property name="minimum">
<number>2</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_Height">
<property name="text">
<string>Height</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_Width">
<property name="text">
<string>Width</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="NoScrollSpinBox" name="spinBox_Width">
<property name="minimum">
<number>2</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_Offset">
<property name="title">
<string>Offset (in pixels)</string>
</property>
<layout class="QGridLayout" name="gridLayout_3" columnstretch="1,1,5">
<item row="1" column="1">
<widget class="QLabel" name="label_Y">
<property name="text">
<string>Y</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_X">
<property name="text">
<string>X</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="NoScrollSpinBox" name="spinBox_Y">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="NoScrollSpinBox" name="spinBox_X">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="2">
<widget class="QToolButton" name="button_LinkOffsets">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../resources/images.qrc">
<normaloff>:/icons/link_broken.ico</normaloff>
<normalon>:/icons/link.ico</normalon>:/icons/link_broken.ico</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="popupMode">
<enum>QToolButton::ToolButtonPopupMode::InstantPopup</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="1">
<widget class="ColorInputWidget" name="colorInput">
<property name="title">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok|QDialogButtonBox::StandardButton::RestoreDefaults</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>NoScrollSpinBox</class>
<extends>QSpinBox</extends>
<header>noscrollspinbox.h</header>
</customwidget>
<customwidget>
<class>NoScrollComboBox</class>
<extends>QComboBox</extends>
<header>noscrollcombobox.h</header>
</customwidget>
<customwidget>
<class>ColorInputWidget</class>
<extends>QGroupBox</extends>
<header>colorinputwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../resources/images.qrc"/>
</resources>
<connections/>
</ui>

View file

@ -3077,10 +3077,13 @@
</property>
<addaction name="actionZoom_In"/>
<addaction name="actionZoom_Out"/>
<addaction name="separator"/>
<addaction name="actionCursor_Tile_Outline"/>
<addaction name="actionPlayer_View_Rectangle"/>
<addaction name="actionBetter_Cursors"/>
<addaction name="actionDive_Emerge_Map"/>
<addaction name="actionShow_Grid"/>
<addaction name="actionGrid_Settings"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
@ -3312,7 +3315,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>Player View Rectangle</string>
<string>Show Player View Rectangle</string>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show the player's view rectangle on the map based on the cursor's position.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -3329,7 +3332,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>Cursor Tile Outline</string>
<string>Show Cursor Tile Outline</string>
</property>
<property name="shortcut">
<string>C</string>
@ -3428,7 +3431,26 @@
<bool>true</bool>
</property>
<property name="text">
<string>Dive/Emerge Map</string>
<string>Show Dive/Emerge Map</string>
</property>
</action>
<action name="actionShow_Grid">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Show Grid</string>
</property>
<property name="shortcut">
<string>Ctrl+G</string>
</property>
</action>
<action name="actionGrid_Settings">
<property name="text">
<string>Grid Settings...</string>
</property>
</action>
</widget>

File diff suppressed because it is too large Load diff

View file

@ -23,6 +23,7 @@
#include "collisionpixmapitem.h"
#include "mappixmapitem.h"
#include "settings.h"
#include "gridsettings.h"
#include "movablerect.h"
#include "cursortilerect.h"
#include "mapruler.h"
@ -48,6 +49,7 @@ public:
QPointer<Project> project = nullptr;
Map *map = nullptr;
Settings *settings;
GridSettings gridSettings;
void setProject(Project * project);
void saveProject();
void save();
@ -67,6 +69,7 @@ public:
void displayMapConnections();
void displayMapBorder();
void displayMapGrid();
void updateMapGrid();
void displayWildMonTables();
void updateMapBorder();
@ -118,7 +121,7 @@ public:
QPointer<CollisionPixmapItem> collision_item = nullptr;
QGraphicsItemGroup *events_group = nullptr;
QList<QGraphicsPixmapItem*> borderItems;
QList<QGraphicsLineItem*> gridLines;
QGraphicsItemGroup *mapGrid = nullptr;
MovableRect *playerViewRect = nullptr;
CursorTileRect *cursorMapTileRect = nullptr;
MapRuler *map_ruler = nullptr;
@ -165,6 +168,7 @@ public slots:
void maskNonVisibleConnectionTiles();
void onBorderMetatilesChanged();
void selectedEventIndexChanged(int index, Event::Group eventGroup);
void toggleGrid(bool);
private:
const QImage defaultCollisionImgSheet = QImage(":/images/collisions.png");
@ -219,7 +223,6 @@ private slots:
void onHoveredMapMovementPermissionCleared();
void onSelectedMetatilesChanged();
void onWheelZoom(int);
void onToggleGridClicked(bool);
signals:
void objectsChanged();
@ -231,6 +234,7 @@ signals:
void currentMetatilesSelectionChanged();
void mapRulerStatusChanged(const QString &);
void tilesetUpdated(QString);
void gridToggled(bool);
};
#endif // EDITOR_H

View file

@ -26,6 +26,7 @@
#include "shortcutseditor.h"
#include "preferenceeditor.h"
#include "projectsettingseditor.h"
#include "gridsettings.h"
#include "customscriptseditor.h"
#include "wildmonchart.h"
#include "updatepromoter.h"
@ -302,6 +303,8 @@ private slots:
void on_actionProject_Settings_triggered();
void on_actionCustom_Scripts_triggered();
void reloadScriptEngine();
void on_actionShow_Grid_triggered();
void on_actionGrid_Settings_triggered();
public:
Ui::MainWindow *ui;
@ -316,6 +319,7 @@ private:
QPointer<NewMapPopup> newMapPrompt = nullptr;
QPointer<PreferenceEditor> preferenceEditor = nullptr;
QPointer<ProjectSettingsEditor> projectSettingsEditor = nullptr;
QPointer<GridSettingsDialog> gridSettingsDialog = nullptr;
QPointer<CustomScriptsEditor> customScriptsEditor = nullptr;
QPointer<UpdatePromoter> updatePromoter = nullptr;
QPointer<NetworkAccessManager> networkAccessManager = nullptr;

View file

@ -0,0 +1,46 @@
#ifndef COLORINPUTWIDGET_H
#define COLORINPUTWIDGET_H
#include <QGroupBox>
#include <QValidator>
namespace Ui {
class ColorInputWidget;
}
class ColorInputWidget : public QGroupBox {
Q_OBJECT
public:
explicit ColorInputWidget(QWidget *parent = nullptr);
explicit ColorInputWidget(const QString &title, QWidget *parent = nullptr);
~ColorInputWidget();
void setColor(QRgb color);
QRgb color() const { return m_color; }
bool setBitDepth(int bits);
int bitDepth() const { return m_bitDepth; }
signals:
void colorChanged(QRgb color);
void bitDepthChanged(int bits);
void editingFinished();
private:
Ui::ColorInputWidget *ui;
QRgb m_color = 0;
int m_bitDepth = 0;
void init();
void updateColorUi();
void pickColor();
void blockEditSignals(bool block);
void setRgbFromSliders();
void setRgbFromSpinners();
void setRgbFromHexString(const QString &);
};
#endif // COLORINPUTWIDGET_H

99
include/ui/gridsettings.h Normal file
View file

@ -0,0 +1,99 @@
#ifndef GRIDSETTINGS_H
#define GRIDSETTINGS_H
#include <QDialog>
#include <QAbstractButton>
class GridSettings {
public:
explicit GridSettings() {};
~GridSettings() {};
enum Style {
Solid,
LargeDashes,
SmallDashes,
Crosshairs,
Dots,
};
uint width = 16;
uint height = 16;
int offsetX = 0;
int offsetY = 0;
Style style = Style::Solid;
QColor color = Qt::black;
QVector<qreal> getHorizontalDashPattern() const { return this->getDashPattern(this->width); }
QVector<qreal> getVerticalDashPattern() const { return this->getDashPattern(this->height); }
static QString getStyleName(Style style);
static GridSettings::Style getStyleFromName(const QString &name);
private:
static const QMap<Style, QString> styleToName;
QVector<qreal> getCenteredDashPattern(uint length, qreal dashLength, qreal gapLength) const;
QVector<qreal> getDashPattern(uint length) const;
};
inline bool operator==(const GridSettings &a, const GridSettings &b) {
return a.width == b.width
&& a.height == b.height
&& a.offsetX == b.offsetX
&& a.offsetY == b.offsetY
&& a.style == b.style
&& a.color == b.color;
}
inline bool operator!=(const GridSettings &a, const GridSettings &b) {
return !(operator==(a, b));
}
namespace Ui {
class GridSettingsDialog;
}
class GridSettingsDialog : public QDialog {
Q_OBJECT
public:
explicit GridSettingsDialog(QWidget *parent = nullptr);
explicit GridSettingsDialog(GridSettings *settings, QWidget *parent = nullptr);
~GridSettingsDialog();
void setSettings(const GridSettings &settings);
GridSettings settings() const { return *m_settings; }
void setDefaultSettings(const GridSettings &settings);
GridSettings defaultSettings() const { return m_defaultSettings; }
signals:
void changedGridSettings();
private:
Ui::GridSettingsDialog *ui;
GridSettings *const m_settings;
const GridSettings m_originalSettings;
GridSettings m_defaultSettings;
bool m_dimensionsLinked = true;
bool m_offsetsLinked = true;
bool m_ownedSettings = false;
void init();
void updateInput();
void setWidth(int value);
void setHeight(int value);
void setOffsetX(int value);
void setOffsetY(int value);
private slots:
void dialogButtonClicked(QAbstractButton *button);
void on_spinBox_Width_valueChanged(int value);
void on_spinBox_Height_valueChanged(int value);
void on_spinBox_X_valueChanged(int value);
void on_spinBox_Y_valueChanged(int value);
void on_comboBox_Style_currentTextChanged(const QString &text);
void onColorChanged(QRgb color);
};
#endif // GRIDSETTINGS_H

View file

@ -2,10 +2,8 @@
#define PALETTEEDITOR_H
#include <QMainWindow>
#include <QValidator>
#include <QSlider>
#include <QFrame>
#include <QLabel>
#include "colorinputwidget.h"
#include "project.h"
#include "history.h"
@ -32,43 +30,27 @@ public:
private:
Ui::PaletteEditor *ui;
Project *project = nullptr;
QList<QList<QSlider*>> sliders;
QList<QList<QSpinBox *>> spinners;
QList<QFrame*> frames;
QList<QToolButton *> pickButtons;
QList<QLineEdit *> hexEdits;
QList<ColorInputWidget*> colorInputs;
Tileset *primaryTileset;
Tileset *secondaryTileset;
QList<History<PaletteHistoryItem*>> palettesHistory;
void refreshColorUis();
void updateColorUi(int index, QRgb color);
void commitEditHistory(int paletteid);
Tileset* getTileset(int paletteId);
void refreshColorInputs();
void commitEditHistory();
void commitEditHistory(int paletteId);
void restoreWindowState();
void setSignalsEnabled(bool enabled);
void setColorsFromHistory(PaletteHistoryItem*, int);
void closeEvent(QCloseEvent*);
void pickColor(int i);
void setRgb(int index, QRgb rgb);
void setRgbFromSliders(int colorIndex);
void setRgbFromHexEdit(int colorIndex);
void setRgbFromSpinners(int colorIndex);
void setPalette(int paletteId, const QList<QRgb> &palette);
void setBitDepth(int bits);
int bitDepth = 24;
class HexCodeValidator : public QValidator {
virtual QValidator::State validate(QString &input, int &) const override {
input = input.toUpper();
return QValidator::Acceptable;
}
};
HexCodeValidator *hexValidator = nullptr;
static const int numColors = 16;
signals:
void closed();

View file

@ -51,6 +51,7 @@ SOURCES += src/core/block.cpp \
src/scriptapi/apiutility.cpp \
src/scriptapi/scripting.cpp \
src/ui/aboutporymap.cpp \
src/ui/colorinputwidget.cpp \
src/ui/connectionslistitem.cpp \
src/ui/customscriptseditor.cpp \
src/ui/customscriptslistitem.cpp \
@ -60,6 +61,7 @@ SOURCES += src/core/block.cpp \
src/ui/collisionpixmapitem.cpp \
src/ui/connectionpixmapitem.cpp \
src/ui/currentselectedmetatilespixmapitem.cpp \
src/ui/gridsettings.cpp \
src/ui/newmapconnectiondialog.cpp \
src/ui/overlay.cpp \
src/ui/prefab.cpp \
@ -157,6 +159,7 @@ HEADERS += include/core/block.h \
include/ui/collisionpixmapitem.h \
include/ui/connectionpixmapitem.h \
include/ui/currentselectedmetatilespixmapitem.h \
include/ui/gridsettings.h \
include/ui/newmapconnectiondialog.h \
include/ui/prefabframe.h \
include/ui/projectsettingseditor.h \
@ -173,6 +176,7 @@ HEADERS += include/core/block.h \
include/ui/prefabcreationdialog.h \
include/ui/regionmappixmapitem.h \
include/ui/citymappixmapitem.h \
include/ui/colorinputwidget.h \
include/ui/mapsceneeventfilter.h \
include/ui/metatilelayersitem.h \
include/ui/metatileselector.h \
@ -218,7 +222,9 @@ HEADERS += include/core/block.h \
include/ui/wildmonchart.h
FORMS += forms/mainwindow.ui \
forms/colorinputwidget.ui \
forms/connectionslistitem.ui \
forms/gridsettingsdialog.ui \
forms/newmapconnectiondialog.ui \
forms/prefabcreationdialog.ui \
forms/prefabframe.ui \

BIN
resources/icons/link.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
resources/icons/link_broken.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -17,6 +17,8 @@
<file>icons/folder_map.ico</file>
<file>icons/folder.ico</file>
<file>icons/help.ico</file>
<file>icons/link_broken.ico</file>
<file>icons/link.ico</file>
<file>icons/map_edited.ico</file>
<file>icons/map_opened.ico</file>
<file>icons/map.ico</file>

View file

@ -47,6 +47,10 @@ Editor::Editor(Ui::MainWindow* ui)
connect(ui->stackedWidget_WildMons, &QStackedWidget::currentChanged, [this] {
emit wildMonTableOpened(getCurrentWildMonTable());
});
connect(ui->toolButton_Open_Scripts, &QToolButton::pressed, this, &Editor::openMapScripts);
connect(ui->actionOpen_Project_in_Text_Editor, &QAction::triggered, this, &Editor::openProjectInTextEditor);
connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, this, &Editor::toggleGrid);
}
Editor::~Editor()
@ -1842,40 +1846,69 @@ int Editor::getBorderDrawDistance(int dimension) {
}
}
void Editor::onToggleGridClicked(bool checked) {
void Editor::toggleGrid(bool checked) {
if (porymapConfig.showGrid == checked)
return;
porymapConfig.showGrid = checked;
// Synchronize action and checkbox
const QSignalBlocker b_Action(ui->actionShow_Grid);
const QSignalBlocker b_Checkbox(ui->checkBox_ToggleGrid);
ui->actionShow_Grid->setChecked(checked);
ui->checkBox_ToggleGrid->setChecked(checked);
this->mapGrid->setVisible(checked);
if (ui->graphicsView_Map->scene())
ui->graphicsView_Map->scene()->update();
}
void Editor::clearMapGrid() {
for (QGraphicsLineItem* item : gridLines) {
if (item) delete item;
}
gridLines.clear();
delete this->mapGrid;
this->mapGrid = nullptr;
}
void Editor::displayMapGrid() {
clearMapGrid();
ui->checkBox_ToggleGrid->disconnect();
int pixelWidth = map->getWidth() * 16;
int pixelHeight = map->getHeight() * 16;
for (int i = 0; i <= map->getWidth(); i++) {
int x = i * 16;
QGraphicsLineItem *line = new QGraphicsLineItem(x, 0, x, pixelHeight);
line->setVisible(ui->checkBox_ToggleGrid->isChecked());
gridLines.append(line);
connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);});
// Note: The grid lines are not added to the scene. They need to be drawn on top of the overlay
// elements of the scripting API, so they're painted manually in MapView::drawForeground.
this->mapGrid = new QGraphicsItemGroup();
const int pixelMapWidth = map->getWidth() * 16;
const int pixelMapHeight = map->getHeight() * 16;
// The grid can be moved with a user-specified x/y offset. The grid's dash patterns will only wrap in full pattern increments,
// so we draw an additional row/column outside the map that can be revealed as the offset changes.
const int offsetX = (this->gridSettings.offsetX % this->gridSettings.width) - this->gridSettings.width;
const int offsetY = (this->gridSettings.offsetY % this->gridSettings.height) - this->gridSettings.height;
QPen pen;
pen.setColor(this->gridSettings.color);
// Create vertical lines
pen.setDashPattern(this->gridSettings.getVerticalDashPattern());
for (int i = offsetX; i <= pixelMapWidth; i += this->gridSettings.width) {
auto line = new QGraphicsLineItem(i, offsetY, i, pixelMapHeight);
line->setPen(pen);
this->mapGrid->addToGroup(line);
}
for (int j = 0; j <= map->getHeight(); j++) {
int y = j * 16;
QGraphicsLineItem *line = new QGraphicsLineItem(0, y, pixelWidth, y);
line->setVisible(ui->checkBox_ToggleGrid->isChecked());
gridLines.append(line);
connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, [=](bool checked){line->setVisible(checked);});
// Create horizontal lines
pen.setDashPattern(this->gridSettings.getHorizontalDashPattern());
for (int i = offsetY; i <= pixelMapHeight; i += this->gridSettings.height) {
auto line = new QGraphicsLineItem(offsetX, i, pixelMapWidth, i);
line->setPen(pen);
this->mapGrid->addToGroup(line);
}
connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, this, &Editor::onToggleGridClicked);
this->mapGrid->setVisible(porymapConfig.showGrid);
}
void Editor::updateMapGrid() {
displayMapGrid();
if (ui->graphicsView_Map->scene())
ui->graphicsView_Map->scene()->update();
}
void Editor::updatePrimaryTileset(QString tilesetLabel, bool forceLoad)

View file

@ -149,10 +149,6 @@ void MainWindow::initExtraShortcuts() {
shortcutReset_Zoom->setObjectName("shortcutZoom_Reset");
shortcutReset_Zoom->setWhatsThis("Zoom Reset");
auto *shortcutToggle_Grid = new Shortcut(QKeySequence("Ctrl+G"), ui->checkBox_ToggleGrid, SLOT(toggle()));
shortcutToggle_Grid->setObjectName("shortcutToggle_Grid");
shortcutToggle_Grid->setWhatsThis("Toggle Grid");
auto *shortcutDuplicate_Events = new Shortcut(QKeySequence("Ctrl+D"), this, SLOT(duplicate()));
shortcutDuplicate_Events->setObjectName("shortcutDuplicate_Events");
shortcutDuplicate_Events->setWhatsThis("Duplicate Selected Event(s)");
@ -317,8 +313,6 @@ void MainWindow::initEditor() {
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_Open_Scripts, &QToolButton::pressed, this->editor, &Editor::openMapScripts);
connect(ui->actionOpen_Project_in_Text_Editor, &QAction::triggered, this->editor, &Editor::openProjectInTextEditor);
this->loadUserSettings();
@ -465,27 +459,45 @@ void MainWindow::applyMapListFilter(QString filterText)
}
void MainWindow::loadUserSettings() {
const QSignalBlocker blocker1(ui->horizontalSlider_CollisionTransparency);
const QSignalBlocker blocker2(ui->slider_DiveEmergeMapOpacity);
const QSignalBlocker blocker3(ui->slider_DiveMapOpacity);
const QSignalBlocker blocker4(ui->slider_EmergeMapOpacity);
const QSignalBlocker blocker5(ui->horizontalSlider_MetatileZoom);
const QSignalBlocker blocker6(ui->horizontalSlider_CollisionZoom);
// Better Cursors
ui->actionBetter_Cursors->setChecked(porymapConfig.prettyCursors);
this->editor->settings->betterCursors = porymapConfig.prettyCursors;
// Player view rectangle
ui->actionPlayer_View_Rectangle->setChecked(porymapConfig.showPlayerView);
this->editor->settings->playerViewRectEnabled = porymapConfig.showPlayerView;
// Cursor tile outline
ui->actionCursor_Tile_Outline->setChecked(porymapConfig.showCursorTile);
this->editor->settings->cursorTileRectEnabled = porymapConfig.showCursorTile;
// Border
ui->checkBox_ToggleBorder->setChecked(porymapConfig.showBorder);
// Grid
const QSignalBlocker b_Grid(ui->checkBox_ToggleGrid);
ui->actionShow_Grid->setChecked(porymapConfig.showGrid);
ui->checkBox_ToggleGrid->setChecked(porymapConfig.showGrid);
// Mirror connections
ui->checkBox_MirrorConnections->setChecked(porymapConfig.mirrorConnectingMaps);
// Collision opacity/transparency
const QSignalBlocker b_CollisionTransparency(ui->horizontalSlider_CollisionTransparency);
this->editor->collisionOpacity = static_cast<qreal>(porymapConfig.collisionOpacity) / 100;
ui->horizontalSlider_CollisionTransparency->setValue(porymapConfig.collisionOpacity);
// Dive map opacity/transparency
const QSignalBlocker b_DiveEmergeOpacity(ui->slider_DiveEmergeMapOpacity);
const QSignalBlocker b_DiveMapOpacity(ui->slider_DiveMapOpacity);
const QSignalBlocker b_EmergeMapOpacity(ui->slider_EmergeMapOpacity);
ui->slider_DiveEmergeMapOpacity->setValue(porymapConfig.diveEmergeMapOpacity);
ui->slider_DiveMapOpacity->setValue(porymapConfig.diveMapOpacity);
ui->slider_EmergeMapOpacity->setValue(porymapConfig.emergeMapOpacity);
// Zoom
const QSignalBlocker b_MetatileZoom(ui->horizontalSlider_MetatileZoom);
const QSignalBlocker b_CollisionZoom(ui->horizontalSlider_CollisionZoom);
ui->horizontalSlider_MetatileZoom->setValue(porymapConfig.metatilesZoom);
ui->horizontalSlider_CollisionZoom->setValue(porymapConfig.collisionZoom);
@ -1910,6 +1922,18 @@ void MainWindow::on_actionCursor_Tile_Outline_triggered()
}
}
void MainWindow::on_actionShow_Grid_triggered() {
this->editor->toggleGrid(ui->actionShow_Grid->isChecked());
}
void MainWindow::on_actionGrid_Settings_triggered() {
if (!this->gridSettingsDialog) {
this->gridSettingsDialog = new GridSettingsDialog(&this->editor->gridSettings, this);
connect(this->gridSettingsDialog, &GridSettingsDialog::changedGridSettings, this->editor, &Editor::updateMapGrid);
}
openSubWindow(this->gridSettingsDialog);
}
void MainWindow::on_actionShortcuts_triggered()
{
if (!shortcutsEditor)

229
src/ui/colorinputwidget.cpp Normal file
View file

@ -0,0 +1,229 @@
#include "colorinputwidget.h"
#include "ui_colorinputwidget.h"
#include "colorpicker.h"
#include <cmath>
class HexCodeValidator : public QValidator {
virtual QValidator::State validate(QString &input, int &) const override {
input = input.toUpper();
return QValidator::Acceptable;
}
};
static inline int rgb5(int rgb) { return round(static_cast<double>(rgb * 31) / 255.0); }
static inline int rgb8(int rgb) { return round(rgb * 255. / 31.); }
static inline int gbaRed(int rgb) { return rgb & 0x1f; }
static inline int gbaGreen(int rgb) { return (rgb >> 5) & 0x1f; }
static inline int gbaBlue(int rgb) { return (rgb >> 10) & 0x1f; }
ColorInputWidget::ColorInputWidget(QWidget *parent) :
QGroupBox(parent),
ui(new Ui::ColorInputWidget)
{
init();
}
ColorInputWidget::ColorInputWidget(const QString &title, QWidget *parent) :
QGroupBox(title, parent),
ui(new Ui::ColorInputWidget)
{
init();
}
void ColorInputWidget::init() {
ui->setupUi(this);
// Connect color change signals
connect(ui->slider_Red, &QSlider::valueChanged, this, &ColorInputWidget::setRgbFromSliders);
connect(ui->slider_Green, &QSlider::valueChanged, this, &ColorInputWidget::setRgbFromSliders);
connect(ui->slider_Blue, &QSlider::valueChanged, this, &ColorInputWidget::setRgbFromSliders);
connect(ui->spinBox_Red, QOverload<int>::of(&QSpinBox::valueChanged), this, &ColorInputWidget::setRgbFromSpinners);
connect(ui->spinBox_Green, QOverload<int>::of(&QSpinBox::valueChanged), this, &ColorInputWidget::setRgbFromSpinners);
connect(ui->spinBox_Blue, QOverload<int>::of(&QSpinBox::valueChanged), this, &ColorInputWidget::setRgbFromSpinners);
static const HexCodeValidator hexValidator;
ui->lineEdit_Hex->setValidator(&hexValidator);
connect(ui->lineEdit_Hex, &QLineEdit::textEdited, this, &ColorInputWidget::setRgbFromHexString);
// We have separate signals for when color input editing finishes.
// This is mostly useful for external commit histories, esp. for the sliders which can rapidly emit color change signals.
connect(ui->slider_Red, &QSlider::sliderReleased, this, &ColorInputWidget::editingFinished);
connect(ui->slider_Green, &QSlider::sliderReleased, this, &ColorInputWidget::editingFinished);
connect(ui->slider_Blue, &QSlider::sliderReleased, this, &ColorInputWidget::editingFinished);
connect(ui->spinBox_Red, &QSpinBox::editingFinished, this, &ColorInputWidget::editingFinished);
connect(ui->spinBox_Green, &QSpinBox::editingFinished, this, &ColorInputWidget::editingFinished);
connect(ui->spinBox_Blue, &QSpinBox::editingFinished, this, &ColorInputWidget::editingFinished);
connect(ui->lineEdit_Hex, &QLineEdit::editingFinished, this, &ColorInputWidget::editingFinished);
// Connect color picker
connect(ui->button_Eyedrop, &QToolButton::clicked, this, &ColorInputWidget::pickColor);
setBitDepth(24);
}
ColorInputWidget::~ColorInputWidget() {
delete ui;
}
void ColorInputWidget::updateColorUi() {
blockEditSignals(true);
int red = qRed(m_color);
int green = qGreen(m_color);
int blue = qBlue(m_color);
if (m_bitDepth == 15) {
// Sliders
ui->slider_Red->setValue(rgb5(red));
ui->slider_Green->setValue(rgb5(green));
ui->slider_Blue->setValue(rgb5(blue));
// Hex
int hex15 = (rgb5(blue) << 10) | (rgb5(green) << 5) | rgb5(red);
ui->lineEdit_Hex->setText(QString("%1").arg(hex15, 4, 16, QLatin1Char('0')).toUpper());
// Spinners
ui->spinBox_Red->setValue(rgb5(red));
ui->spinBox_Green->setValue(rgb5(green));
ui->spinBox_Blue->setValue(rgb5(blue));
} else {
// Sliders
ui->slider_Red->setValue(red);
ui->slider_Green->setValue(green);
ui->slider_Blue->setValue(blue);
// Hex
QColor color(red, green, blue);
ui->lineEdit_Hex->setText(color.name().remove(0, 1).toUpper());
// Spinners
ui->spinBox_Red->setValue(red);
ui->spinBox_Green->setValue(green);
ui->spinBox_Blue->setValue(blue);
}
ui->frame_ColorDisplay->setStyleSheet(QString("background-color: rgb(%1, %2, %3);").arg(red).arg(green).arg(blue));
blockEditSignals(false);
}
void ColorInputWidget::blockEditSignals(bool block) {
ui->slider_Red->blockSignals(block);
ui->slider_Green->blockSignals(block);
ui->slider_Blue->blockSignals(block);
ui->spinBox_Red->blockSignals(block);
ui->spinBox_Green->blockSignals(block);
ui->spinBox_Blue->blockSignals(block);
ui->lineEdit_Hex->blockSignals(block);
}
bool ColorInputWidget::setBitDepth(int bits) {
if (m_bitDepth == bits)
return true;
int singleStep, pageStep, maximum;
QString hexInputMask;
if (bits == 15) {
singleStep = 1;
pageStep = 4;
maximum = 31;
hexInputMask = "HHHH";
} else if (bits == 24) {
singleStep = 8;
pageStep = 24;
maximum = 255;
hexInputMask = "HHHHHH";
} else {
// Unsupported bit depth
return false;
}
m_bitDepth = bits;
blockEditSignals(true);
ui->slider_Red->setSingleStep(singleStep);
ui->slider_Green->setSingleStep(singleStep);
ui->slider_Blue->setSingleStep(singleStep);
ui->slider_Red->setPageStep(pageStep);
ui->slider_Green->setPageStep(pageStep);
ui->slider_Blue->setPageStep(pageStep);
ui->slider_Red->setMaximum(maximum);
ui->slider_Green->setMaximum(maximum);
ui->slider_Blue->setMaximum(maximum);
ui->spinBox_Red->setSingleStep(singleStep);
ui->spinBox_Green->setSingleStep(singleStep);
ui->spinBox_Blue->setSingleStep(singleStep);
ui->spinBox_Red->setMaximum(maximum);
ui->spinBox_Green->setMaximum(maximum);
ui->spinBox_Blue->setMaximum(maximum);
ui->lineEdit_Hex->setInputMask(hexInputMask);
ui->lineEdit_Hex->setMaxLength(hexInputMask.length());
updateColorUi();
blockEditSignals(false);
emit bitDepthChanged(m_bitDepth);
return true;
}
void ColorInputWidget::setColor(QRgb rgb) {
if (m_color == rgb)
return;
m_color = rgb;
updateColorUi();
emit colorChanged(m_color);
}
void ColorInputWidget::setRgbFromSliders() {
if (m_bitDepth == 15) {
setColor(qRgb(rgb8(ui->slider_Red->value()),
rgb8(ui->slider_Green->value()),
rgb8(ui->slider_Blue->value())));
} else {
setColor(qRgb(ui->slider_Red->value(),
ui->slider_Green->value(),
ui->slider_Blue->value()));
}
}
void ColorInputWidget::setRgbFromSpinners() {
if (m_bitDepth == 15) {
setColor(qRgb(rgb8(ui->spinBox_Red->value()), rgb8(ui->spinBox_Green->value()), rgb8(ui->spinBox_Blue->value())));
} else {
setColor(qRgb(ui->spinBox_Red->value(), ui->spinBox_Green->value(), ui->spinBox_Blue->value()));
}
}
void ColorInputWidget::setRgbFromHexString(const QString &text) {
if ((m_bitDepth == 24 && text.length() != 6)
|| (m_bitDepth == 15 && text.length() != 4))
return;
bool ok = false;
int rgb = text.toInt(&ok, 16);
if (!ok) rgb = 0xFFFFFFFF;
if (m_bitDepth == 15) {
int rc = gbaRed(rgb);
int gc = gbaGreen(rgb);
int bc = gbaBlue(rgb);
setColor(qRgb(rgb8(rc), rgb8(gc), rgb8(bc)));
} else {
setColor(qRgb(qRed(rgb), qGreen(rgb), qBlue(rgb)));
}
}
void ColorInputWidget::pickColor() {
ColorPicker picker(this);
if (picker.exec() == QDialog::Accepted) {
QColor c = picker.getColor();
setColor(c.rgb());
emit editingFinished();
}
}

View file

@ -31,10 +31,24 @@ void MapView::drawForeground(QPainter *painter, const QRectF&) {
if (!editor) return;
QStyleOptionGraphicsItem option;
for (QGraphicsLineItem* line : editor->gridLines) {
if (line && line->isVisible())
line->paint(painter, &option, this);
// Draw elements of the map view that should always render on top of anything added by the user with the scripting API.
// Draw map grid
if (editor->mapGrid && editor->mapGrid->isVisible()) {
painter->save();
if (editor->map) {
// We're clipping here to hide parts of the grid that are outside the map.
const QRectF mapRect(-0.5, -0.5, editor->map->getWidth() * 16 + 1.5, editor->map->getHeight() * 16 + 1.5);
painter->setClipping(true);
painter->setClipRect(mapRect);
}
for (auto item : editor->mapGrid->childItems())
item->paint(painter, &option, this);
painter->restore();
}
// Draw cursor rectangles
if (editor->playerViewRect && editor->playerViewRect->isVisible())
editor->playerViewRect->paint(painter, &option, this);
if (editor->cursorMapTileRect && editor->cursorMapTileRect->isVisible())

230
src/ui/gridsettings.cpp Normal file
View file

@ -0,0 +1,230 @@
#include "ui_gridsettingsdialog.h"
#include "gridsettings.h"
// TODO: Save settings in config
const QMap<GridSettings::Style, QString> GridSettings::styleToName = {
{Style::Solid, "Solid"},
{Style::LargeDashes, "Large Dashes"},
{Style::SmallDashes, "Small Dashes"},
{Style::Crosshairs, "Crosshairs"},
{Style::Dots, "Dots"},
};
QString GridSettings::getStyleName(GridSettings::Style style) {
return styleToName.value(style);
}
GridSettings::Style GridSettings::getStyleFromName(const QString &name) {
return styleToName.key(name, GridSettings::Style::Solid);
}
// We do some extra work here to A: try and center the dashes away from the intersections, and B: keep the dash pattern's total
// length equal to the length of a grid square. This keeps the patterns looking reasonable regardless of the grid size.
// Otherwise, the dashes can start to intersect in weird ways and create grid patterns that don't look like a rectangular grid.
QVector<qreal> GridSettings::getCenteredDashPattern(uint length, qreal dashLength, qreal gapLength) const {
const qreal minEdgesLength = 0.6*2;
if (length <= dashLength + minEdgesLength)
return {dashLength};
// Every dash after the first one needs to have room for a 'gapLength' segment.
const int numDashes = 1 + ((length - minEdgesLength) - dashLength) / (dashLength + gapLength);
// Total length of the pattern excluding the centering edges. There are always 1 fewer gap segments than dashes.
const qreal mainLength = (dashLength * numDashes) + (gapLength * (numDashes-1));
const qreal edgeLength = (length - mainLength) / 2;
// Fill the pattern
QVector<qreal> pattern = {0, edgeLength};
for (int i = 0; i < numDashes-1; i++) {
pattern.append(dashLength);
pattern.append(gapLength);
}
pattern.append(dashLength);
pattern.append(edgeLength);
return pattern;
}
QVector<qreal> GridSettings::getDashPattern(uint length) const {
switch (this->style) {
// Equivalent to setting Qt::PenStyle::Solid with no dash pattern.
case Style::Solid: return {1, 0};
// Roughly equivalent to Qt::PenStyle::DashLine but with centering.
case Style::LargeDashes: return getCenteredDashPattern(length, 3.0, 2.0);
// Roughly equivalent to Qt::PenStyle::DotLine but with centering.
case Style::SmallDashes: return getCenteredDashPattern(length, 1.0, 2.5);
// Dashes only at intersections, in the shape of a crosshair.
case Style::Crosshairs: {
const qreal crosshairLength = 2.0;
return {crosshairLength / 2, length - crosshairLength, crosshairLength / 2, 0};
}
// Dots only at intersections.
case Style::Dots: {
const qreal dotLength = 0.1;
return {dotLength, length - dotLength};
}
// Invalid
default: return {};
}
}
GridSettingsDialog::GridSettingsDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::GridSettingsDialog),
m_settings(new GridSettings),
m_originalSettings(*m_settings)
{
m_ownedSettings = true;
init();
}
GridSettingsDialog::GridSettingsDialog(GridSettings *settings, QWidget *parent) :
QDialog(parent),
ui(new Ui::GridSettingsDialog),
m_settings(settings),
m_originalSettings(*settings)
{
m_ownedSettings = false;
init();
}
void GridSettingsDialog::init() {
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
// Populate the styles combo box
const QSignalBlocker b_Style(ui->comboBox_Style);
ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::Solid));
ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::LargeDashes));
ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::SmallDashes));
ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::Crosshairs));
ui->comboBox_Style->addItem(GridSettings::getStyleName(GridSettings::Style::Dots));
ui->button_LinkDimensions->setChecked(m_dimensionsLinked);
ui->button_LinkOffsets->setChecked(m_offsetsLinked);
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &GridSettingsDialog::dialogButtonClicked);
connect(ui->button_LinkDimensions, &QAbstractButton::toggled, [this](bool on) { m_dimensionsLinked = on; });
connect(ui->button_LinkOffsets, &QAbstractButton::toggled, [this](bool on) { m_offsetsLinked = on; });
connect(ui->colorInput, &ColorInputWidget::colorChanged, this, &GridSettingsDialog::onColorChanged);
updateInput();
}
GridSettingsDialog::~GridSettingsDialog() {
delete ui;
if (m_ownedSettings)
delete m_settings;
}
void GridSettingsDialog::setSettings(const GridSettings &settings) {
if (*m_settings == settings)
return;
*m_settings = settings;
updateInput();
emit changedGridSettings();
}
void GridSettingsDialog::updateInput() {
setWidth(m_settings->width);
setHeight(m_settings->height);
setOffsetX(m_settings->offsetX);
setOffsetY(m_settings->offsetY);
const QSignalBlocker b_Color(ui->colorInput);
ui->colorInput->setColor(m_settings->color.rgb());
const QSignalBlocker b_Style(ui->comboBox_Style);
ui->comboBox_Style->setCurrentText(GridSettings::getStyleName(m_settings->style));
}
void GridSettingsDialog::setWidth(int value) {
const QSignalBlocker b(ui->spinBox_Width);
ui->spinBox_Width->setValue(value);
m_settings->width = value;
}
void GridSettingsDialog::setHeight(int value) {
const QSignalBlocker b(ui->spinBox_Height);
ui->spinBox_Height->setValue(value);
m_settings->height = value;
}
void GridSettingsDialog::setOffsetX(int value) {
const QSignalBlocker b(ui->spinBox_X);
ui->spinBox_X->setValue(value);
m_settings->offsetX = value;
}
void GridSettingsDialog::setOffsetY(int value) {
const QSignalBlocker b(ui->spinBox_Y);
ui->spinBox_Y->setValue(value);
m_settings->offsetY = value;
}
void GridSettingsDialog::on_spinBox_Width_valueChanged(int value) {
setWidth(value);
if (m_dimensionsLinked)
setHeight(value);
emit changedGridSettings();
}
void GridSettingsDialog::on_spinBox_Height_valueChanged(int value) {
setHeight(value);
if (m_dimensionsLinked)
setWidth(value);
emit changedGridSettings();
}
void GridSettingsDialog::on_spinBox_X_valueChanged(int value) {
setOffsetX(value);
if (m_offsetsLinked)
setOffsetY(value);
emit changedGridSettings();
}
void GridSettingsDialog::on_spinBox_Y_valueChanged(int value) {
setOffsetY(value);
if (m_offsetsLinked)
setOffsetX(value);
emit changedGridSettings();
}
void GridSettingsDialog::on_comboBox_Style_currentTextChanged(const QString &text) {
m_settings->style = GridSettings::getStyleFromName(text);
emit changedGridSettings();
}
void GridSettingsDialog::onColorChanged(QRgb color) {
m_settings->color = QColor::fromRgb(color);
emit changedGridSettings();
}
void GridSettingsDialog::dialogButtonClicked(QAbstractButton *button) {
auto role = ui->buttonBox->buttonRole(button);
if (role == QDialogButtonBox::AcceptRole) {
// "OK"
close();
} else if (role == QDialogButtonBox::RejectRole) {
// "Cancel"
setSettings(m_originalSettings);
close();
} else if (role == QDialogButtonBox::ResetRole) {
// "Restore Defaults"
setSettings(m_defaultSettings);
}
}

View file

@ -1,19 +1,12 @@
#include "paletteeditor.h"
#include "ui_paletteeditor.h"
#include "colorpicker.h"
#include "paletteutil.h"
#include "config.h"
#include "log.h"
#include <cmath>
#include <QFileDialog>
#include <QMessageBox>
static inline int rgb5(int rgb) { return round(static_cast<double>(rgb * 31) / 255.0); }
static inline int rgb8(int rgb) { return round(rgb * 255. / 31.); }
static inline int gbaRed(int rgb) { return rgb & 0x1f; }
static inline int gbaGreen(int rgb) { return (rgb >> 5) & 0x1f; }
static inline int gbaBlue(int rgb) { return (rgb >> 10) & 0x1f; }
PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset *secondaryTileset, int paletteId, QWidget *parent) :
QMainWindow(parent),
@ -26,55 +19,14 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
this->ui->spinBox_PaletteId->setMinimum(0);
this->ui->spinBox_PaletteId->setMaximum(Project::getNumPalettesTotal() - 1);
this->sliders.clear();
for (int i = 0; i < 16; i++) {
QList<QSlider *> rgbSliders;
rgbSliders.append(this->ui->container->findChild<QSlider *>("slider_red_" + QString::number(i)));
rgbSliders.append(this->ui->container->findChild<QSlider *>("slider_green_" + QString::number(i)));
rgbSliders.append(this->ui->container->findChild<QSlider *>("slider_blue_" + QString::number(i)));
this->sliders.append(rgbSliders);
connect(this->sliders[i][0], &QSlider::valueChanged, [=](int) { setRgbFromSliders(i); });
connect(this->sliders[i][1], &QSlider::valueChanged, [=](int) { setRgbFromSliders(i); });
connect(this->sliders[i][2], &QSlider::valueChanged, [=](int) { setRgbFromSliders(i); });
}
this->spinners.clear();
for (int i = 0; i < 16; i++) {
QList<QSpinBox *> rgbSpinners;
rgbSpinners.append(this->ui->container->findChild<QSpinBox *>("spin_red_" + QString::number(i)));
rgbSpinners.append(this->ui->container->findChild<QSpinBox *>("spin_green_" + QString::number(i)));
rgbSpinners.append(this->ui->container->findChild<QSpinBox *>("spin_blue_" + QString::number(i)));
this->spinners.append(rgbSpinners);
connect(this->spinners[i][0], QOverload<int>::of(&QSpinBox::valueChanged), [=](int) { setRgbFromSpinners(i); });
connect(this->spinners[i][1], QOverload<int>::of(&QSpinBox::valueChanged), [=](int) { setRgbFromSpinners(i); });
connect(this->spinners[i][2], QOverload<int>::of(&QSpinBox::valueChanged), [=](int) { setRgbFromSpinners(i); });
}
this->frames.clear();
for (int i = 0; i < 16; i++) {
this->frames.append(this->ui->container->findChild<QFrame *>("colorFrame_" + QString::number(i)));
this->frames[i]->setFrameStyle(QFrame::NoFrame);
}
this->pickButtons.clear();
for (int i = 0; i < 16; i++) {
this->pickButtons.append(this->ui->container->findChild<QToolButton *>("pick_" + QString::number(i)));
}
this->hexValidator = new HexCodeValidator;
this->hexEdits.clear();
for (int i = 0; i < 16; i++) {
this->hexEdits.append(this->ui->container->findChild<QLineEdit *>("hex_" + QString::number(i)));
this->hexEdits[i]->setValidator(hexValidator);
}
// Connect to function that will update color when hex edit is changed
for (int i = 0; i < this->hexEdits.length(); i++) {
connect(this->hexEdits[i], &QLineEdit::textEdited, [this, i](QString text){
if ((this->bitDepth == 24 && text.length() == 6) || (this->bitDepth == 15 && text.length() == 4)) setRgbFromHexEdit(i);
});
this->colorInputs.clear();
const int numColorsPerRow = 4;
for (int i = 0; i < this->numColors; i++) {
auto colorInput = new ColorInputWidget(QString("Color %1").arg(i));
connect(colorInput, &ColorInputWidget::colorChanged, [this, i](QRgb color) { setRgb(i, color); });
connect(colorInput, &ColorInputWidget::editingFinished, [this] { commitEditHistory(); });
this->colorInputs.append(colorInput);
ui->layout_Colors->addWidget(colorInput, i / numColorsPerRow, i % numColorsPerRow);
}
// Setup edit-undo history for each of the palettes.
@ -82,11 +34,6 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
this->palettesHistory.append(History<PaletteHistoryItem*>());
}
// Connect the color picker's selection to the correct color index
for (int i = 0; i < 16; i++) {
connect(this->pickButtons[i], &QToolButton::clicked, [this, i](){ this->pickColor(i); });
}
int bitDepth = porymapConfig.paletteEditorBitDepth;
if (bitDepth == 15) {
this->ui->bit_depth_15->setChecked(true);
@ -100,246 +47,87 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
connect(this->ui->bit_depth_24, &QRadioButton::toggled, [this](bool checked){ if (checked) this->setBitDepth(24); });
this->setPaletteId(paletteId);
this->commitEditHistory(this->ui->spinBox_PaletteId->value());
this->commitEditHistory();
this->restoreWindowState();
}
PaletteEditor::~PaletteEditor()
{
delete ui;
delete this->hexValidator;
}
void PaletteEditor::updateColorUi(int colorIndex, QRgb rgb) {
setSignalsEnabled(false);
int red = qRed(rgb);
int green = qGreen(rgb);
int blue = qBlue(rgb);
if (this->bitDepth == 15) {
// sliders
this->sliders[colorIndex][0]->setValue(rgb5(red));
this->sliders[colorIndex][1]->setValue(rgb5(green));
this->sliders[colorIndex][2]->setValue(rgb5(blue));
// hex
int hex15 = (rgb5(blue) << 10) | (rgb5(green) << 5) | rgb5(red);
QString hexcode = QString("%1").arg(hex15, 4, 16, QLatin1Char('0')).toUpper();
this->hexEdits[colorIndex]->setText(hexcode);
// spinners
this->spinners[colorIndex][0]->setValue(rgb5(red));
this->spinners[colorIndex][1]->setValue(rgb5(green));
this->spinners[colorIndex][2]->setValue(rgb5(blue));
} else {
// sliders
this->sliders[colorIndex][0]->setValue(red);
this->sliders[colorIndex][1]->setValue(green);
this->sliders[colorIndex][2]->setValue(blue);
// hex
QColor color(red, green, blue);
QString hexcode = color.name().remove(0, 1).toUpper();
this->hexEdits[colorIndex]->setText(hexcode);
// spinners
this->spinners[colorIndex][0]->setValue(red);
this->spinners[colorIndex][1]->setValue(green);
this->spinners[colorIndex][2]->setValue(blue);
}
// frame
QString stylesheet = QString("background-color: rgb(%1, %2, %3);").arg(red).arg(green).arg(blue);
this->frames[colorIndex]->setStyleSheet(stylesheet);
setSignalsEnabled(true);
}
void PaletteEditor::setSignalsEnabled(bool enabled) {
// spinners, sliders, hexbox
for (int i = 0; i < this->sliders.length(); i++) {
this->sliders.at(i).at(0)->blockSignals(!enabled);
this->sliders.at(i).at(1)->blockSignals(!enabled);
this->sliders.at(i).at(2)->blockSignals(!enabled);
}
for (int i = 0; i < this->spinners.length(); i++) {
this->spinners.at(i).at(0)->blockSignals(!enabled);
this->spinners.at(i).at(1)->blockSignals(!enabled);
this->spinners.at(i).at(2)->blockSignals(!enabled);
}
for (int i = 0; i < this->hexEdits.length(); i++) {
this->hexEdits.at(i)->blockSignals(!enabled);
}
Tileset* PaletteEditor::getTileset(int paletteId) {
return (paletteId < Project::getNumPalettesPrimary())
? this->primaryTileset
: this->secondaryTileset;
}
void PaletteEditor::setBitDepth(int bits) {
setSignalsEnabled(false);
switch (bits) {
case 15:
for (int i = 0; i < 16; i++) {
// sliders ranged [0, 31] with 1 single step and 4 page step
this->sliders[i][0]->setSingleStep(1);
this->sliders[i][1]->setSingleStep(1);
this->sliders[i][2]->setSingleStep(1);
this->sliders[i][0]->setPageStep(4);
this->sliders[i][1]->setPageStep(4);
this->sliders[i][2]->setPageStep(4);
this->sliders[i][0]->setMaximum(31);
this->sliders[i][1]->setMaximum(31);
this->sliders[i][2]->setMaximum(31);
// spinners limited [0, 31] with 1 step
this->spinners[i][0]->setSingleStep(1);
this->spinners[i][1]->setSingleStep(1);
this->spinners[i][2]->setSingleStep(1);
this->spinners[i][0]->setMaximum(31);
this->spinners[i][1]->setMaximum(31);
this->spinners[i][2]->setMaximum(31);
// hex box now 4 digits
this->hexEdits[i]->setInputMask("HHHH");
this->hexEdits[i]->setMaxLength(4);
}
break;
case 24:
default:
for (int i = 0; i < 16; i++) {
// sliders ranged [0, 31] with 1 single step and 4 page step
this->sliders[i][0]->setSingleStep(8);
this->sliders[i][1]->setSingleStep(8);
this->sliders[i][2]->setSingleStep(8);
this->sliders[i][0]->setPageStep(24);
this->sliders[i][1]->setPageStep(24);
this->sliders[i][2]->setPageStep(24);
this->sliders[i][0]->setMaximum(255);
this->sliders[i][1]->setMaximum(255);
this->sliders[i][2]->setMaximum(255);
// spinners limited [0, 31] with 1 step
this->spinners[i][0]->setSingleStep(8);
this->spinners[i][1]->setSingleStep(8);
this->spinners[i][2]->setSingleStep(8);
this->spinners[i][0]->setMaximum(255);
this->spinners[i][1]->setMaximum(255);
this->spinners[i][2]->setMaximum(255);
// hex box now 4 digits
this->hexEdits[i]->setInputMask("HHHHHH");
this->hexEdits[i]->setMaxLength(6);
}
break;
}
this->bitDepth = bits;
porymapConfig.paletteEditorBitDepth = bits;
refreshColorUis();
setSignalsEnabled(true);
for (const auto &colorInput : this->colorInputs) {
colorInput->setBitDepth(bits);
}
}
void PaletteEditor::setRgb(int colorIndex, QRgb rgb) {
int paletteNum = this->ui->spinBox_PaletteId->value();
const int paletteId = this->ui->spinBox_PaletteId->value();
Tileset *tileset = paletteNum < Project::getNumPalettesPrimary()
? this->primaryTileset
: this->secondaryTileset;
tileset->palettes[paletteNum][colorIndex] = rgb;
tileset->palettePreviews[paletteNum][colorIndex] = rgb;
Tileset *tileset = getTileset(paletteId);
tileset->palettes[paletteId][colorIndex] = rgb;
tileset->palettePreviews[paletteId][colorIndex] = rgb;
this->updateColorUi(colorIndex, rgb);
this->commitEditHistory(paletteNum);
emit this->changedPaletteColor();
emit changedPaletteColor();
}
void PaletteEditor::setRgbFromSliders(int colorIndex) {
if (this->bitDepth == 15) {
setRgb(colorIndex, qRgb(rgb8(this->sliders[colorIndex][0]->value()),
rgb8(this->sliders[colorIndex][1]->value()),
rgb8(this->sliders[colorIndex][2]->value())));
} else {
setRgb(colorIndex, qRgb(this->sliders[colorIndex][0]->value(),
this->sliders[colorIndex][1]->value(),
this->sliders[colorIndex][2]->value()));
void PaletteEditor::setPalette(int paletteId, const QList<QRgb> &palette) {
Tileset *tileset = getTileset(paletteId);
for (int i = 0; i < this->numColors; i++) {
tileset->palettes[paletteId][i] = palette.at(i);
tileset->palettePreviews[paletteId][i] = palette.at(i);
}
refreshColorInputs();
emit changedPaletteColor();
}
void PaletteEditor::setRgbFromHexEdit(int colorIndex) {
QString text = this->hexEdits[colorIndex]->text();
bool ok = false;
int rgb = text.toInt(&ok, 16);
if (!ok) rgb = 0xFFFFFFFF;
if (this->bitDepth == 15) {
int rc = gbaRed(rgb);
int gc = gbaGreen(rgb);
int bc = gbaBlue(rgb);
setRgb(colorIndex, qRgb(rgb8(rc), rgb8(gc), rgb8(bc)));
} else {
setRgb(colorIndex, qRgb(qRed(rgb), qGreen(rgb), qBlue(rgb)));
}
}
void PaletteEditor::setRgbFromSpinners(int colorIndex) {
if (this->bitDepth == 15) {
setRgb(colorIndex, qRgb(rgb8(this->spinners[colorIndex][0]->value()),
rgb8(this->spinners[colorIndex][1]->value()),
rgb8(this->spinners[colorIndex][2]->value())));
} else {
setRgb(colorIndex, qRgb(this->spinners[colorIndex][0]->value(),
this->spinners[colorIndex][1]->value(),
this->spinners[colorIndex][2]->value()));
}
}
void PaletteEditor::refreshColorUis() {
int paletteNum = this->ui->spinBox_PaletteId->value();
for (int i = 0; i < 16; i++) {
QRgb color;
if (paletteNum < Project::getNumPalettesPrimary()) {
color = this->primaryTileset->palettes.at(paletteNum).at(i);
} else {
color = this->secondaryTileset->palettes.at(paletteNum).at(i);
}
this->updateColorUi(i, color);
void PaletteEditor::refreshColorInputs() {
const int paletteId = ui->spinBox_PaletteId->value();
Tileset *tileset = getTileset(paletteId);
for (int i = 0; i < this->numColors; i++) {
auto colorInput = this->colorInputs.at(i);
const QSignalBlocker b(colorInput);
colorInput->setColor(tileset->palettes.at(paletteId).at(i));
}
}
void PaletteEditor::setPaletteId(int paletteId) {
this->ui->spinBox_PaletteId->blockSignals(true);
const QSignalBlocker b(ui->spinBox_PaletteId);
this->ui->spinBox_PaletteId->setValue(paletteId);
this->refreshColorUis();
this->ui->spinBox_PaletteId->blockSignals(false);
this->refreshColorInputs();
}
void PaletteEditor::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->refreshColorUis();
}
void PaletteEditor::pickColor(int index) {
ColorPicker picker(this);
if (picker.exec() == QDialog::Accepted) {
QColor c = picker.getColor();
this->setRgb(index, c.rgb());
}
return;
this->refreshColorInputs();
}
void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) {
this->refreshColorUis();
this->refreshColorInputs();
if (!this->palettesHistory[paletteId].current()) {
this->commitEditHistory(paletteId);
}
emit this->changedPalette(paletteId);
}
void PaletteEditor::commitEditHistory() {
commitEditHistory(ui->spinBox_PaletteId->value());
}
void PaletteEditor::commitEditHistory(int paletteId) {
QList<QRgb> colors;
for (int i = 0; i < 16; i++) {
colors.append(qRgb(this->spinners[i][0]->value(), this->spinners[i][1]->value(), this->spinners[i][2]->value()));
for (int i = 0; i < this->numColors; i++) {
colors.append(this->colorInputs.at(i)->color());
}
PaletteHistoryItem *commit = new PaletteHistoryItem(colors);
this->palettesHistory[paletteId].push(commit);
@ -356,31 +144,16 @@ void PaletteEditor::on_actionUndo_triggered()
{
int paletteId = this->ui->spinBox_PaletteId->value();
PaletteHistoryItem *prev = this->palettesHistory[paletteId].back();
this->setColorsFromHistory(prev, paletteId);
if (prev)
setPalette(paletteId, prev->colors);
}
void PaletteEditor::on_actionRedo_triggered()
{
int paletteId = this->ui->spinBox_PaletteId->value();
PaletteHistoryItem *next = this->palettesHistory[paletteId].next();
this->setColorsFromHistory(next, paletteId);
}
void PaletteEditor::setColorsFromHistory(PaletteHistoryItem *history, int paletteId) {
if (!history) return;
for (int i = 0; i < 16; i++) {
if (paletteId < Project::getNumPalettesPrimary()) {
this->primaryTileset->palettes[paletteId][i] = history->colors.at(i);
this->primaryTileset->palettePreviews[paletteId][i] = history->colors.at(i);
} else {
this->secondaryTileset->palettes[paletteId][i] = history->colors.at(i);
this->secondaryTileset->palettePreviews[paletteId][i] = history->colors.at(i);
}
}
this->refreshColorUis();
emit this->changedPaletteColor();
if (next)
setPalette(paletteId, next->colors);
}
void PaletteEditor::on_actionImport_Palette_triggered()
@ -407,10 +180,12 @@ void PaletteEditor::on_actionImport_Palette_triggered()
return;
}
if (palette.length() < 16) {
if (palette.length() < this->numColors) {
QMessageBox msgBox(this);
msgBox.setText("Failed to import palette.");
QString message = QString("The palette file has %1 colors, but it must have 16 colors.").arg(palette.length());
QString message = QString("The palette file has %1 colors, but it must have %2 colors.")
.arg(palette.length())
.arg(this->numColors);
msgBox.setInformativeText(message);
msgBox.setDefaultButton(QMessageBox::Ok);
msgBox.setIcon(QMessageBox::Icon::Critical);
@ -418,20 +193,9 @@ void PaletteEditor::on_actionImport_Palette_triggered()
return;
}
int paletteId = this->ui->spinBox_PaletteId->value();
for (int i = 0; i < 16; i++) {
if (paletteId < Project::getNumPalettesPrimary()) {
this->primaryTileset->palettes[paletteId][i] = palette.at(i);
this->primaryTileset->palettePreviews[paletteId][i] = palette.at(i);
} else {
this->secondaryTileset->palettes[paletteId][i] = palette.at(i);
this->secondaryTileset->palettePreviews[paletteId][i] = palette.at(i);
}
}
this->refreshColorUis();
this->commitEditHistory(paletteId);
emit this->changedPaletteColor();
const int paletteId = ui->spinBox_PaletteId->value();
setPalette(paletteId, palette);
commitEditHistory(paletteId);
}
void PaletteEditor::closeEvent(QCloseEvent*) {