Merge branch 'dev' of https://github.com/huderlem/porymap into metatile-layers

This commit is contained in:
GriffinR 2025-07-22 02:13:19 -04:00
commit c45598f6c4
16 changed files with 194 additions and 80 deletions

View File

@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project somewhat adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The MAJOR version number is bumped when there are **"Breaking Changes"** in the pret projects. For more on this, see [the manual page on breaking changes](https://huderlem.github.io/porymap/manual/breaking-changes.html).
## [Unreleased]
### Added
- Add an option under `Preferences` to include common scripts in the autocomplete for Script labels.
- A link to Porymap's manual is now available under `Help`.
### Changed
- The scroll position of the map view now remains the same between the Connections tab and the Map/Events tabs.
- The Move tool now behaves more like a traditional pan tool (with no momentum).

View File

@ -2926,6 +2926,7 @@
<string>Help</string>
</property>
<addaction name="actionAbout_Porymap"/>
<addaction name="actionOpen_Manual"/>
<addaction name="actionOpen_Log_File"/>
<addaction name="actionOpen_Config_Folder"/>
<addaction name="actionCheck_for_Updates"/>
@ -3267,6 +3268,11 @@
<string>Alt+Right</string>
</property>
</action>
<action name="actionOpen_Manual">
<property name="text">
<string>Open Manual</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -104,7 +104,7 @@
<item row="3" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -159,7 +159,7 @@
<item row="0" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -209,7 +209,7 @@
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -253,13 +253,42 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_AutocompleteAllScripts">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, the list of suggestions when typing in an Event's Script field will include all global script labels in the project. Enabling this setting will make Porymap's startup slower.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Autocomplete Script labels using all possible scripts</string>
<widget class="QGroupBox" name="groupBox_ScriptAutocomplete">
<property name="title">
<string>Script label autocomplete</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QRadioButton" name="radioButton_AutocompleteAllScripts">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, the list of suggestions when typing in an Event's Script field will include all global script labels in the project. This is the slowest option for Porymap's project opening.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>All possible scripts</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_AutocompleteCommonScripts">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, the list of suggestions when typing in an Event's Script field will include script labels from the current map's scripts file, scripts in-use by the map's other events, and all script files in the &lt;span style=&quot; font-family:'SFMono-Regular','Menlo','Monaco','Consolas','Liberation Mono','Courier New','Courier','monospace'; font-size:11px; color:#e74c3c; background-color:#ffffff;&quot;&gt;data_scripts_folders &lt;/span&gt;folder.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Current map, and global script files</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_AutocompleteMapScripts">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If checked, the list of suggestions when typing in an Event's Script field will only include script labels from the current map's scripts file and scripts in-use by the map's other events. This is the fastest option for Porymap's project opening.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Current map only</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
@ -294,7 +323,7 @@
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -323,7 +352,7 @@
<item>
<widget class="QScrollArea" name="scrollArea_TextEditor">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
@ -333,13 +362,13 @@
<rect>
<x>0</x>
<y>0</y>
<width>476</width>
<height>343</height>
<width>495</width>
<height>376</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
<enum>QLayout::SizeConstraint::SetMinimumSize</enum>
</property>
<item row="6" column="0" colspan="2">
<widget class="QLabel" name="label_TextEditorGotoLineHelp">
@ -347,7 +376,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When this command is set a button will appear next to the &lt;span style=&quot; font-weight:600; font-style:italic;&quot;&gt;Script&lt;/span&gt; combo-box in the &lt;span style=&quot; font-weight:600; font-style:italic;&quot;&gt;Events&lt;/span&gt; tab which executes this command.&lt;span style=&quot; font-weight:600;&quot;&gt; %F&lt;/span&gt; will be substituted with the file path of the script and &lt;span style=&quot; font-weight:600;&quot;&gt;%L&lt;/span&gt; will be substituted with the line number of the script in that file. &lt;span style=&quot; font-weight:600;&quot;&gt;%F &lt;/span&gt;&lt;span style=&quot; font-style:italic;&quot;&gt;must&lt;/span&gt; be given if &lt;span style=&quot; font-weight:600;&quot;&gt;%L&lt;/span&gt; is given. If &lt;span style=&quot; font-weight:600;&quot;&gt;%F&lt;/span&gt; is &lt;span style=&quot; font-style:italic;&quot;&gt;not&lt;/span&gt; given then the script's file path will be added to the end of the command. If the script can't be found then the current map's scripts file is opened.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -380,7 +409,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This is the command that is executed when clicking &lt;span style=&quot; font-weight:600; font-style:italic;&quot;&gt;Open Project in Text Editor&lt;/span&gt; in the &lt;span style=&quot; font-weight:600; font-style:italic;&quot;&gt;Tools&lt;/span&gt; menu. &lt;span style=&quot; font-weight:600;&quot;&gt;%D&lt;/span&gt; will be substituted with the project's root directory. If &lt;span style=&quot; font-weight:600;&quot;&gt;%D&lt;/span&gt; is &lt;span style=&quot; font-style:italic;&quot;&gt;not&lt;/span&gt; specified then the project directory will be added to the end of the command.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
@ -410,10 +439,10 @@
<item row="2" column="0" colspan="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -426,7 +455,7 @@
<item row="7" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -447,7 +476,7 @@
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::StandardButton::Apply|QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>

View File

@ -27,6 +27,12 @@ extern const QVersionNumber porymapVersion;
#define CONFIG_BACKWARDS_COMPATABILITY
enum ScriptAutocompleteMode {
MapOnly,
MapAndCommon,
All,
};
class KeyValueConfigBase
{
public:
@ -119,7 +125,7 @@ public:
QString textEditorGotoLine;
int paletteEditorBitDepth;
int projectSettingsTab;
bool loadAllEventScripts;
ScriptAutocompleteMode scriptAutocompleteMode;
bool warpBehaviorWarningDisabled;
bool eventDeleteWarningDisabled;
bool eventOverlayEnabled;

View File

@ -148,7 +148,7 @@ signals:
void modified();
void scriptsModified();
void mapDimensionsChanged(const QSize &size);
void openScriptRequested(QString label);
void openScriptRequested(const QString &label);
void connectionAdded(MapConnection*);
void connectionRemoved(MapConnection*);
void layoutChanged();

View File

@ -209,7 +209,8 @@ public:
public slots:
void openMapScripts() const;
void openScript(const QString &scriptLabel) const;
bool openScript(const QString &scriptLabel) const;
bool openScriptInFile(const QString &scriptLabel, const QString &filepath) const;
void openProjectInTextEditor() const;
void maskNonVisibleConnectionTiles();
void onBorderMetatilesChanged();

View File

@ -290,6 +290,7 @@ private slots:
void on_spinBox_SelectedCollision_valueChanged(int collision);
void on_actionRegion_Map_Editor_triggered();
void on_actionPreferences_triggered();
void on_actionOpen_Manual_triggered();
void on_actionCheck_for_Updates_triggered();
void togglePreferenceSpecificUi();
void on_actionProject_Settings_triggered();

View File

@ -215,7 +215,11 @@ public:
static QString getScriptFileExtension(bool usePoryScript);
QString getScriptDefaultString(bool usePoryScript, QString mapName) const;
QStringList getEventScriptsFilepaths() const;
QStringList getAllEventScriptsFilepaths() const;
QStringList getMapScriptsFilepaths() const;
QStringList getCommonEventScriptsFilepaths() const;
QStringList findScriptsFiles(const QString &searchDir, const QStringList &fileNames = {"*"}) const;
void insertGlobalScriptLabels(QStringList &scriptLabels) const;
QString getDefaultPrimaryTilesetLabel() const;

View File

@ -2,6 +2,7 @@
#define PREFERENCES_H
#include <QMainWindow>
#include "config.h"
class NoScrollComboBox;
class QAbstractButton;
@ -23,7 +24,7 @@ public:
signals:
void preferencesSaved();
void themeChanged(const QString &theme);
void scriptSettingsChanged(bool on);
void scriptSettingsChanged(ScriptAutocompleteMode mode);
void reloadProjectRequested();
private:

View File

@ -355,7 +355,7 @@ void PorymapConfig::reset() {
this->textEditorGotoLine = "";
this->paletteEditorBitDepth = 24;
this->projectSettingsTab = 0;
this->loadAllEventScripts = false;
this->scriptAutocompleteMode = ScriptAutocompleteMode::MapOnly;
this->warpBehaviorWarningDisabled = false;
this->eventDeleteWarningDisabled = false;
this->eventOverlayEnabled = false;
@ -490,8 +490,13 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
}
} else if (key == "project_settings_tab") {
this->projectSettingsTab = getConfigInteger(key, value, 0);
#ifdef CONFIG_BACKWARDS_COMPATABILITY
// Old setting replaced by script_autocomplete_mode
} else if (key == "load_all_event_scripts") {
this->loadAllEventScripts = getConfigBool(key, value);
this->scriptAutocompleteMode = getConfigBool(key, value) ? ScriptAutocompleteMode::All : ScriptAutocompleteMode::MapOnly;
#endif
} else if (key == "script_autocomplete_mode") {
this->scriptAutocompleteMode = static_cast<ScriptAutocompleteMode>(getConfigInteger(key, value, ScriptAutocompleteMode::MapOnly, ScriptAutocompleteMode::All));
} else if (key == "warp_behavior_warning_disabled") {
this->warpBehaviorWarningDisabled = getConfigBool(key, value);
} else if (key == "event_delete_warning_disabled") {
@ -613,7 +618,7 @@ QMap<QString, QString> PorymapConfig::getKeyValueMap() {
map.insert("text_editor_goto_line", this->textEditorGotoLine);
map.insert("palette_editor_bit_depth", QString::number(this->paletteEditorBitDepth));
map.insert("project_settings_tab", QString::number(this->projectSettingsTab));
map.insert("load_all_event_scripts", QString::number(this->loadAllEventScripts));
map.insert("script_autocomplete_mode", QString::number(this->scriptAutocompleteMode));
map.insert("warp_behavior_warning_disabled", QString::number(this->warpBehaviorWarningDisabled));
map.insert("event_delete_warning_disabled", QString::number(this->eventDeleteWarningDisabled));
map.insert("event_overlay_enabled", QString::number(this->eventOverlayEnabled));

View File

@ -2353,21 +2353,29 @@ void Editor::openMapScripts() const {
openInTextEditor(map->getScriptsFilepath());
}
void Editor::openScript(const QString &scriptLabel) const {
bool Editor::openScript(const QString &scriptLabel) const {
// Find the location of scriptLabel.
QStringList scriptPaths(map->getScriptsFilepath());
scriptPaths << project->getEventScriptsFilepaths();
int lineNum = 0;
QString scriptPath = scriptPaths.first();
for (const auto &path : scriptPaths) {
lineNum = ParseUtil::getScriptLineNumber(path, scriptLabel);
if (lineNum != 0) {
scriptPath = path;
break;
}
}
// First, try the current map's scripts file.
if (openScriptInFile(scriptLabel, map->getScriptsFilepath()))
return true;
openInTextEditor(scriptPath, lineNum);
// Script is not in the current map's scripts file.
// Search all possible script files.
const QStringList paths = project->getAllEventScriptsFilepaths();
for (const auto &path : paths) {
if (openScriptInFile(scriptLabel, path))
return true;
}
return false;
}
bool Editor::openScriptInFile(const QString &scriptLabel, const QString &filepath) const {
int lineNum = ParseUtil::getScriptLineNumber(filepath, scriptLabel);
if (lineNum == 0)
return false;
openInTextEditor(filepath, lineNum);
return true;
}
void Editor::openMapJson(const QString &mapName) const {

View File

@ -2908,6 +2908,11 @@ void MainWindow::on_actionOpen_Config_Folder_triggered() {
QDesktopServices::openUrl(QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)));
}
void MainWindow::on_actionOpen_Manual_triggered() {
static const QUrl url("https://huderlem.github.io/porymap/");
QDesktopServices::openUrl(url);
}
void MainWindow::on_actionPreferences_triggered() {
if (!preferenceEditor) {
preferenceEditor = new PreferenceEditor(this);

View File

@ -2969,13 +2969,19 @@ bool Project::readGlobalConstants() {
bool Project::readEventScriptLabels() {
this->globalScriptLabels.clear();
if (porymapConfig.loadAllEventScripts) {
for (const auto &filePath : getEventScriptsFilepaths())
this->globalScriptLabels << ParseUtil::getGlobalScriptLabels(filePath);
this->globalScriptLabels.sort(Qt::CaseInsensitive);
this->globalScriptLabels.removeDuplicates();
QStringList paths;
if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::All) {
paths = getAllEventScriptsFilepaths();
} else if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::MapAndCommon) {
paths = getCommonEventScriptsFilepaths();
}
for (const auto &path : paths) {
this->globalScriptLabels << ParseUtil::getGlobalScriptLabels(path);
}
this->globalScriptLabels.sort(Qt::CaseInsensitive);
this->globalScriptLabels.removeDuplicates();
emit eventScriptLabelsRead();
return true;
}
@ -3011,30 +3017,46 @@ QString Project::getScriptDefaultString(bool usePoryScript, QString mapName) con
return QString("%1_MapScripts::\n\t.byte 0\n").arg(mapName);
}
QStringList Project::getEventScriptsFilepaths() const {
QStringList filePaths(QDir::cleanPath(root + "/" + projectConfig.getFilePath(ProjectFilePath::data_event_scripts)));
const QString scriptsDir = QDir::cleanPath(root + "/" + projectConfig.getFilePath(ProjectFilePath::data_scripts_folders));
const QString mapsDir = QDir::cleanPath(root + "/" + projectConfig.getFilePath(ProjectFilePath::data_map_folders));
QStringList Project::getAllEventScriptsFilepaths() const {
return getMapScriptsFilepaths() + getCommonEventScriptsFilepaths();
}
if (projectConfig.usePoryScript) {
QDirIterator it_pory_shared(scriptsDir, {"*.pory"}, QDir::Files);
while (it_pory_shared.hasNext())
filePaths << it_pory_shared.next();
// Get the paths for all "scripts.inc" / "scripts.pory" files in the data/maps/*/ folders.
QStringList Project::getMapScriptsFilepaths() const {
return findScriptsFiles(projectConfig.getFilePath(ProjectFilePath::data_map_folders), {"scripts"});
}
QDirIterator it_pory_maps(mapsDir, {"scripts.pory"}, QDir::Files, QDirIterator::Subdirectories);
while (it_pory_maps.hasNext())
filePaths << it_pory_maps.next();
// Get the paths for all "*.inc" / "*.pory" files in the data/scripts/ folder, + data/event_scripts.s.
QStringList Project::getCommonEventScriptsFilepaths() const {
QStringList paths = { QDir::cleanPath(this->root + "/" + projectConfig.getFilePath(ProjectFilePath::data_event_scripts)) };
return paths + findScriptsFiles(projectConfig.getFilePath(ProjectFilePath::data_scripts_folders));
}
QStringList Project::findScriptsFiles(const QString &searchDir, const QStringList &fileNames) const {
QStringList paths;
QString dir = searchDir;
if (!dir.startsWith(this->root)) {
dir = QDir::cleanPath(this->root + "/" + dir);
}
QDirIterator it_inc_shared(scriptsDir, {"*.inc"}, QDir::Files);
while (it_inc_shared.hasNext())
filePaths << it_inc_shared.next();
QStringList filters;
for (const auto &s : fileNames) {
filters.append(s + getScriptFileExtension(false));
if (projectConfig.usePoryScript) {
filters.append(s + getScriptFileExtension(true));
}
}
QDirIterator it_inc_maps(mapsDir, {"scripts.inc"}, QDir::Files, QDirIterator::Subdirectories);
while (it_inc_maps.hasNext())
filePaths << it_inc_maps.next();
return filePaths;
// TODO: Filter out .inc files that are generated by a .pory file.
// They won't cause problems for the user, but it'll create extra parsing work later.
if (!filters.isEmpty()) {
QDirIterator it(dir, filters, QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
while (it.hasNext()) {
paths.append(it.next());
}
}
return paths;
}
void Project::loadEventPixmap(Event *event, bool forceLoad) {

View File

@ -201,8 +201,8 @@ void EventFrame::populateScriptDropdown(NoScrollComboBox * combo, Project * proj
QStringList scripts = map->getScriptLabels(this->event->getEventGroup());
populateDropdown(combo, scripts);
// Depending on the settings, the autocomplete may also contain all global scripts.
if (project && porymapConfig.loadAllEventScripts) {
// Depending on the settings, the autocomplete may also contain scripts from outside the map.
if (project && porymapConfig.scriptAutocompleteMode != ScriptAutocompleteMode::MapOnly) {
project->insertGlobalScriptLabels(scripts);
}

View File

@ -1,6 +1,5 @@
#include "preferenceeditor.h"
#include "ui_preferenceeditor.h"
#include "config.h"
#include "noscrollcombobox.h"
#include "message.h"
@ -76,7 +75,14 @@ void PreferenceEditor::updateFields() {
ui->checkBox_OpenRecentProject->setChecked(porymapConfig.reopenOnLaunch);
ui->checkBox_CheckForUpdates->setChecked(porymapConfig.checkForUpdates);
ui->checkBox_DisableEventWarning->setChecked(porymapConfig.eventDeleteWarningDisabled);
ui->checkBox_AutocompleteAllScripts->setChecked(porymapConfig.loadAllEventScripts);
if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::MapOnly) {
ui->radioButton_AutocompleteMapScripts->setChecked(true);
} else if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::MapAndCommon) {
ui->radioButton_AutocompleteCommonScripts->setChecked(true);
} else if (porymapConfig.scriptAutocompleteMode == ScriptAutocompleteMode::All) {
ui->radioButton_AutocompleteAllScripts->setChecked(true);
}
auto logTypeEnd = porymapConfig.statusBarLogTypes.end();
ui->checkBox_StatusErrors->setChecked(porymapConfig.statusBarLogTypes.find(LogType::LOG_ERROR) != logTypeEnd);
@ -95,11 +101,18 @@ void PreferenceEditor::saveFields() {
porymapConfig.theme = themeSelector->currentText();
changedTheme = true;
}
bool loadAllEventScripts = ui->checkBox_AutocompleteAllScripts->isChecked();
if (loadAllEventScripts != porymapConfig.loadAllEventScripts) {
porymapConfig.loadAllEventScripts = loadAllEventScripts;
emit scriptSettingsChanged(loadAllEventScripts);
auto scriptAutocompleteMode = ScriptAutocompleteMode::MapOnly;
if (ui->radioButton_AutocompleteCommonScripts->isChecked()) {
scriptAutocompleteMode = ScriptAutocompleteMode::MapAndCommon;
} else if (ui->radioButton_AutocompleteAllScripts->isChecked()) {
scriptAutocompleteMode = ScriptAutocompleteMode::All;
}
if (scriptAutocompleteMode != porymapConfig.scriptAutocompleteMode) {
porymapConfig.scriptAutocompleteMode = scriptAutocompleteMode;
emit scriptSettingsChanged(scriptAutocompleteMode);
}
porymapConfig.eventSelectionShapeMode = ui->radioButton_OnSprite->isChecked() ? QGraphicsPixmapItem::MaskShape : QGraphicsPixmapItem::BoundingRectShape;
porymapConfig.textEditorOpenFolder = ui->lineEdit_TextEditorOpenFolder->text();
porymapConfig.textEditorGotoLine = ui->lineEdit_TextEditorGotoLine->text();

View File

@ -119,11 +119,16 @@ void UIntSpinBox::onEditFinished() {
// Valid input
newValue = this->valueFromText(input);
} else if (state == QValidator::Intermediate) {
// User has deleted all the number text.
// If they did this by selecting all text and then hitting delete
// make sure to put the cursor back in front of the prefix.
newValue = m_minimum;
this->lineEdit()->setCursorPosition(m_prefix.length());
if (input == m_prefix) {
// User has deleted all the number text.
// If they did this by selecting all text and then hitting delete
// make sure to put the cursor back in front of the prefix.
newValue = m_minimum;
this->lineEdit()->setCursorPosition(m_prefix.length());
} else {
// Other intermediate inputs (values outside of acceptable range) should be ignored.
return;
}
}
if (newValue != m_value) {
m_value = newValue;
@ -167,10 +172,14 @@ QValidator::State UIntSpinBox::validate(QString &input, int &pos) const {
bool ok;
uint32_t num = copy.toUInt(&ok, m_displayIntegerBase);
if (!ok || num < m_minimum || num > m_maximum)
if (!ok)
return QValidator::Invalid;
input += copy.toUpper();
if (num < m_minimum || num > m_maximum)
return QValidator::Intermediate;
return QValidator::Acceptable;
}