Merge branch 'master' of https://github.com/huderlem/porymap into slam

This commit is contained in:
GriffinR 2024-10-16 22:29:03 -04:00
commit b5c50e7660
39 changed files with 2206 additions and 5929 deletions

View file

@ -11,6 +11,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Redesigned the Connections tab, adding a number of new features including the option to open or display diving maps and a list UI for easier edit access.
- Add a `Close Project` option
- Add charts to the `Wild Pokémon` tab that show species and level distributions.
- Add options for customizing the map grid under `View -> Grid Settings`.
- An alert will be displayed when attempting to open a seemingly invalid project.
- Add support for defining project values with `enum` where `#define` was expected.
@ -22,6 +23,7 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- The max encounter rate is now read from the project, rather than assuming the default value from RSE.
- It's now possible to cancel quitting if there are unsaved changes in sub-windows.
- The triple-layer metatiles setting can now be set automatically using a project constant.
- `Export Map Stitch Image` now shows a preview of the full image, not just the current map.
### Fixed
- Fix `Add Region Map...` not updating the region map settings file.
@ -46,6 +48,10 @@ The **"Breaking Changes"** listed below are changes that have been made in the d
- Fix the map list mishandling value gaps when sorting by Area.
- Fix a freeze on startup if project values are defined with mismatched parentheses.
- Fix stitched map images sometimes rendering garbage
- Fix the `Reset` button on `Export Map Timelapse Image` not resetting the Timelapse settings.
- Stop sliders in the Palette Editor from creating a bunch of edit history when used.
- Fix scrolling on some containers locking up when the mouse stops over a spin box or combo box.
- Fix some file dialogs returning to an incorrect window when closed.
## [5.4.1] - 2024-03-21
### Fixed

310
forms/colorinputwidget.ui Normal file
View file

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

275
forms/gridsettingsdialog.ui Normal file
View file

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

View file

@ -2939,12 +2939,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
@ -3459,10 +3453,13 @@
</property>
<addaction name="actionZoom_In"/>
<addaction name="actionZoom_Out"/>
<addaction name="separator"/>
<addaction name="actionCursor_Tile_Outline"/>
<addaction name="actionPlayer_View_Rectangle"/>
<addaction name="actionBetter_Cursors"/>
<addaction name="actionDive_Emerge_Map"/>
<addaction name="actionShow_Grid"/>
<addaction name="actionGrid_Settings"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
@ -3694,7 +3691,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>Player View Rectangle</string>
<string>Show Player View Rectangle</string>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show the player's view rectangle on the map based on the cursor's position.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -3711,7 +3708,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>Cursor Tile Outline</string>
<string>Show Cursor Tile Outline</string>
</property>
<property name="shortcut">
<string>C</string>
@ -3810,7 +3807,26 @@
<bool>true</bool>
</property>
<property name="text">
<string>Dive/Emerge Map</string>
<string>Show Dive/Emerge Map</string>
</property>
</action>
<action name="actionShow_Grid">
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<property name="text">
<string>Show Grid</string>
</property>
<property name="shortcut">
<string>Ctrl+G</string>
</property>
</action>
<action name="actionGrid_Settings">
<property name="text">
<string>Grid Settings...</string>
</property>
</action>
</widget>

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>696</width>
<height>396</height>
<width>817</width>
<height>535</height>
</rect>
</property>
<property name="windowTitle">
@ -16,15 +16,313 @@
<property name="sizeGripEnabled">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QGridLayout" name="gridLayout_4" columnstretch="2,1">
<item row="1" column="1">
<widget class="QFrame" name="frame_Options">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_Options">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Map</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="NoScrollComboBox" name="comboBox_MapSelection">
<property name="sizeAdjustPolicy">
<enum>QComboBox::SizeAdjustPolicy::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Events">
<property name="title">
<string>Events</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_Event_Options">
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox_Triggers">
<property name="text">
<string>Triggers</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBox_Objects">
<property name="text">
<string>Objects</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="checkBox_HealLocations">
<property name="text">
<string>Heal Locations</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="checkBox_Warps">
<property name="text">
<string>Warps</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_AllEvents">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_BGs">
<property name="text">
<string>BGs</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Connections">
<property name="title">
<string>Connections</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_Connection_Options">
<item row="1" column="2">
<widget class="QCheckBox" name="checkBox_ConnectionLeft">
<property name="text">
<string>Left</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_ConnectionUp">
<property name="text">
<string>Up</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QCheckBox" name="checkBox_ConnectionRight">
<property name="text">
<string>Right</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox_ConnectionDown">
<property name="text">
<string>Down</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_AllConnections">
<property name="text">
<string>All</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Misc">
<property name="title">
<string>Miscellaneous</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_Misc_Options">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_Grid">
<property name="text">
<string>Grid</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_Elevation">
<property name="text">
<string>Collision</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBox_Border">
<property name="text">
<string>Border</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Timelapse">
<property name="title">
<string>Timelapse</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="2" column="1">
<widget class="QSpinBox" name="spinBox_TimelapseDelay">
<property name="specialValueText">
<string/>
</property>
<property name="suffix">
<string>ms</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>2000</number>
</property>
<property name="value">
<number>200</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Frame Delay</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="spinBox_FrameSkip">
<property name="suffix">
<string/>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Edit Frame Skip</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="checkBox_ActualSize">
<property name="text">
<string>Preview actual size</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_Reset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton_Cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_Save">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_Preview">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Preview</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QScrollArea" name="scrollArea">
<widget class="QScrollArea" name="scrollArea_Preview">
<property name="widgetResizable">
<bool>true</bool>
</property>
@ -33,12 +331,24 @@
<rect>
<x>0</x>
<y>0</y>
<width>403</width>
<height>343</height>
<width>469</width>
<height>464</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="2" column="3">
<widget class="QGraphicsView" name="graphicsView_Preview">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -53,65 +363,13 @@
<bool>false</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustIgnored</enum>
<enum>QAbstractScrollArea::SizeAdjustPolicy::AdjustIgnored</enum>
</property>
<property name="dragMode">
<enum>QGraphicsView::NoDrag</enum>
<enum>QGraphicsView::DragMode::NoDrag</enum>
</property>
</widget>
</item>
<item row="4" column="4">
<spacer name="verticalSpacer_South">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>100</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="4">
<spacer name="verticalSpacer_North">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>100</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="5">
<spacer name="horizontalSpacer_West">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>100</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<spacer name="horizontalSpacer_East">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>100</width>
<height>10</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
@ -119,256 +377,18 @@
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_Options">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Map</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="NoScrollComboBox" name="comboBox_MapSelection">
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Events">
<property name="title">
<string>Events</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_Event_Options">
<item row="0" column="1">
<widget class="QCheckBox" name="checkBox_Warps">
<property name="text">
<string>Warps</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_Objects">
<property name="text">
<string>Objects</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="checkBox_BGs">
<property name="text">
<string>BGs</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_Triggers">
<property name="text">
<string>Triggers</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBox_HealSpots">
<property name="text">
<string>Heal Spots</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Connections">
<property name="title">
<string>Connections</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_Connection_Options">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_ConnectionUp">
<property name="text">
<string>Up</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBox_ConnectionDown">
<property name="text">
<string>Down</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="checkBox_ConnectionLeft">
<property name="text">
<string>Left</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QCheckBox" name="checkBox_ConnectionRight">
<property name="text">
<string>Right</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Misc">
<property name="title">
<string>Miscellaneous</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_Misc_Options">
<item row="0" column="0">
<widget class="QCheckBox" name="checkBox_Grid">
<property name="text">
<string>Grid</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBox_Elevation">
<property name="text">
<string>Collision</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBox_Border">
<property name="text">
<string>Border</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_Timelapse">
<property name="title">
<string>Timelapse</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="2" column="1">
<widget class="QSpinBox" name="spinBox_TimelapseDelay">
<property name="specialValueText">
<string/>
</property>
<property name="suffix">
<string>ms</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>2000</number>
</property>
<property name="value">
<number>200</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Frame Delay</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="spinBox_FrameSkip">
<property name="suffix">
<string/>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Edit Frame Skip</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<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>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="pushButton_Reset">
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<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="QPushButton" name="pushButton_Cancel">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_Save">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_Description">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>

File diff suppressed because it is too large Load diff

65
include/core/filedialog.h Normal file
View file

@ -0,0 +1,65 @@
#ifndef FILEDIALOG_H
#define FILEDIALOG_H
#include <QFileDialog>
/*
Static QFileDialog functions will (unless otherwise specified) use native file dialogs.
In general this is good (we want our file dialogs to be visually seamless) but unfortunately
the native file dialogs ignore the parent widget, so in some cases they'll return focus to
the main window rather than the window that opened the file dialog.
To make working around this a little easier we use this class, which will use the native
file dialog and manually return focus to the parent widget.
It will also save the directory of the previous file selected in a file dialog, and if
no 'dir' argument is specified it will open new dialogs at that directory.
*/
class FileDialog : public QFileDialog
{
public:
FileDialog(QWidget *parent, Qt::WindowFlags flags) : QFileDialog(parent, flags) {};
FileDialog(QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &directory = QString(),
const QString &filter = QString()) : QFileDialog(parent, caption, directory, filter) {};
static void setDirectory(const QString &dir) { FileDialog::prevDirectory = dir; }
static QString getDirectory() { return FileDialog::prevDirectory; }
static QString getOpenFileName(QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
QFileDialog::Options options = Options());
static QStringList getOpenFileNames(QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
QFileDialog::Options options = Options());
static QString getExistingDirectory(QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
QFileDialog::Options options = ShowDirsOnly);
static QString getSaveFileName(QWidget *parent = nullptr,
const QString &caption = QString(),
const QString &dir = QString(),
const QString &filter = QString(),
QString *selectedFilter = nullptr,
QFileDialog::Options options = Options());
private:
static QString prevDirectory;
static QString getDirectoryFromInput(const QString &dir);
static void setDirectoryFromFile(const QString &fileName);
static void restoreFocus(QWidget *parent);
};
#endif // FILEDIALOG_H

View file

@ -24,6 +24,7 @@
#include "collisionpixmapitem.h"
#include "layoutpixmapitem.h"
#include "settings.h"
#include "gridsettings.h"
#include "movablerect.h"
#include "cursortilerect.h"
#include "mapruler.h"
@ -54,6 +55,7 @@ public:
QUndoGroup editGroup; // Manages the undo history for each map
Settings *settings;
GridSettings gridSettings;
void setProject(Project * project);
void save();
@ -83,6 +85,7 @@ public:
void displayMapConnections();
void displayMapBorder();
void displayMapGrid();
void updateMapGrid();
void displayWildMonTables();
void updateMapBorder();
@ -132,7 +135,7 @@ public:
QGraphicsItemGroup *events_group = nullptr;
QList<QGraphicsPixmapItem*> borderItems;
QList<QGraphicsLineItem*> gridLines;
QGraphicsItemGroup *mapGrid = nullptr;
MapRuler *map_ruler = nullptr;
MovableRect *playerViewRect = nullptr;
@ -197,6 +200,7 @@ public slots:
void maskNonVisibleConnectionTiles();
void onBorderMetatilesChanged();
void selectedEventIndexChanged(int index, Event::Group eventGroup);
void toggleGrid(bool);
private:
const QImage defaultCollisionImgSheet = QImage(":/images/collisions.png");
@ -251,7 +255,6 @@ private slots:
void onHoveredMapMovementPermissionCleared();
void onSelectedMetatilesChanged();
void onWheelZoom(int);
void onToggleGridClicked(bool);
signals:
void objectsChanged();
@ -263,6 +266,7 @@ signals:
void currentMetatilesSelectionChanged();
void mapRulerStatusChanged(const QString &);
void tilesetUpdated(QString);
void gridToggled(bool);
};
#endif // EDITOR_H

View file

@ -27,6 +27,7 @@
#include "shortcutseditor.h"
#include "preferenceeditor.h"
#include "projectsettingseditor.h"
#include "gridsettings.h"
#include "customscriptseditor.h"
#include "wildmonchart.h"
#include "updatepromoter.h"
@ -326,6 +327,8 @@ private slots:
void on_actionProject_Settings_triggered();
void on_actionCustom_Scripts_triggered();
void reloadScriptEngine();
void on_actionShow_Grid_triggered();
void on_actionGrid_Settings_triggered();
public:
Ui::MainWindow *ui;
@ -340,6 +343,7 @@ private:
QPointer<NewMapPopup> newMapPrompt = nullptr;
QPointer<PreferenceEditor> preferenceEditor = nullptr;
QPointer<ProjectSettingsEditor> projectSettingsEditor = nullptr;
QPointer<GridSettingsDialog> gridSettingsDialog = nullptr;
QPointer<CustomScriptsEditor> customScriptsEditor = nullptr;
FilterChildrenProxyModel *groupListProxyModel;

View file

@ -77,7 +77,6 @@ public:
QFileSystemWatcher fileWatcher;
QMap<QString, qint64> modifiedFileTimestamps;
bool usingAsmTilesets;
QString importExportPath;
QSet<QString> disabledSettingsNames;
int pokemonMinLevel;
int pokemonMaxLevel;
@ -227,7 +226,6 @@ public:
QString buildMetatileLabelsText(const QMap<QString, uint16_t> defines);
QString findMetatileLabelsTileset(QString label);
void setImportExportPath(QString filename);
static QString getExistingFilepath(QString filepath);
void applyParsedLimits();

View file

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

View file

@ -31,7 +31,6 @@ private:
Ui::CustomScriptsEditor *ui;
bool hasUnsavedChanges = false;
QString fileDialogDir;
const QString baseDir;
void displayScript(const QString &filepath, bool enabled);

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

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

View file

@ -16,6 +16,24 @@ enum ImageExporterMode {
Timelapse,
};
struct ImageExporterSettings {
bool showObjects = false;
bool showWarps = false;
bool showBGs = false;
bool showTriggers = false;
bool showHealLocations = false;
bool showUpConnections = false;
bool showDownConnections = false;
bool showLeftConnections = false;
bool showRightConnections = false;
bool showGrid = false;
bool showBorder = false;
bool showCollision = false;
bool previewActualSize = false;
int timelapseSkipAmount = 1;
int timelapseDelayMs = 200;
};
class MapImageExporter : public QDialog
{
Q_OBJECT
@ -34,50 +52,44 @@ private:
QPixmap preview;
bool showObjects = false;
bool showWarps = false;
bool showBGs = false;
bool showTriggers = false;
bool showHealSpots = false;
bool showUpConnections = false;
bool showDownConnections = false;
bool showLeftConnections = false;
bool showRightConnections = false;
bool showGrid = false;
bool showBorder = false;
bool showCollision = false;
int timelapseSkipAmount = 1;
int timelapseDelayMs = 200;
ImageExporterSettings settings;
ImageExporterMode mode = ImageExporterMode::Normal;
void updatePreview();
void scalePreview();
void updateShowBorderState();
void saveImage();
QPixmap getStitchedImage(QProgressDialog *progress, bool includeBorder);
QPixmap getFormattedMapPixmap(Map *map, bool ignoreBorder = false);
bool historyItemAppliesToFrame(const QUndoCommand *command);
protected:
virtual void showEvent(QShowEvent *) override;
virtual void resizeEvent(QResizeEvent *) override;
private slots:
void on_checkBox_Objects_stateChanged(int state);
void on_checkBox_Warps_stateChanged(int state);
void on_checkBox_BGs_stateChanged(int state);
void on_checkBox_Triggers_stateChanged(int state);
void on_checkBox_HealSpots_stateChanged(int state);
void on_checkBox_HealLocations_stateChanged(int state);
void on_checkBox_AllEvents_stateChanged(int state);
void on_checkBox_ConnectionUp_stateChanged(int state);
void on_checkBox_ConnectionDown_stateChanged(int state);
void on_checkBox_ConnectionLeft_stateChanged(int state);
void on_checkBox_ConnectionRight_stateChanged(int state);
void on_checkBox_AllConnections_stateChanged(int state);
void on_checkBox_Elevation_stateChanged(int state);
void on_checkBox_Grid_stateChanged(int state);
void on_checkBox_Border_stateChanged(int state);
void on_pushButton_Save_pressed();
void on_pushButton_Reset_pressed();
void on_pushButton_Cancel_pressed();
void on_spinBox_TimelapseDelay_valueChanged(int delayMs);
void on_spinBox_FrameSkip_valueChanged(int skip);
void on_checkBox_ActualSize_stateChanged(int state);
};
#endif // MAPIMAGEEXPORTER_H

View file

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

View file

@ -4,7 +4,6 @@
#include "orderedjson.h"
#include <QDialog>
#include <QFileDialog>
class Project;
@ -33,7 +32,7 @@ private:
void hideMessages();
QString browse(QString filter, QFileDialog::FileMode mode);
QString browse(QString filter);
private slots:
void on_browse_tilesetImagePath_clicked();

View file

@ -69,7 +69,7 @@ private:
void applySpeciesColors(const QList<QBarSet*> &);
QChart::ChartTheme currentTheme() const;
void updateTheme();
void limitChartAnimation(QChart*);
void limitChartAnimation();
void showHelpDialog();
};

View file

@ -25,6 +25,7 @@ SOURCES += src/core/block.cpp \
src/core/bitpacker.cpp \
src/core/blockdata.cpp \
src/core/events.cpp \
src/core/filedialog.cpp \
src/core/heallocation.cpp \
src/core/imageexport.cpp \
src/core/map.cpp \
@ -51,6 +52,7 @@ SOURCES += src/core/block.cpp \
src/scriptapi/apiutility.cpp \
src/scriptapi/scripting.cpp \
src/ui/aboutporymap.cpp \
src/ui/colorinputwidget.cpp \
src/ui/connectionslistitem.cpp \
src/ui/customscriptseditor.cpp \
src/ui/customscriptslistitem.cpp \
@ -60,6 +62,7 @@ SOURCES += src/core/block.cpp \
src/ui/collisionpixmapitem.cpp \
src/ui/connectionpixmapitem.cpp \
src/ui/currentselectedmetatilespixmapitem.cpp \
src/ui/gridsettings.cpp \
src/ui/newmapconnectiondialog.cpp \
src/ui/overlay.cpp \
src/ui/prefab.cpp \
@ -122,6 +125,7 @@ HEADERS += include/core/block.h \
include/core/bitpacker.h \
include/core/blockdata.h \
include/core/events.h \
include/core/filedialog.h \
include/core/heallocation.h \
include/core/history.h \
include/core/imageexport.h \
@ -158,6 +162,7 @@ HEADERS += include/core/block.h \
include/ui/collisionpixmapitem.h \
include/ui/connectionpixmapitem.h \
include/ui/currentselectedmetatilespixmapitem.h \
include/ui/gridsettings.h \
include/ui/newmapconnectiondialog.h \
include/ui/prefabframe.h \
include/ui/projectsettingseditor.h \
@ -176,6 +181,7 @@ HEADERS += include/core/block.h \
include/ui/prefabcreationdialog.h \
include/ui/regionmappixmapitem.h \
include/ui/citymappixmapitem.h \
include/ui/colorinputwidget.h \
include/ui/metatilelayersitem.h \
include/ui/metatileselector.h \
include/ui/movablerect.h \
@ -220,7 +226,9 @@ HEADERS += include/core/block.h \
include/ui/wildmonchart.h
FORMS += forms/mainwindow.ui \
forms/colorinputwidget.ui \
forms/connectionslistitem.ui \
forms/gridsettingsdialog.ui \
forms/newmapconnectiondialog.ui \
forms/prefabcreationdialog.ui \
forms/prefabframe.ui \

BIN
resources/icons/link.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
resources/icons/link_broken.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -21,6 +21,8 @@
<file>icons/lock_edit.ico</file>
<file>icons/unlock_edit.ico</file>
<file>icons/help.ico</file>
<file>icons/link_broken.ico</file>
<file>icons/link.ico</file>
<file>icons/map_edited.ico</file>
<file>icons/map_opened.ico</file>
<file>icons/map.ico</file>

51
src/core/filedialog.cpp Normal file
View file

@ -0,0 +1,51 @@
#include "filedialog.h"
QString FileDialog::prevDirectory;
QString FileDialog::getDirectoryFromInput(const QString &dir) {
if (dir.isEmpty())
return FileDialog::prevDirectory;
return dir;
}
void FileDialog::setDirectoryFromFile(const QString &fileName) {
if (!fileName.isEmpty())
FileDialog::prevDirectory = QFileInfo(fileName).absolutePath();
}
void FileDialog::restoreFocus(QWidget *parent) {
if (parent) {
parent->raise();
parent->activateWindow();
}
}
QString FileDialog::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) {
const QString fileName = QFileDialog::getOpenFileName(parent, caption, getDirectoryFromInput(dir), filter, selectedFilter, options);
setDirectoryFromFile(fileName);
restoreFocus(parent);
return fileName;
}
QStringList FileDialog::getOpenFileNames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) {
const QStringList fileNames = QFileDialog::getOpenFileNames(parent, caption, getDirectoryFromInput(dir), filter, selectedFilter, options);
if (!fileNames.isEmpty())
setDirectoryFromFile(fileNames.last());
restoreFocus(parent);
return fileNames;
}
QString FileDialog::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) {
const QString fileName = QFileDialog::getSaveFileName(parent, caption, getDirectoryFromInput(dir), filter, selectedFilter, options);
setDirectoryFromFile(fileName);
restoreFocus(parent);
return fileName;
}
QString FileDialog::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options) {
const QString existingDir = QFileDialog::getExistingDirectory(parent, caption, getDirectoryFromInput(dir), options);
if (!existingDir.isEmpty())
setDirectory(existingDir);
restoreFocus(parent);
return existingDir;
}

View file

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

View file

@ -22,8 +22,8 @@
#include "eventfilters.h"
#include "newmapconnectiondialog.h"
#include "config.h"
#include "filedialog.h"
#include <QFileDialog>
#include <QClipboard>
#include <QDirIterator>
#include <QStandardItemModel>
@ -149,10 +149,6 @@ void MainWindow::initExtraShortcuts() {
shortcutReset_Zoom->setObjectName("shortcutZoom_Reset");
shortcutReset_Zoom->setWhatsThis("Zoom Reset");
auto *shortcutToggle_Grid = new Shortcut(QKeySequence("Ctrl+G"), ui->checkBox_ToggleGrid, SLOT(toggle()));
shortcutToggle_Grid->setObjectName("shortcutToggle_Grid");
shortcutToggle_Grid->setWhatsThis("Toggle Grid");
auto *shortcutDuplicate_Events = new Shortcut(QKeySequence("Ctrl+D"), this, SLOT(duplicate()));
shortcutDuplicate_Events->setObjectName("shortcutDuplicate_Events");
shortcutDuplicate_Events->setWhatsThis("Duplicate Selected Event(s)");
@ -358,8 +354,6 @@ void MainWindow::initEditor() {
connect(this->editor, &Editor::wildMonTableEdited, [this] { this->markMapEdited(); });
connect(this->editor, &Editor::mapRulerStatusChanged, this, &MainWindow::onMapRulerStatusChanged);
connect(this->editor, &Editor::tilesetUpdated, this, &Scripting::cb_TilesetUpdated);
connect(ui->toolButton_Open_Scripts, &QToolButton::pressed, this->editor, &Editor::openMapScripts);
connect(ui->actionOpen_Project_in_Text_Editor, &QAction::triggered, this->editor, &Editor::openProjectInTextEditor);
this->loadUserSettings();
@ -501,27 +495,45 @@ void MainWindow::applyMapListFilter(QString filterText) {
}
void MainWindow::loadUserSettings() {
const QSignalBlocker blocker1(ui->horizontalSlider_CollisionTransparency);
const QSignalBlocker blocker2(ui->slider_DiveEmergeMapOpacity);
const QSignalBlocker blocker3(ui->slider_DiveMapOpacity);
const QSignalBlocker blocker4(ui->slider_EmergeMapOpacity);
const QSignalBlocker blocker5(ui->horizontalSlider_MetatileZoom);
const QSignalBlocker blocker6(ui->horizontalSlider_CollisionZoom);
// Better Cursors
ui->actionBetter_Cursors->setChecked(porymapConfig.prettyCursors);
this->editor->settings->betterCursors = porymapConfig.prettyCursors;
// Player view rectangle
ui->actionPlayer_View_Rectangle->setChecked(porymapConfig.showPlayerView);
this->editor->settings->playerViewRectEnabled = porymapConfig.showPlayerView;
// Cursor tile outline
ui->actionCursor_Tile_Outline->setChecked(porymapConfig.showCursorTile);
this->editor->settings->cursorTileRectEnabled = porymapConfig.showCursorTile;
// Border
ui->checkBox_ToggleBorder->setChecked(porymapConfig.showBorder);
// Grid
const QSignalBlocker b_Grid(ui->checkBox_ToggleGrid);
ui->actionShow_Grid->setChecked(porymapConfig.showGrid);
ui->checkBox_ToggleGrid->setChecked(porymapConfig.showGrid);
// Mirror connections
ui->checkBox_MirrorConnections->setChecked(porymapConfig.mirrorConnectingMaps);
// Collision opacity/transparency
const QSignalBlocker b_CollisionTransparency(ui->horizontalSlider_CollisionTransparency);
this->editor->collisionOpacity = static_cast<qreal>(porymapConfig.collisionOpacity) / 100;
ui->horizontalSlider_CollisionTransparency->setValue(porymapConfig.collisionOpacity);
// Dive map opacity/transparency
const QSignalBlocker b_DiveEmergeOpacity(ui->slider_DiveEmergeMapOpacity);
const QSignalBlocker b_DiveMapOpacity(ui->slider_DiveMapOpacity);
const QSignalBlocker b_EmergeMapOpacity(ui->slider_EmergeMapOpacity);
ui->slider_DiveEmergeMapOpacity->setValue(porymapConfig.diveEmergeMapOpacity);
ui->slider_DiveMapOpacity->setValue(porymapConfig.diveMapOpacity);
ui->slider_EmergeMapOpacity->setValue(porymapConfig.emergeMapOpacity);
// Zoom
const QSignalBlocker b_MetatileZoom(ui->horizontalSlider_MetatileZoom);
const QSignalBlocker b_CollisionZoom(ui->horizontalSlider_CollisionZoom);
ui->horizontalSlider_MetatileZoom->setValue(porymapConfig.metatilesZoom);
ui->horizontalSlider_CollisionZoom->setValue(porymapConfig.collisionZoom);
@ -825,7 +837,7 @@ QString MainWindow::getDefaultLayout() {
}
QString MainWindow::getExistingDirectory(QString dir) {
return QFileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly);
return FileDialog::getExistingDirectory(this, "Open Directory", dir, QFileDialog::ShowDirsOnly);
}
void MainWindow::on_action_Open_Project_triggered()
@ -2332,6 +2344,18 @@ void MainWindow::on_actionCursor_Tile_Outline_triggered()
}
}
void MainWindow::on_actionShow_Grid_triggered() {
this->editor->toggleGrid(ui->actionShow_Grid->isChecked());
}
void MainWindow::on_actionGrid_Settings_triggered() {
if (!this->gridSettingsDialog) {
this->gridSettingsDialog = new GridSettingsDialog(&this->editor->gridSettings, this);
connect(this->gridSettingsDialog, &GridSettingsDialog::changedGridSettings, this->editor, &Editor::updateMapGrid);
}
openSubWindow(this->gridSettingsDialog);
}
void MainWindow::on_actionShortcuts_triggered()
{
if (!shortcutsEditor)
@ -3011,15 +3035,11 @@ void MainWindow::on_actionImport_Map_from_Advance_Map_1_92_triggered(){
void MainWindow::importMapFromAdvanceMap1_92()
{
QString filepath = QFileDialog::getOpenFileName(
this,
QString("Import Map from Advance Map 1.92"),
this->editor->project->importExportPath,
"Advance Map 1.92 Map Files (*.map)");
QString filepath = FileDialog::getOpenFileName(this, "Import Map from Advance Map 1.92", "", "Advance Map 1.92 Map Files (*.map)");
if (filepath.isEmpty()) {
return;
}
this->editor->project->setImportExportPath(filepath);
MapParser parser;
bool error = false;
Layout *mapLayout = parser.parse(filepath, &error, editor->project);

View file

@ -7,6 +7,7 @@
#include "tile.h"
#include "tileset.h"
#include "map.h"
#include "filedialog.h"
#include "orderedjson.h"
@ -89,7 +90,7 @@ void Project::initSignals() {
void Project::set_root(QString dir) {
this->root = dir;
this->importExportPath = dir;
FileDialog::setDirectory(dir);
this->parser.set_root(dir);
}
@ -2980,11 +2981,6 @@ QString Project::getDynamicMapDefineName() {
return prefix + projectConfig.getIdentifier(ProjectIdentifier::define_map_dynamic);
}
void Project::setImportExportPath(QString filename)
{
this->importExportPath = QFileInfo(filename).absolutePath();
}
// If the provided filepath is an absolute path to an existing file, return filepath.
// If not, and the provided filepath is a relative path from the project dir to an existing file, return the relative path.
// Otherwise return empty string.

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

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

View file

@ -4,9 +4,9 @@
#include "config.h"
#include "editor.h"
#include "shortcut.h"
#include "filedialog.h"
#include <QDir>
#include <QFileDialog>
CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) :
QMainWindow(parent),
@ -23,8 +23,6 @@ CustomScriptsEditor::CustomScriptsEditor(QWidget *parent) :
for (int i = 0; i < paths.length(); i++)
this->displayScript(paths.at(i), enabled.at(i));
this->fileDialogDir = userConfig.projectDir;
connect(ui->button_CreateNewScript, &QAbstractButton::clicked, this, &CustomScriptsEditor::createNewScript);
connect(ui->button_LoadScript, &QAbstractButton::clicked, this, &CustomScriptsEditor::loadScript);
connect(ui->button_RefreshScripts, &QAbstractButton::clicked, this, &CustomScriptsEditor::userRefreshScripts);
@ -147,19 +145,13 @@ bool CustomScriptsEditor::getScriptEnabled(QListWidgetItem * item) const {
}
QString CustomScriptsEditor::chooseScript(QString dir) {
return QFileDialog::getOpenFileName(this, "Choose Custom Script File", dir, "JavaScript Files (*.js)");
return FileDialog::getOpenFileName(this, "Choose Custom Script File", dir, "JavaScript Files (*.js)");
}
void CustomScriptsEditor::createNewScript() {
QString filepath = QFileDialog::getSaveFileName(this, "Create New Script File", this->fileDialogDir + "/new_script.js", "JavaScript Files (*.js)");
// QFileDialog::getSaveFileName returns focus to the main editor window when closed. Workaround for this below
this->raise();
this->activateWindow();
const QString filepath = FileDialog::getSaveFileName(this, "Create New Script File", FileDialog::getDirectory() + "/new_script.js", "JavaScript Files (*.js)");
if (filepath.isEmpty())
return;
this->fileDialogDir = filepath;
QFile scriptFile(filepath);
if (!scriptFile.open(QIODevice::WriteOnly)) {
@ -179,10 +171,9 @@ void CustomScriptsEditor::createNewScript() {
}
void CustomScriptsEditor::loadScript() {
QString filepath = this->chooseScript(this->fileDialogDir);
QString filepath = this->chooseScript(FileDialog::getDirectory());
if (filepath.isEmpty())
return;
this->fileDialogDir = filepath;
this->displayNewScript(filepath);
}

View file

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

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

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

View file

@ -2,8 +2,8 @@
#include "ui_mapimageexporter.h"
#include "qgifimage.h"
#include "editcommands.h"
#include "filedialog.h"
#include <QFileDialog>
#include <QImage>
#include <QPainter>
#include <QPoint>
@ -23,6 +23,19 @@ QString getTitle(ImageExporterMode mode) {
return "";
}
QString getDescription(ImageExporterMode mode) {
switch (mode)
{
case ImageExporterMode::Normal:
return "Exports an image of the selected map.";
case ImageExporterMode::Stitch:
return "Exports a combined image of all the maps connected to the selected map.";
case ImageExporterMode::Timelapse:
return "Exports a GIF of the edit history for the selected map.";
}
return "";
}
MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExporterMode mode) :
QDialog(parent_),
ui(new Ui::MapImageExporter)
@ -34,6 +47,7 @@ MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExpor
this->editor = editor_;
this->mode = mode;
this->setWindowTitle(getTitle(this->mode));
this->ui->label_Description->setText(getDescription(this->mode));
this->ui->groupBox_Connections->setVisible(this->mode != ImageExporterMode::Stitch);
this->ui->groupBox_Timelapse->setVisible(this->mode == ImageExporterMode::Timelapse);
@ -43,7 +57,8 @@ MapImageExporter::MapImageExporter(QWidget *parent_, Editor *editor_, ImageExpor
this->ui->comboBox_MapSelection->setEnabled(false);// TODO: allow selecting map from drop-down
}
updatePreview();
connect(ui->pushButton_Save, &QPushButton::pressed, this, &MapImageExporter::saveImage);
connect(ui->pushButton_Cancel, &QPushButton::pressed, this, &MapImageExporter::close);
}
MapImageExporter::~MapImageExporter() {
@ -51,7 +66,25 @@ MapImageExporter::~MapImageExporter() {
delete ui;
}
// Allow the window to open before displaying the preview.
void MapImageExporter::showEvent(QShowEvent *event) {
QWidget::showEvent(event);
if (!event->spontaneous())
QTimer::singleShot(0, this, &MapImageExporter::updatePreview);
}
void MapImageExporter::resizeEvent(QResizeEvent *event) {
QDialog::resizeEvent(event);
scalePreview();
}
void MapImageExporter::saveImage() {
// Make sure preview is up-to-date before we save.
if (this->preview.isNull())
updatePreview();
if (this->preview.isNull())
return;
QString title = getTitle(this->mode);
QString defaultFilename;
switch (this->mode)
@ -68,40 +101,27 @@ void MapImageExporter::saveImage() {
}
QString defaultFilepath = QString("%1/%2.%3")
.arg(editor->project->importExportPath)
.arg(FileDialog::getDirectory())
.arg(defaultFilename)
.arg(this->mode == ImageExporterMode::Timelapse ? "gif" : "png");
QString filter = this->mode == ImageExporterMode::Timelapse ? "Image Files (*.gif)" : "Image Files (*.png *.jpg *.bmp)";
QString filepath = QFileDialog::getSaveFileName(this, title, defaultFilepath, filter);
QString filepath = FileDialog::getSaveFileName(this, title, defaultFilepath, filter);
if (!filepath.isEmpty()) {
editor->project->setImportExportPath(filepath);
switch (this->mode) {
case ImageExporterMode::Normal:
case ImageExporterMode::Stitch:
// Normal and Stitch modes already have the image ready to go in the preview.
this->preview.save(filepath);
break;
case ImageExporterMode::Stitch: {
QProgressDialog progress("Building map stitch...", "Cancel", 0, 1, this);
progress.setAutoClose(true);
progress.setWindowModality(Qt::WindowModal);
progress.setModal(true);
QPixmap pixmap = this->getStitchedImage(&progress, this->showBorder);
if (progress.wasCanceled()) {
progress.close();
return;
}
pixmap.save(filepath);
progress.close();
break;
}
case ImageExporterMode::Timelapse:
// Timelapse will play in order of layout changes then map changes (events)
// TODO: potentially update in the future?
QGifImage timelapseImg;
timelapseImg.setDefaultDelay(timelapseDelayMs);
timelapseImg.setDefaultDelay(this->settings.timelapseDelayMs);
timelapseImg.setDefaultTransparentColor(QColor(0, 0, 0));
// lambda to avoid redundancy
auto generateTimelapseFromHistory = [=, this, &timelapseImg](QString progressText, QUndoStack &historyStack){
auto generateTimelapseFromHistory = [this, &timelapseImg](QString progressText, QUndoStack &historyStack){
QProgressDialog progress(progressText, "Cancel", 0, 1, this);
progress.setAutoClose(true);
progress.setWindowModality(Qt::WindowModal);
@ -111,7 +131,7 @@ void MapImageExporter::saveImage() {
int maxWidth = this->layout->getWidth() * 16;
int maxHeight = this->layout->getHeight() * 16;
if (showBorder) {
if (this->settings.showBorder) {
maxWidth += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
maxHeight += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
}
@ -122,7 +142,7 @@ void MapImageExporter::saveImage() {
historyStack.undo();
int width = this->layout->getWidth() * 16;
int height = this->layout->getHeight() * 16;
if (showBorder) {
if (this->settings.showBorder) {
width += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
height += 2 * STITCH_MODE_BORDER_DISTANCE * 16;
}
@ -153,7 +173,7 @@ void MapImageExporter::saveImage() {
historyStack.redo();
}
progress.setValue(progress.maximum() - i);
QPixmap pixmap = this->getFormattedMapPixmap(this->map, !this->showBorder);
QPixmap pixmap = this->getFormattedMapPixmap(this->map);
if (pixmap.width() < maxWidth || pixmap.height() < maxHeight) {
QPixmap pixmap2 = QPixmap(maxWidth, maxHeight);
QPainter painter(&pixmap2);
@ -163,7 +183,7 @@ void MapImageExporter::saveImage() {
pixmap = pixmap2;
}
timelapseImg.addFrame(pixmap.toImage());
for (int j = 0; j < timelapseSkipAmount; j++) {
for (int j = 0; j < this->settings.timelapseSkipAmount; j++) {
if (i > 0) {
i--;
historyStack.redo();
@ -209,26 +229,26 @@ bool MapImageExporter::historyItemAppliesToFrame(const QUndoCommand *command) {
case CommandId::ID_PaintCollision:
case CommandId::ID_BucketFillCollision:
case CommandId::ID_MagicFillCollision:
return this->showCollision;
return this->settings.showCollision;
case CommandId::ID_PaintBorder:
return this->showBorder;
return this->settings.showBorder;
case CommandId::ID_MapConnectionMove:
case CommandId::ID_MapConnectionChangeDirection:
case CommandId::ID_MapConnectionChangeMap:
case CommandId::ID_MapConnectionAdd:
case CommandId::ID_MapConnectionRemove:
return this->showUpConnections || this->showDownConnections || this->showLeftConnections || this->showRightConnections;
return this->settings.showUpConnections || this->settings.showDownConnections || this->settings.showLeftConnections || this->settings.showRightConnections;
case CommandId::ID_EventMove:
case CommandId::ID_EventShift:
case CommandId::ID_EventCreate:
case CommandId::ID_EventDelete:
case CommandId::ID_EventDuplicate: {
bool eventTypeIsApplicable =
(this->showObjects && (command->id() & IDMask_EventType_Object) != 0)
|| (this->showWarps && (command->id() & IDMask_EventType_Warp) != 0)
|| (this->showBGs && (command->id() & IDMask_EventType_BG) != 0)
|| (this->showTriggers && (command->id() & IDMask_EventType_Trigger) != 0)
|| (this->showHealSpots && (command->id() & IDMask_EventType_Heal) != 0);
(this->settings.showObjects && (command->id() & IDMask_EventType_Object) != 0)
|| (this->settings.showWarps && (command->id() & IDMask_EventType_Warp) != 0)
|| (this->settings.showBGs && (command->id() & IDMask_EventType_BG) != 0)
|| (this->settings.showTriggers && (command->id() & IDMask_EventType_Trigger) != 0)
|| (this->settings.showHealLocations && (command->id() & IDMask_EventType_Heal) != 0);
return eventTypeIsApplicable;
}
default:
@ -371,19 +391,33 @@ QPixmap MapImageExporter::getStitchedImage(QProgressDialog *progress, bool inclu
}
void MapImageExporter::updatePreview() {
if (scene) {
delete scene;
scene = nullptr;
if (this->scene) {
delete this->scene;
this->scene = nullptr;
}
this->scene = new QGraphicsScene;
preview = getFormattedMapPixmap(this->map);
scene = new QGraphicsScene;
scene->addPixmap(preview);
this->scene->setSceneRect(this->scene->itemsBoundingRect());
if (this->mode == ImageExporterMode::Stitch) {
QProgressDialog progress("Building map stitch...", "Cancel", 0, 1, this);
progress.setAutoClose(true);
progress.setWindowModality(Qt::WindowModal);
progress.setModal(true);
progress.setMinimumDuration(1000);
this->preview = getStitchedImage(&progress, this->settings.showBorder);
progress.close();
} else {
// Timelapse mode doesn't currently have a real preview. It just displays the current map as in Normal mode.
this->preview = getFormattedMapPixmap(this->map);
}
this->scene->addPixmap(this->preview);
ui->graphicsView_Preview->setScene(scene);
scalePreview();
}
this->ui->graphicsView_Preview->setScene(scene);
this->ui->graphicsView_Preview->setFixedSize(scene->itemsBoundingRect().width() + 2,
scene->itemsBoundingRect().height() + 2);
void MapImageExporter::scalePreview() {
if (this->scene && !this->settings.previewActualSize){
ui->graphicsView_Preview->fitInView(this->scene->sceneRect(), Qt::KeepAspectRatioByExpanding);
}
}
// THIS
@ -403,7 +437,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
pixmap = map->layout->pixmap;
}
if (showCollision) {
if (this->settings.showCollision) {
QPainter collisionPainter(&pixmap);
layout->renderCollision(true);
collisionPainter.setOpacity(editor->collisionOpacity);
@ -414,7 +448,7 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
// draw map border
// note: this will break when allowing map to be selected from drop down maybe
int borderHeight = 0, borderWidth = 0;
if (!ignoreBorder && this->showBorder) {
if (!ignoreBorder && this->settings.showBorder) {
int borderDistance = this->mode ? STITCH_MODE_BORDER_DISTANCE : BORDER_DISTANCE;
layout->renderBorder();
int borderHorzDist = editor->getBorderDrawDistance(layout->getBorderWidth());
@ -437,15 +471,16 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
return pixmap;
}
if (!ignoreBorder && (this->showUpConnections || this->showDownConnections || this->showLeftConnections || this->showRightConnections)) {
if (!ignoreBorder && (this->settings.showUpConnections || this->settings.showDownConnections || this->settings.showLeftConnections || this->settings.showRightConnections)) {
// if showing connections, draw on outside of image
QPainter connectionPainter(&pixmap);
// TODO: Reading the connections from the editor and not 'map' is incorrect.
for (auto connectionItem : editor->connection_items) {
const QString direction = connectionItem->connection->direction();
if ((showUpConnections && direction == "up")
|| (showDownConnections && direction == "down")
|| (showLeftConnections && direction == "left")
|| (showRightConnections && direction == "right"))
if ((this->settings.showUpConnections && direction == "up")
|| (this->settings.showDownConnections && direction == "down")
|| (this->settings.showLeftConnections && direction == "left")
|| (this->settings.showRightConnections && direction == "right"))
connectionPainter.drawImage(connectionItem->x() + borderWidth, connectionItem->y() + borderHeight,
connectionItem->connection->getPixmap().toImage());
}
@ -453,27 +488,30 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
}
// draw events
QPainter eventPainter(&pixmap);
QList<Event *> events = map->getAllEvents();
int pixelOffset = 0;
if (!ignoreBorder && this->showBorder) {
pixelOffset = this->mode == ImageExporterMode::Normal ? BORDER_DISTANCE * 16 : STITCH_MODE_BORDER_DISTANCE * 16;
if (this->settings.showObjects || this->settings.showWarps || this->settings.showBGs || this->settings.showTriggers || this->settings.showHealLocations) {
QPainter eventPainter(&pixmap);
int pixelOffset = 0;
if (!ignoreBorder && this->settings.showBorder) {
pixelOffset = this->mode == ImageExporterMode::Normal ? BORDER_DISTANCE * 16 : STITCH_MODE_BORDER_DISTANCE * 16;
}
const QList<Event *> events = map->getAllEvents();
for (const auto &event : events) {
Event::Group group = event->getEventGroup();
if ((this->settings.showObjects && group == Event::Group::Object)
|| (this->settings.showWarps && group == Event::Group::Warp)
|| (this->settings.showBGs && group == Event::Group::Bg)
|| (this->settings.showTriggers && group == Event::Group::Coord)
|| (this->settings.showHealLocations && group == Event::Group::Heal)) {
editor->project->setEventPixmap(event);
eventPainter.drawImage(QPoint(event->getPixelX() + pixelOffset, event->getPixelY() + pixelOffset), event->getPixmap().toImage());
}
}
eventPainter.end();
}
for (Event *event : events) {
editor->project->setEventPixmap(event);
Event::Group group = event->getEventGroup();
if ((showObjects && group == Event::Group::Object)
|| (showWarps && group == Event::Group::Warp)
|| (showBGs && group == Event::Group::Bg)
|| (showTriggers && group == Event::Group::Coord)
|| (showHealSpots && group == Event::Group::Heal))
eventPainter.drawImage(QPoint(event->getPixelX() + pixelOffset, event->getPixelY() + pixelOffset), event->getPixmap().toImage());
}
eventPainter.end();
// draw grid directly onto the pixmap
// since the last grid lines are outside of the pixmap, add a pixel to the bottom and right
if (showGrid) {
if (this->settings.showGrid) {
int addX = 1, addY = 1;
if (borderHeight) addY = 0;
if (borderWidth) addX = 0;
@ -496,97 +534,161 @@ QPixmap MapImageExporter::getFormattedMapPixmap(Map *map, bool ignoreBorder) {
void MapImageExporter::updateShowBorderState() {
// If any of the Connections settings are enabled then this setting is locked (it's implicitly enabled)
bool on = (this->settings.showUpConnections || this->settings.showDownConnections || this->settings.showLeftConnections || this->settings.showRightConnections);
const QSignalBlocker blocker(ui->checkBox_Border);
if (showUpConnections || showDownConnections || showLeftConnections || showRightConnections) {
ui->checkBox_Border->setChecked(true);
ui->checkBox_Border->setDisabled(true);
showBorder = true;
} else {
ui->checkBox_Border->setDisabled(false);
}
ui->checkBox_Border->setChecked(on);
ui->checkBox_Border->setDisabled(on);
this->settings.showBorder = on;
}
void MapImageExporter::on_checkBox_Elevation_stateChanged(int state) {
showCollision = (state == Qt::Checked);
this->settings.showCollision = (state == Qt::Checked);
updatePreview();
}
void MapImageExporter::on_checkBox_Grid_stateChanged(int state) {
showGrid = (state == Qt::Checked);
this->settings.showGrid = (state == Qt::Checked);
updatePreview();
}
void MapImageExporter::on_checkBox_Border_stateChanged(int state) {
showBorder = (state == Qt::Checked);
this->settings.showBorder = (state == Qt::Checked);
updatePreview();
}
void MapImageExporter::on_checkBox_Objects_stateChanged(int state) {
showObjects = (state == Qt::Checked);
this->settings.showObjects = (state == Qt::Checked);
updatePreview();
}
void MapImageExporter::on_checkBox_Warps_stateChanged(int state) {
showWarps = (state == Qt::Checked);
this->settings.showWarps = (state == Qt::Checked);
updatePreview();
}
void MapImageExporter::on_checkBox_BGs_stateChanged(int state) {
showBGs = (state == Qt::Checked);
this->settings.showBGs = (state == Qt::Checked);
updatePreview();
}
void MapImageExporter::on_checkBox_Triggers_stateChanged(int state) {
showTriggers = (state == Qt::Checked);
this->settings.showTriggers = (state == Qt::Checked);
updatePreview();
}
void MapImageExporter::on_checkBox_HealSpots_stateChanged(int state) {
showHealSpots = (state == Qt::Checked);
void MapImageExporter::on_checkBox_HealLocations_stateChanged(int state) {
this->settings.showHealLocations = (state == Qt::Checked);
updatePreview();
}
// Shortcut setting for enabling all events
void MapImageExporter::on_checkBox_AllEvents_stateChanged(int state) {
bool on = (state == Qt::Checked);
const QSignalBlocker b_Objects(ui->checkBox_Objects);
ui->checkBox_Objects->setChecked(on);
ui->checkBox_Objects->setDisabled(on);
this->settings.showObjects = on;
const QSignalBlocker b_Warps(ui->checkBox_Warps);
ui->checkBox_Warps->setChecked(on);
ui->checkBox_Warps->setDisabled(on);
this->settings.showWarps = on;
const QSignalBlocker b_BGs(ui->checkBox_BGs);
ui->checkBox_BGs->setChecked(on);
ui->checkBox_BGs->setDisabled(on);
this->settings.showBGs = on;
const QSignalBlocker b_Triggers(ui->checkBox_Triggers);
ui->checkBox_Triggers->setChecked(on);
ui->checkBox_Triggers->setDisabled(on);
this->settings.showTriggers = on;
const QSignalBlocker b_HealLocations(ui->checkBox_HealLocations);
ui->checkBox_HealLocations->setChecked(on);
ui->checkBox_HealLocations->setDisabled(on);
this->settings.showHealLocations = on;
updatePreview();
}
void MapImageExporter::on_checkBox_ConnectionUp_stateChanged(int state) {
showUpConnections = (state == Qt::Checked);
this->settings.showUpConnections = (state == Qt::Checked);
updateShowBorderState();
updatePreview();
}
void MapImageExporter::on_checkBox_ConnectionDown_stateChanged(int state) {
showDownConnections = (state == Qt::Checked);
this->settings.showDownConnections = (state == Qt::Checked);
updateShowBorderState();
updatePreview();
}
void MapImageExporter::on_checkBox_ConnectionLeft_stateChanged(int state) {
showLeftConnections = (state == Qt::Checked);
this->settings.showLeftConnections = (state == Qt::Checked);
updateShowBorderState();
updatePreview();
}
void MapImageExporter::on_checkBox_ConnectionRight_stateChanged(int state) {
showRightConnections = (state == Qt::Checked);
this->settings.showRightConnections = (state == Qt::Checked);
updateShowBorderState();
updatePreview();
}
void MapImageExporter::on_pushButton_Save_pressed() {
saveImage();
// Shortcut setting for enabling all connection directions
void MapImageExporter::on_checkBox_AllConnections_stateChanged(int state) {
bool on = (state == Qt::Checked);
const QSignalBlocker b_Up(ui->checkBox_ConnectionUp);
ui->checkBox_ConnectionUp->setChecked(on);
ui->checkBox_ConnectionUp->setDisabled(on);
this->settings.showUpConnections = on;
const QSignalBlocker b_Down(ui->checkBox_ConnectionDown);
ui->checkBox_ConnectionDown->setChecked(on);
ui->checkBox_ConnectionDown->setDisabled(on);
this->settings.showDownConnections = on;
const QSignalBlocker b_Left(ui->checkBox_ConnectionLeft);
ui->checkBox_ConnectionLeft->setChecked(on);
ui->checkBox_ConnectionLeft->setDisabled(on);
this->settings.showLeftConnections = on;
const QSignalBlocker b_Right(ui->checkBox_ConnectionRight);
ui->checkBox_ConnectionRight->setChecked(on);
ui->checkBox_ConnectionRight->setDisabled(on);
this->settings.showRightConnections = on;
updateShowBorderState();
updatePreview();
}
void MapImageExporter::on_checkBox_ActualSize_stateChanged(int state) {
this->settings.previewActualSize = (state == Qt::Checked);
if (this->settings.previewActualSize) {
ui->graphicsView_Preview->resetTransform();
} else {
scalePreview();
}
}
void MapImageExporter::on_pushButton_Reset_pressed() {
for (auto widget : this->findChildren<QCheckBox *>())
this->settings = {};
for (auto widget : this->findChildren<QCheckBox *>()) {
const QSignalBlocker b(widget); // Prevent calls to updatePreview
widget->setChecked(false);
}
void MapImageExporter::on_pushButton_Cancel_pressed() {
this->close();
}
ui->spinBox_TimelapseDelay->setValue(this->settings.timelapseDelayMs);
ui->spinBox_FrameSkip->setValue(this->settings.timelapseSkipAmount);
updatePreview();
}
void MapImageExporter::on_spinBox_TimelapseDelay_valueChanged(int delayMs) {
timelapseDelayMs = delayMs;
this->settings.timelapseDelayMs = delayMs;
}
void MapImageExporter::on_spinBox_FrameSkip_valueChanged(int skip) {
timelapseSkipAmount = skip;
this->settings.timelapseSkipAmount = skip;
}

View file

@ -1,6 +1,5 @@
#include "newtilesetdialog.h"
#include "ui_newtilesetdialog.h"
#include <QFileDialog>
#include "project.h"
NewTilesetDialog::NewTilesetDialog(Project* project, QWidget *parent) :

View file

@ -2,6 +2,7 @@
#include <QCompleter>
#include <QLineEdit>
#include <QWheelEvent>
NoScrollComboBox::NoScrollComboBox(QWidget *parent)
: QComboBox(parent)
@ -39,8 +40,11 @@ void NoScrollComboBox::wheelEvent(QWheelEvent *event)
{
// By default NoScrollComboBoxes will allow scrolling to modify its contents only when it explicitly has focus.
// If focusedScrollingEnabled is false it won't allow scrolling even with focus.
if (this->focusedScrollingEnabled && hasFocus())
if (this->focusedScrollingEnabled && hasFocus()) {
QComboBox::wheelEvent(event);
} else {
event->ignore();
}
}
void NoScrollComboBox::setFocusedScrollingEnabled(bool enabled)

View file

@ -1,4 +1,5 @@
#include "noscrollspinbox.h"
#include <QWheelEvent>
unsigned actionId = 0xffff;
@ -12,8 +13,11 @@ NoScrollSpinBox::NoScrollSpinBox(QWidget *parent)
void NoScrollSpinBox::wheelEvent(QWheelEvent *event)
{
// Only allow scrolling to modify contents when it explicitly has focus.
if (hasFocus())
if (hasFocus()) {
QSpinBox::wheelEvent(event);
} else {
event->ignore();
}
}
void NoScrollSpinBox::focusOutEvent(QFocusEvent *event) {

View file

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

View file

@ -2,6 +2,7 @@
#include "config.h"
#include "noscrollcombobox.h"
#include "prefab.h"
#include "filedialog.h"
#include <QAbstractButton>
#include <QFormLayout>
@ -383,10 +384,10 @@ QString ProjectSettingsEditor::chooseProjectFile(const QString &defaultFilepath)
QString path;
if (defaultFilepath.endsWith("/")){
// Default filepath is a folder, choose a new folder
path = QFileDialog::getExistingDirectory(this, "Choose Project File Folder", startDir) + QDir::separator();
path = FileDialog::getExistingDirectory(this, "Choose Project File Folder", startDir) + QDir::separator();
} else{
// Default filepath is not a folder, choose a new file
path = QFileDialog::getOpenFileName(this, "Choose Project File", startDir);
path = FileDialog::getOpenFileName(this, "Choose Project File", startDir);
}
if (!path.startsWith(this->baseDir)){
@ -573,10 +574,9 @@ void ProjectSettingsEditor::chooseImageFile(QLineEdit * filepathEdit) {
}
void ProjectSettingsEditor::chooseFile(QLineEdit * filepathEdit, const QString &description, const QString &extensions) {
QString filepath = QFileDialog::getOpenFileName(this, description, this->project->importExportPath, extensions);
QString filepath = FileDialog::getOpenFileName(this, description, "", extensions);
if (filepath.isEmpty())
return;
this->project->setImportExportPath(filepath);
if (filepathEdit)
filepathEdit->setText(this->stripProjectDir(filepath));

View file

@ -10,7 +10,6 @@
#include <QDir>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QFormLayout>
#include <QLineEdit>
#include <QSpinBox>

View file

@ -1,6 +1,7 @@
#include "project.h"
#include "regionmappropertiesdialog.h"
#include "ui_regionmappropertiesdialog.h"
#include "filedialog.h"
RegionMapPropertiesDialog::RegionMapPropertiesDialog(QWidget *parent) :
QDialog(parent),
@ -30,13 +31,9 @@ void RegionMapPropertiesDialog::hideMessages() {
this->adjustSize();
}
QString RegionMapPropertiesDialog::browse(QString filter, QFileDialog::FileMode mode) {
QString RegionMapPropertiesDialog::browse(QString filter) {
if (!this->project) return QString();
QFileDialog browser;
browser.setFileMode(mode);
QString filepath = browser.getOpenFileName(this, "Select a File", this->project->importExportPath, filter);
if (!filepath.isEmpty())
this->project->setImportExportPath(filepath);
QString filepath = FileDialog::getOpenFileName(this, "Select a File", "", filter);
// remove the project root from the filepath
return filepath.replace(this->project->root + "/", "");
@ -107,21 +104,21 @@ poryjson::Json RegionMapPropertiesDialog::saveToJson() {
}
void RegionMapPropertiesDialog::on_browse_tilesetImagePath_clicked() {
QString path = browse("Images (*.png *.bmp)", QFileDialog::ExistingFile);
QString path = browse("Images (*.png *.bmp)");
if (!path.isEmpty()) {
ui->config_tilemapImagePath->setText(path);
}
}
void RegionMapPropertiesDialog::on_browse_tilemapBinPath_clicked() {
QString path = browse("Binary (*.bin *.tilemap *.4bpp *.8bpp)", QFileDialog::AnyFile);
QString path = browse("Binary (*.bin *.tilemap *.4bpp *.8bpp)");
if (!path.isEmpty()) {
ui->config_tilemapBinPath->setText(path);
}
}
void RegionMapPropertiesDialog::on_browse_tilemapPalettePath_clicked() {
QString path = browse("Text (*.pal)", QFileDialog::AnyFile);
QString path = browse("Text (*.pal)");
if (!path.isEmpty()) {
ui->config_tilemapPalettePath->setText(path);
}
@ -129,12 +126,12 @@ void RegionMapPropertiesDialog::on_browse_tilemapPalettePath_clicked() {
void RegionMapPropertiesDialog::on_browse_layoutPath_clicked() {
if (ui->config_layoutFormat->currentIndex() == 0) {
QString path = browse("Text File (*.h *.c *.inc *.txt)", QFileDialog::AnyFile);
QString path = browse("Text File (*.h *.c *.inc *.txt)");
if (!path.isEmpty()) {
ui->config_layoutPath->setText(path);
}
} else {
QString path = browse("Binary (*.bin)", QFileDialog::AnyFile);
QString path = browse("Binary (*.bin)");
if (!path.isEmpty()) {
ui->config_layoutPath->setText(path);
}

View file

@ -7,7 +7,7 @@
#include "imageexport.h"
#include "config.h"
#include "shortcut.h"
#include <QFileDialog>
#include "filedialog.h"
#include <QMessageBox>
#include <QDialogButtonBox>
#include <QCloseEvent>
@ -637,15 +637,11 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
QString descriptor = primary ? "primary" : "secondary";
QString descriptorCaps = primary ? "Primary" : "Secondary";
QString filepath = QFileDialog::getOpenFileName(
this,
QString("Import %1 Tileset Tiles Image").arg(descriptorCaps),
this->project->importExportPath,
"Image Files (*.png *.bmp *.jpg *.dib)");
QString filepath = FileDialog::getOpenFileName(this, QString("Import %1 Tileset Tiles Image").arg(descriptorCaps), "", "Image Files (*.png *.bmp *.jpg *.dib)");
if (filepath.isEmpty()) {
return;
}
this->project->setImportExportPath(filepath);
logInfo(QString("Importing %1 tileset tiles '%2'").arg(descriptor).arg(filepath));
// Read image data from buffer so that the built-in QImage doesn't try to detect file format
@ -698,15 +694,11 @@ void TilesetEditor::importTilesetTiles(Tileset *tileset, bool primary) {
msgBox.setIcon(QMessageBox::Icon::Warning);
msgBox.exec();
QString filepath = QFileDialog::getOpenFileName(
this,
QString("Select Palette for Tiles Image").arg(descriptorCaps),
this->project->importExportPath,
"Palette Files (*.pal *.act *tpl *gpl)");
QString filepath = FileDialog::getOpenFileName(this, "Select Palette for Tiles Image", "", "Palette Files (*.pal *.act *tpl *gpl)");
if (filepath.isEmpty()) {
return;
}
this->project->setImportExportPath(filepath);
bool error = false;
QList<QRgb> palette = PaletteUtil::parse(filepath, &error);
if (error) {
@ -939,10 +931,9 @@ void TilesetEditor::pasteMetatile(const Metatile * toPaste, QString newLabel)
void TilesetEditor::on_actionExport_Primary_Tiles_Image_triggered()
{
QString defaultName = QString("%1_Tiles_Pal%2").arg(this->primaryTileset->name).arg(this->paletteId);
QString defaultFilepath = QString("%1/%2.png").arg(this->project->importExportPath).arg(defaultName);
QString filepath = QFileDialog::getSaveFileName(this, "Export Primary Tiles Image", defaultFilepath, "Image Files (*.png)");
QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName);
QString filepath = FileDialog::getSaveFileName(this, "Export Primary Tiles Image", defaultFilepath, "Image Files (*.png)");
if (!filepath.isEmpty()) {
this->project->setImportExportPath(filepath);
QImage image = this->tileSelector->buildPrimaryTilesIndexedImage();
exportIndexed4BPPPng(image, filepath);
}
@ -951,10 +942,9 @@ void TilesetEditor::on_actionExport_Primary_Tiles_Image_triggered()
void TilesetEditor::on_actionExport_Secondary_Tiles_Image_triggered()
{
QString defaultName = QString("%1_Tiles_Pal%2").arg(this->secondaryTileset->name).arg(this->paletteId);
QString defaultFilepath = QString("%1/%2.png").arg(this->project->importExportPath).arg(defaultName);
QString filepath = QFileDialog::getSaveFileName(this, "Export Secondary Tiles Image", defaultFilepath, "Image Files (*.png)");
QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName);
QString filepath = FileDialog::getSaveFileName(this, "Export Secondary Tiles Image", defaultFilepath, "Image Files (*.png)");
if (!filepath.isEmpty()) {
this->project->setImportExportPath(filepath);
QImage image = this->tileSelector->buildSecondaryTilesIndexedImage();
exportIndexed4BPPPng(image, filepath);
}
@ -963,10 +953,9 @@ void TilesetEditor::on_actionExport_Secondary_Tiles_Image_triggered()
void TilesetEditor::on_actionExport_Primary_Metatiles_Image_triggered()
{
QString defaultName = QString("%1_Metatiles").arg(this->primaryTileset->name);
QString defaultFilepath = QString("%1/%2.png").arg(this->project->importExportPath).arg(defaultName);
QString filepath = QFileDialog::getSaveFileName(this, "Export Primary Metatiles Image", defaultFilepath, "Image Files (*.png)");
QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName);
QString filepath = FileDialog::getSaveFileName(this, "Export Primary Metatiles Image", defaultFilepath, "Image Files (*.png)");
if (!filepath.isEmpty()) {
this->project->setImportExportPath(filepath);
QImage image = this->metatileSelector->buildPrimaryMetatilesImage();
image.save(filepath, "PNG");
}
@ -975,10 +964,9 @@ void TilesetEditor::on_actionExport_Primary_Metatiles_Image_triggered()
void TilesetEditor::on_actionExport_Secondary_Metatiles_Image_triggered()
{
QString defaultName = QString("%1_Metatiles").arg(this->secondaryTileset->name);
QString defaultFilepath = QString("%1/%2.png").arg(this->project->importExportPath).arg(defaultName);
QString filepath = QFileDialog::getSaveFileName(this, "Export Secondary Metatiles Image", defaultFilepath, "Image Files (*.png)");
QString defaultFilepath = QString("%1/%2.png").arg(FileDialog::getDirectory()).arg(defaultName);
QString filepath = FileDialog::getSaveFileName(this, "Export Secondary Metatiles Image", defaultFilepath, "Image Files (*.png)");
if (!filepath.isEmpty()) {
this->project->setImportExportPath(filepath);
QImage image = this->metatileSelector->buildSecondaryMetatilesImage();
image.save(filepath, "PNG");
}
@ -998,15 +986,11 @@ void TilesetEditor::importTilesetMetatiles(Tileset *tileset, bool primary)
{
QString descriptorCaps = primary ? "Primary" : "Secondary";
QString filepath = QFileDialog::getOpenFileName(
this,
QString("Import %1 Tileset Metatiles from Advance Map 1.92").arg(descriptorCaps),
this->project->importExportPath,
"Advance Map 1.92 Metatile Files (*.bvd)");
QString filepath = FileDialog::getOpenFileName(this, QString("Import %1 Tileset Metatiles from Advance Map 1.92").arg(descriptorCaps), "", "Advance Map 1.92 Metatile Files (*.bvd)");
if (filepath.isEmpty()) {
return;
}
this->project->setImportExportPath(filepath);
bool error = false;
QList<Metatile*> metatiles = MetatileParser::parse(filepath, &error, primary);
if (error) {

View file

@ -31,6 +31,8 @@ WildMonChart::WildMonChart(QWidget *parent, const EncounterTableModel *table) :
connect(ui->comboBox_Species, &QComboBox::currentTextChanged, this, &WildMonChart::refreshLevelDistributionChart);
connect(ui->comboBox_Group, &QComboBox::currentTextChanged, this, &WildMonChart::refreshLevelDistributionChart);
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &WildMonChart::limitChartAnimation);
// Set up Theme combo box
for (auto i : themes)
ui->comboBox_Theme->addItem(i.first, i.second);
@ -176,14 +178,16 @@ void WildMonChart::refreshSpeciesDistributionChart() {
if (ui->chartView_SpeciesDistribution->chart())
ui->chartView_SpeciesDistribution->chart()->deleteLater();
ui->chartView_SpeciesDistribution->setChart(createSpeciesDistributionChart());
limitChartAnimation(ui->chartView_SpeciesDistribution->chart());
if (ui->tabWidget->currentWidget() == ui->tabSpecies)
limitChartAnimation();
}
void WildMonChart::refreshLevelDistributionChart() {
if (ui->chartView_LevelDistribution->chart())
ui->chartView_LevelDistribution->chart()->deleteLater();
ui->chartView_LevelDistribution->setChart(createLevelDistributionChart());
limitChartAnimation(ui->chartView_LevelDistribution->chart());
if (ui->tabWidget->currentWidget() == ui->tabLevels)
limitChartAnimation();
}
QStringList WildMonChart::getSpeciesNamesAlphabetical() const {
@ -408,17 +412,27 @@ void WildMonChart::applySpeciesColors(const QList<QBarSet*> &barSets) {
set->setColor(this->speciesToColor.value(set->label()));
}
// Turn off the animation once it's played, otherwise it replays any time the window changes size.
void WildMonChart::limitChartAnimation(QChart *chart) {
// Turn off the chart animation once it's played, otherwise it replays any time the window changes size.
// The animation only begins when it's first displayed, so we'll only ever consider the chart for the current tab,
// and when the tab changes we'll call this again.
void WildMonChart::limitChartAnimation() {
// Chart may be destroyed before the animation finishes
QPointer<QChart> safeChart = chart;
QPointer<QChart> chart;
if (ui->tabWidget->currentWidget() == ui->tabSpecies) {
chart = ui->chartView_SpeciesDistribution->chart();
} else if (ui->tabWidget->currentWidget() == ui->tabLevels) {
chart = ui->chartView_LevelDistribution->chart();
}
if (!chart || chart->animationOptions() == QChart::NoAnimation)
return;
// QChart has no signal for when the animation is finished, so we use a timer to stop the animation.
// It is technically possible to get the chart to freeze mid-animation by resizing the window after
// the timer starts but before it finishes, but 1. animations are short so this is difficult to do,
// and 2. the issue resolves if the window is resized afterwards, so it's probably fine.
QTimer::singleShot(chart->animationDuration() + 500, [safeChart] {
if (safeChart) safeChart->setAnimationOptions(QChart::NoAnimation);
QTimer::singleShot(chart->animationDuration(), Qt::PreciseTimer, [chart] {
if (chart) chart->setAnimationOptions(QChart::NoAnimation);
});
}