Merge branch 'master' into event-paste
This commit is contained in:
commit
c05e03a576
25 changed files with 5826 additions and 1731 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
BIN
docsrc/manual/images/tileset-editor/pe-open-window.png
Normal file
BIN
docsrc/manual/images/tileset-editor/pe-open-window.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 682 KiB |
BIN
docsrc/manual/images/tileset-editor/tse-display-tool.png
Normal file
BIN
docsrc/manual/images/tileset-editor/tse-display-tool.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 714 KiB |
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
BIN
docsrc/manual/images/tileset-editor/tse-metatile-properties.png
Normal file
BIN
docsrc/manual/images/tileset-editor/tse-metatile-properties.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
BIN
docsrc/manual/images/tileset-editor/tse-open-window.png
Normal file
BIN
docsrc/manual/images/tileset-editor/tse-open-window.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 611 KiB |
|
@ -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.
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
.. _rme-ref:
|
||||
|
||||
*********************
|
||||
The Region Map Editor
|
||||
*********************
|
||||
|
|
|
@ -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
|
||||
|
|
163
docsrc/manual/tileset-editor.rst
Normal file
163
docsrc/manual/tileset-editor.rst
Normal 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
219
forms/colorpicker.ui
Normal 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>
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
33
include/ui/colorpicker.h
Normal 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
|
|
@ -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();
|
||||
|
|
|
@ -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 \
|
||||
|
|
128
src/config.cpp
128
src/config.cpp
|
@ -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(",");
|
||||
|
|
|
@ -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
81
src/ui/colorpicker.cpp
Normal 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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue