Merge branch 'master' into event-paste

This commit is contained in:
GriffinR 2022-07-11 22:45:38 -04:00 committed by GitHub
commit c05e03a576
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 5826 additions and 1731 deletions

View file

@ -15,6 +15,8 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Add ability to set the opacity of the scripting overlay.
- Add ability to get/set map header properties and read tile pixel data via the API.
- Add button to copy the full metatile label to the clipboard in the Tileset Editor.
- Add option to not open the most recent project on launch.
- Add color picker to palette editor for taking colors from the screen.
### Changed
- If an object event is inanimate, it will always render using its first frame.
@ -25,6 +27,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Metatiles are always rendered accurately with 3 layers, and the unused layer is not assumed to be transparent.
- `object_event_graphics_info.h` can now be parsed correctly if it uses structs with attributes.
- The selection is no longer reset when pasting events. The newly pasted events are selected instead.
- Palette editor ui is updated a bit to allow hex and rgb value input.
### Fixed
- Fix cursor tile outline not updating at the end of a dragged selection.
@ -33,6 +36,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Fix selected space not updating while painting in Collision view.
- Fix the map music dropdown being empty when importing a map from Advance Map.
- Fix object events added by pasting ignoring the map event limit.
- Fixed a bug where saving the tileset editor would reselect the main editor's first selected metatile.
## [4.5.0] - 2021-12-26
### Added

View file

@ -18,6 +18,7 @@ Porymap Documentation
manual/editing-wild-encounters
manual/creating-new-maps
manual/region-map-editor
manual/tileset-editor
manual/scripting-capabilities
manual/project-files
manual/shortcuts

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 KiB

View file

@ -73,7 +73,8 @@ Wild Pokémon Tab
Tileset Editor
--------------
The Tileset Editor can be opened with *File -> Tileset Editor* (``Ctrl+T``). When the Tileset Editor is opened, it is opened in the context of the currently-opened map. Every map has a primary and secondary tileset, so you will work with a combination of the two whenever you use the Tileset Editor. The left-side pane shows the primary and secondary tilesets' metatiles. The right-side panes allow you to modify the currently-selected metatile.
The Tileset Editor can be opened with *File -> Tileset Editor* (``Ctrl+T``).
Check out :ref:`The Tileset Editor <tse-ref>` section for more details.
.. figure:: images/navigation/tileset-editor.png
:alt: Tileset Editor
@ -83,11 +84,13 @@ The Tileset Editor can be opened with *File -> Tileset Editor* (``Ctrl+T``). Wh
Region Map Editor
-----------------
The Region Map Editor can be opened with *File -> Region Map Editor* (``Ctrl+M``). This window will allow you to modify the look and layout of maps on the game's region map. You can also modify the city map images using the bottom two panes. Currently the Region Map Editor is only available for pokeemerald and pokeruby projects.
The Region Map Editor can be opened with *File -> Region Map Editor* (``Ctrl+M``).
This window will allow you to modify the look and layout of maps on the game's region map.
Check out :ref:`The Region Map Editor <rme-ref>` section for more details.
.. figure:: images/navigation/region-map-editor.png
:alt: Region Map Editor
Region Map Editor
We covered all of the basic views and windows of Porymap above. Next, let's learn how to use Porymap's features to the fullest when editing map tiles.
We covered all of the basic views and windows of porymap above. Next, let's learn how to use Porymap's features to the fullest when editing map tiles.

View file

@ -1,3 +1,5 @@
.. _rme-ref:
*********************
The Region Map Editor
*********************

View file

@ -17,6 +17,7 @@ determined by this file.
:widths: 10, 3, 5, 5, 20
``recent_project``, , global, yes, The project that will be opened on launch
``reopen_on_launch``, 1, global, yes, Whether the most recent project should be opened on launch
``recent_map``, , global, yes, The map that will be opened on launch
``pretty_cursors``, 1, global, yes, Whether to use custom crosshair cursors
``map_sort_order``, group, global, yes, The order map list is sorted in

View file

@ -0,0 +1,163 @@
.. _tse-ref:
*********************
The Tileset Editor
*********************
Here, you can edit individual tilesets.
When the Tileset Editor is opened, it is opened in the context of the
currently-opened map. Every map has a primary and secondary tileset, so you
will work with a combination of the two whenever you use the Tileset Editor.
The left-side pane shows the primary and secondary tilesets' metatiles.
The right-side panes allow you to modify the currently-selected metatile.
.. figure:: images/tileset-editor/tse-open-window.png
:align: center
:width: 75%
:alt: TSE Window
Tileset Editor Window
Metatile Properties
===================
.. figure:: images/tileset-editor/tse-metatile-properties.png
:align: center
:width: 30%
:alt: MP Frame
Metatile Properties Panel
Layer Type
----------
Defines the background layers the metatiles will use for drawing.
The options are:
**Normal** --- Metatile uses middle and top bg layers
**Covered** --- Metatile uses bottom and middle bg layers
**Split** --- Metatile uses bottom and top bg layers
Metatile Behavior
-----------------
Defines the metatile behavior associated with this metatile. This can be used
for a variety of different reasons. For example, warps, ice, and tall grass effects
are all determined by a metatile's behavior.
This dropdown is populated with constants found in ``include/constants/metatile_behaviors.h``.
Encounter Type
--------------
*pokefirered exclusive*
Used to determine which category of wild encounter to attempt.
Terrain Type
------------
*pokefirered exclusive*
Used to determine certain attributes of metatiles. Can be useful in certain scenarios.
For example, to determine if the player is facing water or standing in grass.
Metatile Label
--------------
*optional*
A name can be given to metatiles so that they may be referenced in source code.
These are defined in ``include/constants/metatile_labels.h`` and can be used in
together with the ``METATILE_ID`` macro.
For example, the metatile pictured above can be accessed like
``METATILE_ID(General, Plain_Grass)``.
Tools Menu
==========
The tileset editor provides users with several useful tools for making edits
easier and more convenient.
Import Tiles Image...
---------------------
Tool to automatically import a new tile image for a tileset.
The tile image is an indexed png of 8x8 pixel tiles, which are used to form
metatiles in the tileset editor.
Import Metatiles from Advance Map 1.92...
-----------------------------------------
Helpful for users converting projects from binary hacks.
Metatile data exported from Advance Map 1.92 in a ``.bvd``` file can be imported
into porymap's tileset editor.
This saves a lot of time since metatiles will not have to be defined from scratch.
Change Number of Metatiles
--------------------------
The number of metatiles in both the current primary and current secondary tileset
can be adjusted within the limits.
.. note::
You may need to change the Makefile rules for the number of tiles allowed
for the tileset in the file ``graphics_file_rules.mk``. You can simply
remove the ``-num_tiles=`` argument altogether.
Other Tools
-----------
.. figure:: images/tileset-editor/tse-display-tool.png
:align: center
:width: 60%
:alt: TSE Unused
Displaying Unused Tiles
There are also tools to count the number of metatile and tile usages across the
entire project, which can be useful, for example, in determining whether a
metatile can be deleted. The output of these operations is pictured above.
Palette Editor
==============
The palette editor is where the ``.pal`` files are modified for each tileset.
.. figure:: images/tileset-editor/pe-open-window.png
:align: center
:width: 75%
:alt: PE
Palette Editor
The current palette is indicated by the spinner at the top left. To switch
between palettes, just change the spinner value.
At the top right is a setting for the bit depth at which colors are displayed.
The colors in a palette file are displayed as 24 bit numbers, but the GBA
hardware only allows 15 bit colors, so displaying in 15 bits can be more realistic.
Each individual color can be adjusted with either the sliders or the spinners,
in addition to the hex value box. Each color also has an eyedropper toolbutton
which allows users to pick any color from the screen and add it to the palette.
Entire palettes can also be imported from a variety of formats,
including JASC, Adobe Color Table, Tile Layer Pro, and Advance PE.
Each imported palette must contain 16 colors.

219
forms/colorpicker.ui Normal file
View file

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ColorPicker</class>
<widget class="QDialog" name="ColorPicker">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>383</width>
<height>243</height>
</rect>
</property>
<property name="windowTitle">
<string>Color Picker</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="frame_4">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="viewport">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>120</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>120</width>
<height>120</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<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="QFrame" name="frame_centralColor">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="baseSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<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>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_RGB">
<property name="font">
<font>
<family>Courier</family>
</font>
</property>
<property name="text">
<string>RGB (000, 000, 000)</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_HEX">
<property name="font">
<font>
<family>Courier</family>
</font>
</property>
<property name="text">
<string>#FFFFFF</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_SPACE">
<property name="font">
<font>
<family>Courier</family>
</font>
</property>
<property name="text">
<string>press [SPACE] to capture color</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -2661,6 +2661,7 @@
<addaction name="actionUse_Encounter_Json"/>
<addaction name="actionMonitor_Project_Files"/>
<addaction name="actionUse_Poryscript"/>
<addaction name="actionOpen_Recent_Project_On_Launch"/>
<addaction name="separator"/>
<addaction name="actionEdit_Preferences"/>
<addaction name="actionEdit_Shortcuts"/>
@ -2733,6 +2734,14 @@
<string>Use Poryscript</string>
</property>
</action>
<action name="actionOpen_Recent_Project_On_Launch">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Open Recent Project On Launch</string>
</property>
</action>
<action name="action_NewMap">
<property name="text">
<string>New Map...</string>

File diff suppressed because it is too large Load diff

View file

@ -28,6 +28,7 @@ protected:
virtual QMap<QString, QString> getKeyValueMap() = 0;
virtual void onNewConfigFileCreated() = 0;
virtual void setUnreadKeys() = 0;
void setConfigBool(QString key, bool * field, QString value);
};
class PorymapConfig: public KeyValueConfigBase
@ -38,6 +39,7 @@ public:
}
virtual void reset() override {
this->recentProject = "";
this->reopenOnLaunch = true;
this->mapSortOrder = MapSortOrder::Group;
this->prettyCursors = true;
this->collisionOpacity = 50;
@ -53,6 +55,7 @@ public:
this->textEditorGotoLine = "";
}
void setRecentProject(QString project);
void setReopenOnLaunch(bool enabled);
void setMapSortOrder(MapSortOrder order);
void setPrettyCursors(bool enabled);
void setMainGeometry(QByteArray, QByteArray, QByteArray, QByteArray);
@ -71,6 +74,7 @@ public:
void setTextEditorOpenFolder(const QString &command);
void setTextEditorGotoLine(const QString &command);
QString getRecentProject();
bool getReopenOnLaunch();
MapSortOrder getMapSortOrder();
bool getPrettyCursors();
QMap<QString, QByteArray> getMainGeometry();
@ -96,6 +100,7 @@ protected:
virtual void setUnreadKeys() override {};
private:
QString recentProject;
bool reopenOnLaunch;
QString stringFromByteArray(QByteArray);
QByteArray bytesFromString(QString);
MapSortOrder mapSortOrder;

View file

@ -8,6 +8,13 @@ template <typename T>
class History {
public:
History() { }
~History() {
while (!history.isEmpty()) {
delete history.takeLast();
}
}
T back() {
if (head > 0) {
return history.at(--head);

View file

@ -250,6 +250,7 @@ private slots:
void on_actionUse_Encounter_Json_triggered(bool checked);
void on_actionMonitor_Project_Files_triggered(bool checked);
void on_actionUse_Poryscript_triggered(bool checked);
void on_actionOpen_Recent_Project_On_Launch_triggered(bool checked);
void on_actionEdit_Shortcuts_triggered();
void on_mainTabBar_tabBarClicked(int index);

33
include/ui/colorpicker.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef COLORPICKER_H
#define COLORPICKER_H
#include <QDialog>
#include <QGraphicsScene>
namespace Ui {
class ColorPicker;
}
class ColorPicker : public QDialog
{
Q_OBJECT
public:
explicit ColorPicker(QWidget *parent = nullptr);
~ColorPicker();
QColor getColor() { return this->color; }
private:
Ui::ColorPicker *ui;
QGraphicsScene *scene = nullptr;
QTimer *timer = nullptr;
QPoint lastCursorPos = QPoint(0, 0);
QColor color = Qt::white;
void hover(int mouseX, int mouseY);
};
#endif // COLORPICKER_H

View file

@ -2,6 +2,7 @@
#define PALETTEEDITOR_H
#include <QMainWindow>
#include <QValidator>
#include <QSlider>
#include <QFrame>
#include <QLabel>
@ -31,23 +32,43 @@ public:
private:
Ui::PaletteEditor *ui;
Project *project = nullptr;
QList<QList<QSlider*>> sliders;
QList<QList<QSpinBox *>> spinners;
QList<QFrame*> frames;
QList<QLabel*> rgbLabels;
QList<QToolButton *> pickButtons;
QList<QLineEdit *> hexEdits;
Tileset *primaryTileset;
Tileset *secondaryTileset;
QList<History<PaletteHistoryItem*>> palettesHistory;
void disableSliderSignals();
void enableSliderSignals();
void initColorSliders();
void refreshColorSliders();
void refreshColors();
void refreshColor(int);
void setColor(int);
void refreshColorUis();
void updateColorUi(int index, QRgb color);
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 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;
signals:
void closed();

View file

@ -82,6 +82,7 @@ SOURCES += src/core/block.cpp \
src/ui/multikeyedit.cpp \
src/ui/preferenceeditor.cpp \
src/ui/regionmappropertiesdialog.cpp \
src/ui/colorpicker.cpp \
src/config.cpp \
src/editor.cpp \
src/main.cpp \
@ -164,6 +165,7 @@ HEADERS += include/core/block.h \
include/ui/multikeyedit.h \
include/ui/preferenceeditor.h \
include/ui/regionmappropertiesdialog.h \
include/ui/colorpicker.h \
include/config.h \
include/editor.h \
include/mainwindow.h \
@ -183,7 +185,8 @@ FORMS += forms/mainwindow.ui \
forms/mapimageexporter.ui \
forms/shortcutseditor.ui \
forms/preferenceeditor.ui \
forms/regionmappropertiesdialog.ui
forms/regionmappropertiesdialog.ui \
forms/colorpicker.ui
RESOURCES += \
resources/images.qrc \

View file

@ -79,6 +79,14 @@ void KeyValueConfigBase::save() {
}
}
void KeyValueConfigBase::setConfigBool(QString key, bool * field, QString value) {
bool ok;
*field = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for %1: '%2'. Must be 0 or 1.").arg(key).arg(value));
}
}
const QMap<MapSortOrder, QString> mapSortOrderMap = {
{MapSortOrder::Group, "group"},
{MapSortOrder::Layout, "layout"},
@ -108,12 +116,10 @@ QString PorymapConfig::getConfigFilepath() {
void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
if (key == "recent_project") {
this->recentProject = value;
} else if (key == "reopen_on_launch") {
setConfigBool(key, &this->reopenOnLaunch, value);
} else if (key == "pretty_cursors") {
bool ok;
this->prettyCursors = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for pretty_cursors: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->prettyCursors, value);
} else if (key == "map_sort_order") {
QString sortOrder = value.toLower();
if (mapSortOrderReverseMap.contains(sortOrder)) {
@ -157,35 +163,15 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
this->metatilesZoom = 30;
}
} else if (key == "show_player_view") {
bool ok;
this->showPlayerView = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for show_player_view: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->showPlayerView, value);
} else if (key == "show_cursor_tile") {
bool ok;
this->showCursorTile = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for show_cursor_tile: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->showCursorTile, value);
} else if (key == "show_border") {
bool ok;
this->showBorder = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for show_border: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->showBorder, value);
} else if (key == "show_grid") {
bool ok;
this->showGrid = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for show_grid: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->showGrid, value);
} else if (key == "monitor_files") {
bool ok;
this->monitorFiles = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for monitor_files: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->monitorFiles, value);
} else if (key == "region_map_dimensions") {
bool ok1, ok2;
QStringList dims = value.split("x");
@ -211,6 +197,7 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
QMap<QString, QString> PorymapConfig::getKeyValueMap() {
QMap<QString, QString> map;
map.insert("recent_project", this->recentProject);
map.insert("reopen_on_launch", this->reopenOnLaunch ? "1" : "0");
map.insert("pretty_cursors", this->prettyCursors ? "1" : "0");
map.insert("map_sort_order", mapSortOrderMap.value(this->mapSortOrder));
map.insert("main_window_geometry", stringFromByteArray(this->mainWindowGeometry));
@ -260,6 +247,11 @@ void PorymapConfig::setRecentProject(QString project) {
this->save();
}
void PorymapConfig::setReopenOnLaunch(bool enabled) {
this->reopenOnLaunch = enabled;
this->save();
}
void PorymapConfig::setMapSortOrder(MapSortOrder order) {
this->mapSortOrder = order;
this->save();
@ -354,6 +346,10 @@ QString PorymapConfig::getRecentProject() {
return this->recentProject;
}
bool PorymapConfig::getReopenOnLaunch() {
return this->reopenOnLaunch;
}
MapSortOrder PorymapConfig::getMapSortOrder() {
return this->mapSortOrder;
}
@ -475,77 +471,29 @@ void ProjectConfig::parseConfigKeyValue(QString key, QString value) {
} else if (key == "recent_map") {
this->recentMap = value;
} else if (key == "use_encounter_json") {
bool ok;
this->useEncounterJson = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for use_encounter_json: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->useEncounterJson, value);
} else if (key == "use_poryscript") {
bool ok;
this->usePoryScript = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for use_poryscript: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->usePoryScript, value);
} else if (key == "use_custom_border_size") {
bool ok;
this->useCustomBorderSize = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for use_custom_border_size: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->useCustomBorderSize, value);
} else if (key == "enable_event_weather_trigger") {
bool ok;
this->enableEventWeatherTrigger = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for enable_event_weather_trigger: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->enableEventWeatherTrigger, value);
} else if (key == "enable_event_secret_base") {
bool ok;
this->enableEventSecretBase = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for enable_event_secret_base: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->enableEventSecretBase, value);
} else if (key == "enable_hidden_item_quantity") {
bool ok;
this->enableHiddenItemQuantity = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for enable_hidden_item_quantity: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->enableHiddenItemQuantity, value);
} else if (key == "enable_hidden_item_requires_itemfinder") {
bool ok;
this->enableHiddenItemRequiresItemfinder = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for enable_hidden_item_requires_itemfinder: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->enableHiddenItemRequiresItemfinder, value);
} else if (key == "enable_heal_location_respawn_data") {
bool ok;
this->enableHealLocationRespawnData = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for enable_heal_location_respawn_data: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->enableHealLocationRespawnData, value);
} else if (key == "enable_event_clone_object") {
bool ok;
this->enableEventCloneObject = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for enable_event_clone_object: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->enableEventCloneObject, value);
} else if (key == "enable_floor_number") {
bool ok;
this->enableFloorNumber = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for enable_floor_number: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->enableFloorNumber, value);
} else if (key == "create_map_text_file") {
bool ok;
this->createMapTextFile = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for create_map_text_file: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->createMapTextFile, value);
} else if (key == "enable_triple_layer_metatiles") {
bool ok;
this->enableTripleLayerMetatiles = value.toInt(&ok);
if (!ok) {
logWarn(QString("Invalid config value for enable_triple_layer_metatiles: '%1'. Must be 0 or 1.").arg(value));
}
setConfigBool(key, &this->enableTripleLayerMetatiles, value);
} else if (key == "custom_scripts") {
this->customScripts.clear();
QList<QString> paths = value.split(",");

View file

@ -461,6 +461,7 @@ void MainWindow::loadUserSettings() {
ui->horizontalSlider_MetatileZoom->setValue(porymapConfig.getMetatilesZoom());
ui->horizontalSlider_MetatileZoom->blockSignals(false);
ui->actionMonitor_Project_Files->setChecked(porymapConfig.getMonitorFiles());
ui->actionOpen_Recent_Project_On_Launch->setChecked(porymapConfig.getReopenOnLaunch());
setTheme(porymapConfig.getTheme());
}
@ -485,6 +486,9 @@ void MainWindow::setTheme(QString theme) {
}
bool MainWindow::openRecentProject() {
if (!porymapConfig.getReopenOnLaunch())
return false;
QString default_dir = porymapConfig.getRecentProject();
if (!default_dir.isNull() && default_dir.length() > 0) {
logInfo(QString("Opening recent project: '%1'").arg(default_dir));
@ -1774,6 +1778,11 @@ void MainWindow::on_actionUse_Poryscript_triggered(bool checked)
projectConfig.setUsePoryScript(checked);
}
void MainWindow::on_actionOpen_Recent_Project_On_Launch_triggered(bool checked)
{
porymapConfig.setReopenOnLaunch(checked);
}
void MainWindow::on_actionEdit_Shortcuts_triggered()
{
if (!shortcutsEditor)

81
src/ui/colorpicker.cpp Normal file
View file

@ -0,0 +1,81 @@
#include "colorpicker.h"
#include "ui_colorpicker.h"
#include <QtWidgets>
const int zoom_box_dimensions = 15;
ColorPicker::ColorPicker(QWidget *parent) :
QDialog(parent),
ui(new Ui::ColorPicker)
{
ui->setupUi(this);
this->scene = new QGraphicsScene;
// listen for spacebar press to take color
QShortcut *takeColor = new QShortcut(Qt::Key_Space, this);
QObject::connect(takeColor, &QShortcut::activated, [this](){
timer->stop();
this->accept();
});
// need to set up a timer because there is no good way to get global mouse movement
// outside of the application in a cross-platform way
timer = new QTimer(this);
connect(timer, &QTimer::timeout, [this]() {
QPoint cursorPos = QCursor::pos();
if (lastCursorPos != cursorPos) {
lastCursorPos = cursorPos;
this->hover(cursorPos.x(), cursorPos.y());
}
});
timer->start(10);
}
ColorPicker::~ColorPicker()
{
delete scene;
delete timer;
delete ui;
}
void ColorPicker::hover(int mouseX, int mouseY) {
QScreen *screen = QGuiApplication::primaryScreen();
if (const QWindow *window = windowHandle())
screen = window->screen();
if (!screen)
return;
// 15 X 15 box with 8x magnification = 120px square)
QRect zoomRect(mouseX - zoom_box_dimensions / 2, mouseY - zoom_box_dimensions / 2, zoom_box_dimensions, zoom_box_dimensions);
QPixmap grab = screen->grabWindow(0, mouseX - zoom_box_dimensions / 2, mouseY - zoom_box_dimensions / 2, zoom_box_dimensions, zoom_box_dimensions);
int pixelRatio = grab.devicePixelRatio();
// TODO: investigate for high dpi displays why text is too high res
grab.setDevicePixelRatio(1);
QPixmap magnified = grab.scaled(zoom_box_dimensions * 8, zoom_box_dimensions * 8, Qt::KeepAspectRatio);
QPainter painter(&magnified);
painter.setRenderHint(QPainter::Antialiasing, false);
QRectF rect(zoom_box_dimensions / 2 * 8 - 1, zoom_box_dimensions / 2 * 8 - 1, zoom_box_dimensions / 2 + 2, zoom_box_dimensions / 2 + 2);
painter.drawRect(rect);
painter.end();
// TODO: bounds checking?
this->color = grab.toImage().pixelColor(zoom_box_dimensions / 2 * pixelRatio, zoom_box_dimensions / 2 * pixelRatio);
int r = this->color.red();
int g = this->color.green();
int b = this->color.blue();
// update the displayed color value
QString rgb = QString("rgb(%1, %2, %3)").arg(r).arg(g).arg(b);
QString stylesheet = QString("background-color: %1;").arg(rgb);
this->ui->frame_centralColor->setStyleSheet(stylesheet);
this->ui->label_RGB->setText(rgb);
this->ui->label_HEX->setText(color.name());
this->ui->viewport->setPixmap(magnified);
}

View file

@ -1,11 +1,20 @@
#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),
ui(new Ui::PaletteEditor)
@ -16,101 +25,74 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
this->ui->setupUi(this);
this->ui->spinBox_PaletteId->setMinimum(0);
this->ui->spinBox_PaletteId->setMaximum(Project::getNumPalettesTotal() - 1);
this->sliders.clear();
for (int i = 0; i < 16; i++) {
this->sliders.append(QList<QSlider*>());
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->sliders[0].append(this->ui->horizontalSlider);
this->sliders[0].append(this->ui->horizontalSlider_2);
this->sliders[0].append(this->ui->horizontalSlider_3);
this->sliders[1].append(this->ui->horizontalSlider_4);
this->sliders[1].append(this->ui->horizontalSlider_5);
this->sliders[1].append(this->ui->horizontalSlider_6);
this->sliders[2].append(this->ui->horizontalSlider_7);
this->sliders[2].append(this->ui->horizontalSlider_8);
this->sliders[2].append(this->ui->horizontalSlider_9);
this->sliders[3].append(this->ui->horizontalSlider_10);
this->sliders[3].append(this->ui->horizontalSlider_11);
this->sliders[3].append(this->ui->horizontalSlider_12);
this->sliders[4].append(this->ui->horizontalSlider_13);
this->sliders[4].append(this->ui->horizontalSlider_14);
this->sliders[4].append(this->ui->horizontalSlider_15);
this->sliders[5].append(this->ui->horizontalSlider_16);
this->sliders[5].append(this->ui->horizontalSlider_17);
this->sliders[5].append(this->ui->horizontalSlider_18);
this->sliders[6].append(this->ui->horizontalSlider_19);
this->sliders[6].append(this->ui->horizontalSlider_20);
this->sliders[6].append(this->ui->horizontalSlider_21);
this->sliders[7].append(this->ui->horizontalSlider_22);
this->sliders[7].append(this->ui->horizontalSlider_23);
this->sliders[7].append(this->ui->horizontalSlider_24);
this->sliders[8].append(this->ui->horizontalSlider_25);
this->sliders[8].append(this->ui->horizontalSlider_26);
this->sliders[8].append(this->ui->horizontalSlider_27);
this->sliders[9].append(this->ui->horizontalSlider_28);
this->sliders[9].append(this->ui->horizontalSlider_29);
this->sliders[9].append(this->ui->horizontalSlider_30);
this->sliders[10].append(this->ui->horizontalSlider_31);
this->sliders[10].append(this->ui->horizontalSlider_32);
this->sliders[10].append(this->ui->horizontalSlider_33);
this->sliders[11].append(this->ui->horizontalSlider_34);
this->sliders[11].append(this->ui->horizontalSlider_35);
this->sliders[11].append(this->ui->horizontalSlider_36);
this->sliders[12].append(this->ui->horizontalSlider_37);
this->sliders[12].append(this->ui->horizontalSlider_38);
this->sliders[12].append(this->ui->horizontalSlider_39);
this->sliders[13].append(this->ui->horizontalSlider_40);
this->sliders[13].append(this->ui->horizontalSlider_41);
this->sliders[13].append(this->ui->horizontalSlider_42);
this->sliders[14].append(this->ui->horizontalSlider_43);
this->sliders[14].append(this->ui->horizontalSlider_44);
this->sliders[14].append(this->ui->horizontalSlider_45);
this->sliders[15].append(this->ui->horizontalSlider_46);
this->sliders[15].append(this->ui->horizontalSlider_47);
this->sliders[15].append(this->ui->horizontalSlider_48);
this->frames.clear();
this->frames.append(this->ui->frame);
this->frames.append(this->ui->frame_2);
this->frames.append(this->ui->frame_3);
this->frames.append(this->ui->frame_4);
this->frames.append(this->ui->frame_5);
this->frames.append(this->ui->frame_6);
this->frames.append(this->ui->frame_7);
this->frames.append(this->ui->frame_8);
this->frames.append(this->ui->frame_9);
this->frames.append(this->ui->frame_10);
this->frames.append(this->ui->frame_11);
this->frames.append(this->ui->frame_12);
this->frames.append(this->ui->frame_13);
this->frames.append(this->ui->frame_14);
this->frames.append(this->ui->frame_15);
this->frames.append(this->ui->frame_16);
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->rgbLabels.clear();
this->rgbLabels.append(this->ui->label_rgb0);
this->rgbLabels.append(this->ui->label_rgb1);
this->rgbLabels.append(this->ui->label_rgb2);
this->rgbLabels.append(this->ui->label_rgb3);
this->rgbLabels.append(this->ui->label_rgb4);
this->rgbLabels.append(this->ui->label_rgb5);
this->rgbLabels.append(this->ui->label_rgb6);
this->rgbLabels.append(this->ui->label_rgb7);
this->rgbLabels.append(this->ui->label_rgb8);
this->rgbLabels.append(this->ui->label_rgb9);
this->rgbLabels.append(this->ui->label_rgb10);
this->rgbLabels.append(this->ui->label_rgb11);
this->rgbLabels.append(this->ui->label_rgb12);
this->rgbLabels.append(this->ui->label_rgb13);
this->rgbLabels.append(this->ui->label_rgb14);
this->rgbLabels.append(this->ui->label_rgb15);
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);
});
}
// Setup edit-undo history for each of the palettes.
for (int i = 0; i < Project::getNumPalettesTotal(); i++) {
this->palettesHistory.append(History<PaletteHistoryItem*>());
}
this->initColorSliders();
// 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); });
}
setBitDepth(24);
// Connect bit depth buttons
connect(this->ui->bit_depth_15, &QRadioButton::toggled, [this](bool checked){ if (checked) this->setBitDepth(15); });
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->restoreWindowState();
@ -119,34 +101,194 @@ PaletteEditor::PaletteEditor(Project *project, Tileset *primaryTileset, Tileset
PaletteEditor::~PaletteEditor()
{
delete ui;
delete this->hexValidator;
}
void PaletteEditor::disableSliderSignals() {
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(true);
this->sliders.at(i).at(1)->blockSignals(true);
this->sliders.at(i).at(2)->blockSignals(true);
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);
}
}
void PaletteEditor::enableSliderSignals() {
for (int i = 0; i < this->sliders.length(); i++) {
this->sliders.at(i).at(0)->blockSignals(false);
this->sliders.at(i).at(1)->blockSignals(false);
this->sliders.at(i).at(2)->blockSignals(false);
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;
refreshColorUis();
setSignalsEnabled(true);
}
void PaletteEditor::setRgb(int colorIndex, QRgb rgb) {
int paletteNum = this->ui->spinBox_PaletteId->value();
Tileset *tileset = paletteNum < Project::getNumPalettesPrimary()
? this->primaryTileset
: this->secondaryTileset;
tileset->palettes[paletteNum][colorIndex] = rgb;
tileset->palettePreviews[paletteNum][colorIndex] = rgb;
this->updateColorUi(colorIndex, rgb);
this->commitEditHistory(paletteNum);
emit this->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::initColorSliders() {
for (int i = 0; i < 16; i++) {
connect(this->sliders[i][0], &QSlider::valueChanged, [=](int) { this->setColor(i); });
connect(this->sliders[i][1], &QSlider::valueChanged, [=](int) { this->setColor(i); });
connect(this->sliders[i][2], &QSlider::valueChanged, [=](int) { this->setColor(i); });
void PaletteEditor::setRgbFromHexEdit(int colorIndex) {
QString text = this->hexEdits[colorIndex]->text();
bool ok = false;
if (this->bitDepth == 15) {
int rgb15 = text.toInt(&ok, 16);
int rc = gbaRed(rgb15);
int gc = gbaGreen(rgb15);
int bc = gbaBlue(rgb15);
QRgb rgb = qRgb(rc, gc, bc);
if (!ok) rgb = 0xFFFFFFFF;
setRgb(colorIndex, rgb);
} else {
QRgb rgb = text.toInt(&ok, 16);
if (!ok) rgb = 0xFFFFFFFF;
setRgb(colorIndex, rgb);
}
}
void PaletteEditor::refreshColorSliders() {
disableSliderSignals();
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;
@ -156,64 +298,34 @@ void PaletteEditor::refreshColorSliders() {
color = this->secondaryTileset->palettes.at(paletteNum).at(i);
}
this->sliders[i][0]->setValue(qRed(color) / 8);
this->sliders[i][1]->setValue(qGreen(color) / 8);
this->sliders[i][2]->setValue(qBlue(color) / 8);
this->updateColorUi(i, color);
}
enableSliderSignals();
}
void PaletteEditor::refreshColors() {
for (int i = 0; i < 16; i++) {
this->refreshColor(i);
}
}
void PaletteEditor::refreshColor(int colorIndex) {
int red = this->sliders[colorIndex][0]->value() * 8;
int green = this->sliders[colorIndex][1]->value() * 8;
int blue = this->sliders[colorIndex][2]->value() * 8;
QString stylesheet = QString("background-color: rgb(%1, %2, %3);")
.arg(red)
.arg(green)
.arg(blue);
this->frames[colorIndex]->setStyleSheet(stylesheet);
this->rgbLabels[colorIndex]->setText(QString("RGB(%1, %2, %3)").arg(red).arg(green).arg(blue));
}
void PaletteEditor::setPaletteId(int paletteId) {
this->ui->spinBox_PaletteId->blockSignals(true);
this->ui->spinBox_PaletteId->setValue(paletteId);
this->refreshColorSliders();
this->refreshColors();
this->refreshColorUis();
this->ui->spinBox_PaletteId->blockSignals(false);
}
void PaletteEditor::setTilesets(Tileset *primaryTileset, Tileset *secondaryTileset) {
this->primaryTileset = primaryTileset;
this->secondaryTileset = secondaryTileset;
this->refreshColorSliders();
this->refreshColors();
this->refreshColorUis();
}
void PaletteEditor::setColor(int colorIndex) {
int paletteNum = this->ui->spinBox_PaletteId->value();
int red = this->sliders[colorIndex][0]->value() * 8;
int green = this->sliders[colorIndex][1]->value() * 8;
int blue = this->sliders[colorIndex][2]->value() * 8;
Tileset *tileset = paletteNum < Project::getNumPalettesPrimary()
? this->primaryTileset
: this->secondaryTileset;
tileset->palettes[paletteNum][colorIndex] = qRgb(red, green, blue);
tileset->palettePreviews[paletteNum][colorIndex] = qRgb(red, green, blue);
this->refreshColor(colorIndex);
this->commitEditHistory(paletteNum);
emit this->changedPaletteColor();
void PaletteEditor::pickColor(int index) {
ColorPicker picker(this);
if (picker.exec() == QDialog::Accepted) {
QColor c = picker.getColor();
this->setRgb(index, c.rgb());
}
return;
}
void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) {
this->refreshColorSliders();
this->refreshColors();
this->refreshColorUis();
if (!this->palettesHistory[paletteId].current()) {
this->commitEditHistory(paletteId);
}
@ -223,7 +335,7 @@ void PaletteEditor::on_spinBox_PaletteId_valueChanged(int paletteId) {
void PaletteEditor::commitEditHistory(int paletteId) {
QList<QRgb> colors;
for (int i = 0; i < 16; i++) {
colors.append(qRgb(this->sliders[i][0]->value() * 8, this->sliders[i][1]->value() * 8, this->sliders[i][2]->value() * 8));
colors.append(qRgb(this->spinners[i][0]->value(), this->spinners[i][1]->value(), this->spinners[i][2]->value()));
}
PaletteHistoryItem *commit = new PaletteHistoryItem(colors);
this->palettesHistory[paletteId].push(commit);
@ -263,8 +375,7 @@ void PaletteEditor::setColorsFromHistory(PaletteHistoryItem *history, int palett
}
}
this->refreshColorSliders();
this->refreshColors();
this->refreshColorUis();
emit this->changedPaletteColor();
}
@ -314,8 +425,7 @@ void PaletteEditor::on_actionImport_Palette_triggered()
}
}
this->refreshColorSliders();
this->refreshColors();
this->refreshColorUis();
this->commitEditHistory(paletteId);
emit this->changedPaletteColor();
}

View file

@ -556,10 +556,15 @@ void TilesetEditor::on_comboBox_terrainType_activated(int terrainType)
void TilesetEditor::on_actionSave_Tileset_triggered()
{
// need this temporary metatile ID to reset selection after saving
// when the tilesetsSaved signal is sent, it will be reset to the current map metatile
uint16_t reselectMetatileID = this->metatileSelector->getSelectedMetatileId();
saveMetatileLabel();
this->project->saveTilesets(this->primaryTileset, this->secondaryTileset);
emit this->tilesetsSaved(this->primaryTileset->name, this->secondaryTileset->name);
this->metatileSelector->select(reselectMetatileID);
if (this->paletteEditor) {
this->paletteEditor->setTilesets(this->primaryTileset, this->secondaryTileset);
}