mirror of
https://github.com/huderlem/porymap.git
synced 2026-08-20 07:36:31 -05:00
Generalize FieldManager, automatic value validation
This commit is contained in:
309
include/config.h
309
include/config.h
@@ -15,9 +15,9 @@
|
||||
#include <QFontDatabase>
|
||||
#include <QStandardPaths>
|
||||
#include <QColorSpace>
|
||||
#include <set>
|
||||
|
||||
#include "converter.h"
|
||||
#include "fieldmanager.h"
|
||||
#include "events.h"
|
||||
#include "gridsettings.h"
|
||||
#include "scriptsettings.h"
|
||||
@@ -26,11 +26,6 @@
|
||||
#include "orderedset.h"
|
||||
#include "block.h"
|
||||
|
||||
// TODO: Go through and re-test the default window geometries.
|
||||
// TODO: Value validation? Make sure anything no longer validated can't cause problems.
|
||||
// TODO: Documentation
|
||||
// TODO: File splits
|
||||
|
||||
extern const QVersionNumber porymapVersion;
|
||||
|
||||
// Distance in pixels from the edge of a GBA screen (240x160) to the center 16x16 pixels.
|
||||
@@ -58,12 +53,11 @@ public:
|
||||
virtual bool save();
|
||||
|
||||
// Loads the contents of the config from disk.
|
||||
// The file to load from depends on the file name given to
|
||||
// the constructor, and the root given to setRoot, if any.
|
||||
// Returns true if saving was successful, false otherwise.
|
||||
// A successful load includes initializing an empty or non-existing file.
|
||||
virtual bool load();
|
||||
|
||||
virtual QJsonObject toJson() const;
|
||||
virtual QJsonObject toJson();
|
||||
virtual void loadFromJson(const QJsonObject& obj);
|
||||
|
||||
void setRoot(const QString& dir);
|
||||
@@ -74,31 +68,7 @@ protected:
|
||||
virtual void initializeFromEmpty() {};
|
||||
virtual QJsonObject getDefaultJson() const { return QJsonObject(); }
|
||||
|
||||
struct FieldManager {
|
||||
QJsonValue (*get)(const KeyValueConfigBase*);
|
||||
QStringList (*set)(KeyValueConfigBase*, const QJsonValue&);
|
||||
};
|
||||
template <typename ConfigType, auto Member>
|
||||
static FieldManager makeFieldManager() {
|
||||
// Deduce the type from the pointer to the member variable.
|
||||
using T = std::remove_reference_t<decltype(std::declval<ConfigType>().*Member)>;
|
||||
return FieldManager{
|
||||
.get = [](const KeyValueConfigBase* base) -> QJsonValue {
|
||||
auto c = static_cast<const ConfigType*>(base);
|
||||
return Converter<T>::toJson(c->*Member);
|
||||
},
|
||||
.set = [](KeyValueConfigBase* base, const QJsonValue& json) {
|
||||
QStringList errors;
|
||||
const T value = Converter<T>::fromJson(json, &errors);
|
||||
if (errors.isEmpty()) static_cast<ConfigType*>(base)->*Member = value;
|
||||
return errors;
|
||||
}
|
||||
};
|
||||
}
|
||||
virtual const QHash<QString,FieldManager>& registeredFields() const {
|
||||
static QHash<QString,FieldManager> empty;
|
||||
return empty;
|
||||
}
|
||||
virtual FieldManager* getFieldManager() { return nullptr; }
|
||||
|
||||
virtual bool parseJsonKeyValue(const QString& key, const QJsonValue& value);
|
||||
virtual bool parseLegacyKeyValue(const QString& , const QString& ) {return false;}
|
||||
@@ -109,7 +79,6 @@ protected:
|
||||
private:
|
||||
bool loadLegacy();
|
||||
|
||||
// TODO: Make this setting accessible somewhere
|
||||
bool m_saveAllFields = true;
|
||||
};
|
||||
|
||||
@@ -194,86 +163,86 @@ public:
|
||||
// Since the release of the Retina display, Apple products use the Display P3 color space by default.
|
||||
// If we don't use this for exported images (which by default will either have no color space or the sRGB
|
||||
// color space) then they may appear to have different colors than the same image displayed in Porymap.
|
||||
int imageExportColorSpaceId = static_cast<int>(QColorSpace::DisplayP3);
|
||||
std::optional<QColorSpace::NamedColorSpace> imageExportColorSpace = QColorSpace::DisplayP3;
|
||||
#else
|
||||
// As of writing Qt has no way to get a reasonable color space from the user's environment,
|
||||
// so we export images without one and let them handle it.
|
||||
int imageExportColorSpaceId = 0;
|
||||
std::optional<QColorSpace::NamedColorSpace> imageExportColorSpace = {};
|
||||
#endif
|
||||
QMap<QString,QString> trustedScriptHashes;
|
||||
|
||||
#define REGISTER(key, member) {QStringLiteral(key), makeFieldManager<PorymapConfig, &PorymapConfig::member>()}
|
||||
const QHash<QString,FieldManager>& registeredFields() const override {
|
||||
static const QHash<QString,FieldManager> fields = {
|
||||
REGISTER("reopen_on_launch", reopenOnLaunch),
|
||||
REGISTER("project_manually_closed", projectManuallyClosed),
|
||||
REGISTER("map_list_tab", mapListTab),
|
||||
REGISTER("map_list_edit_groups_enabled", mapListEditGroupsEnabled),
|
||||
REGISTER("map_list_tabs_hiding_empty_folders", mapListTabsHidingEmptyFolders),
|
||||
REGISTER("map_list_layouts_sorted", mapListLayoutsSorted),
|
||||
REGISTER("map_list_locations_sorted", mapListLocationsSorted),
|
||||
REGISTER("pretty_cursors", prettyCursors),
|
||||
REGISTER("mirror_connecting_maps", mirrorConnectingMaps),
|
||||
REGISTER("show_dive_emerge_maps", showDiveEmergeMaps),
|
||||
REGISTER("dive_emerge_map_opacity", diveEmergeMapOpacity),
|
||||
REGISTER("dive_map_opacity", diveMapOpacity),
|
||||
REGISTER("emerge_map_opacity", emergeMapOpacity),
|
||||
REGISTER("collision_opacity", collisionOpacity),
|
||||
REGISTER("collision_zoom", collisionZoom),
|
||||
REGISTER("metatiles_zoom", metatilesZoom),
|
||||
REGISTER("tileset_editor_metatiles_zoom", tilesetEditorMetatilesZoom),
|
||||
REGISTER("tileset_editor_tiles_zoom", tilesetEditorTilesZoom),
|
||||
REGISTER("tileset_editor_layer_orientation", tilesetEditorLayerOrientation),
|
||||
REGISTER("show_player_view", showPlayerView),
|
||||
REGISTER("show_cursor_tile", showCursorTile),
|
||||
REGISTER("show_border", showBorder),
|
||||
REGISTER("show_grid", showGrid),
|
||||
REGISTER("show_tileset_editor_metatile_grid", showTilesetEditorMetatileGrid),
|
||||
REGISTER("show_tileset_editor_layer_grid", showTilesetEditorLayerGrid),
|
||||
REGISTER("show_tileset_editor_divider", showTilesetEditorDivider),
|
||||
REGISTER("show_tileset_editor_raw_attributes", showTilesetEditorRawAttributes),
|
||||
REGISTER("show_palette_editor_unused_colors", showPaletteEditorUnusedColors),
|
||||
REGISTER("monitor_files", monitorFiles),
|
||||
REGISTER("tileset_checkerboard_fill", tilesetCheckerboardFill),
|
||||
REGISTER("new_map_header_section_expanded", newMapHeaderSectionExpanded),
|
||||
REGISTER("display_ids_hexadecimal", displayIdsHexadecimal),
|
||||
REGISTER("theme", theme),
|
||||
REGISTER("wild_mon_chart_theme", wildMonChartTheme),
|
||||
REGISTER("text_editor_open_folder", textEditorOpenFolder),
|
||||
REGISTER("text_editor_goto_line", textEditorGotoLine),
|
||||
REGISTER("palette_editor_bit_depth", paletteEditorBitDepth),
|
||||
REGISTER("project_settings_tab", projectSettingsTab),
|
||||
REGISTER("script_autocomplete_mode", scriptAutocompleteMode),
|
||||
REGISTER("warp_behavior_warning_disabled", warpBehaviorWarningDisabled),
|
||||
REGISTER("event_delete_warning_disabled", eventDeleteWarningDisabled),
|
||||
REGISTER("event_overlay_enabled", eventOverlayEnabled),
|
||||
REGISTER("check_for_updates", checkForUpdates),
|
||||
REGISTER("show_project_loading_screen", showProjectLoadingScreen),
|
||||
REGISTER("last_update_check_time", lastUpdateCheckTime),
|
||||
REGISTER("last_update_check_version", lastUpdateCheckVersion),
|
||||
REGISTER("rate_limit_times", rateLimitTimes),
|
||||
REGISTER("event_selection_shape_mode", eventSelectionShapeMode),
|
||||
REGISTER("shown_in_game_reload_message", shownInGameReloadMessage),
|
||||
REGISTER("map_grid", gridSettings),
|
||||
REGISTER("status_bar_log_types", statusBarLogTypes),
|
||||
REGISTER("application_font", applicationFont),
|
||||
REGISTER("map_list_font", mapListFont),
|
||||
REGISTER("image_export_color_space_id", imageExportColorSpaceId),
|
||||
REGISTER("trusted_script_hashes", trustedScriptHashes),
|
||||
FieldManager* getFieldManager() override {
|
||||
if (!m_fm) {
|
||||
m_fm = std::make_shared<FieldManager>();
|
||||
m_fm->addField(&this->reopenOnLaunch, "reopen_on_launch");
|
||||
m_fm->addField(&this->projectManuallyClosed, "project_manually_closed");
|
||||
m_fm->addField(&this->mapListTab, "map_list_tab", 0, 2);
|
||||
m_fm->addField(&this->mapListEditGroupsEnabled, "map_list_edit_groups_enabled");
|
||||
m_fm->addField(&this->mapListTabsHidingEmptyFolders, "map_list_tabs_hiding_empty_folders");
|
||||
m_fm->addField(&this->mapListLayoutsSorted, "map_list_layouts_sorted");
|
||||
m_fm->addField(&this->mapListLocationsSorted, "map_list_locations_sorted");
|
||||
m_fm->addField(&this->prettyCursors, "pretty_cursors");
|
||||
m_fm->addField(&this->mirrorConnectingMaps, "mirror_connecting_maps");
|
||||
m_fm->addField(&this->showDiveEmergeMaps, "show_dive_emerge_maps");
|
||||
m_fm->addField(&this->diveEmergeMapOpacity, "dive_emerge_map_opacity", 10, 90);
|
||||
m_fm->addField(&this->diveMapOpacity, "dive_map_opacity", 10, 90);
|
||||
m_fm->addField(&this->emergeMapOpacity, "emerge_map_opacity", 10, 90);
|
||||
m_fm->addField(&this->collisionOpacity, "collision_opacity", 0, 100);
|
||||
m_fm->addField(&this->collisionZoom, "collision_zoom", 10, 100);
|
||||
m_fm->addField(&this->metatilesZoom, "metatiles_zoom", 10, 100);
|
||||
m_fm->addField(&this->tilesetEditorMetatilesZoom, "tileset_editor_metatiles_zoom", 10, 100);
|
||||
m_fm->addField(&this->tilesetEditorTilesZoom, "tileset_editor_tiles_zoom", 10, 100);
|
||||
m_fm->addField(&this->tilesetEditorLayerOrientation, "tileset_editor_layer_orientation");
|
||||
m_fm->addField(&this->showPlayerView, "show_player_view");
|
||||
m_fm->addField(&this->showCursorTile, "show_cursor_tile");
|
||||
m_fm->addField(&this->showBorder, "show_border");
|
||||
m_fm->addField(&this->showGrid, "show_grid");
|
||||
m_fm->addField(&this->showTilesetEditorMetatileGrid, "show_tileset_editor_metatile_grid");
|
||||
m_fm->addField(&this->showTilesetEditorLayerGrid, "show_tileset_editor_layer_grid");
|
||||
m_fm->addField(&this->showTilesetEditorDivider, "show_tileset_editor_divider");
|
||||
m_fm->addField(&this->showTilesetEditorRawAttributes, "show_tileset_editor_raw_attributes");
|
||||
m_fm->addField(&this->showPaletteEditorUnusedColors, "show_palette_editor_unused_colors");
|
||||
m_fm->addField(&this->monitorFiles, "monitor_files");
|
||||
m_fm->addField(&this->tilesetCheckerboardFill, "tileset_checkerboard_fill");
|
||||
m_fm->addField(&this->newMapHeaderSectionExpanded, "new_map_header_section_expanded");
|
||||
m_fm->addField(&this->displayIdsHexadecimal, "display_ids_hexadecimal");
|
||||
m_fm->addField(&this->theme, "theme");
|
||||
m_fm->addField(&this->wildMonChartTheme, "wild_mon_chart_theme");
|
||||
m_fm->addField(&this->textEditorOpenFolder, "text_editor_open_folder");
|
||||
m_fm->addField(&this->textEditorGotoLine, "text_editor_goto_line");
|
||||
m_fm->addField(&this->paletteEditorBitDepth, "palette_editor_bit_depth", {24,15});
|
||||
m_fm->addField(&this->projectSettingsTab, "project_settings_tab");
|
||||
m_fm->addField(&this->scriptAutocompleteMode, "script_autocomplete_mode");
|
||||
m_fm->addField(&this->warpBehaviorWarningDisabled, "warp_behavior_warning_disabled");
|
||||
m_fm->addField(&this->eventDeleteWarningDisabled, "event_delete_warning_disabled");
|
||||
m_fm->addField(&this->eventOverlayEnabled, "event_overlay_enabled");
|
||||
m_fm->addField(&this->checkForUpdates, "check_for_updates");
|
||||
m_fm->addField(&this->showProjectLoadingScreen, "show_project_loading_screen");
|
||||
m_fm->addField(&this->lastUpdateCheckTime, "last_update_check_time");
|
||||
m_fm->addField(&this->lastUpdateCheckVersion, "last_update_check_version");
|
||||
m_fm->addField(&this->rateLimitTimes, "rate_limit_times");
|
||||
m_fm->addField(&this->eventSelectionShapeMode, "event_selection_shape_mode");
|
||||
m_fm->addField(&this->shownInGameReloadMessage, "shown_in_game_reload_message");
|
||||
m_fm->addField(&this->gridSettings, "map_grid");
|
||||
m_fm->addField(&this->statusBarLogTypes, "status_bar_log_types");
|
||||
m_fm->addField(&this->applicationFont, "application_font");
|
||||
m_fm->addField(&this->mapListFont, "map_list_font");
|
||||
m_fm->addField(&this->imageExportColorSpace, "image_export_color_space");
|
||||
m_fm->addField(&this->trustedScriptHashes, "trusted_script_hashes");
|
||||
|
||||
REGISTER("recent_projects", recentProjects),
|
||||
REGISTER("geometry", savedGeometryMap),
|
||||
REGISTER("geometry_version", geometryVersion),
|
||||
};
|
||||
return fields;
|
||||
m_fm->addField(&this->recentProjects, "recent_projects");
|
||||
m_fm->addField(&this->savedGeometryMap, "geometry");
|
||||
m_fm->addField(&this->geometryVersion, "geometry_version");
|
||||
}
|
||||
return m_fm.get();
|
||||
};
|
||||
#undef REGISTER
|
||||
|
||||
protected:
|
||||
virtual bool parseLegacyKeyValue(const QString& key, const QString& value) override;
|
||||
virtual QJsonObject getDefaultJson() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<FieldManager> m_fm = nullptr;
|
||||
QStringList recentProjects;
|
||||
QMap<QString, QByteArray> savedGeometryMap;
|
||||
int geometryVersion = 0;
|
||||
@@ -470,64 +439,63 @@ public:
|
||||
QMap<QString, QString> pokemonIconPaths;
|
||||
QVersionNumber minimumVersion;
|
||||
|
||||
#define REGISTER(key, member) {QStringLiteral(key), makeFieldManager<ProjectConfig, &ProjectConfig::member>()}
|
||||
const QHash<QString,FieldManager>& registeredFields() const override {
|
||||
static const QHash<QString,FieldManager> fields = {
|
||||
REGISTER("base_game_version", baseGameVersion),
|
||||
REGISTER("use_poryscript", usePoryScript),
|
||||
REGISTER("use_custom_border_size", useCustomBorderSize),
|
||||
REGISTER("enable_event_weather_trigger", eventWeatherTriggerEnabled),
|
||||
REGISTER("enable_event_secret_base", eventSecretBaseEnabled),
|
||||
REGISTER("enable_hidden_item_quantity", hiddenItemQuantityEnabled),
|
||||
REGISTER("enable_hidden_item_requires_itemfinder", hiddenItemRequiresItemfinderEnabled),
|
||||
REGISTER("enable_heal_location_respawn_data", healLocationRespawnDataEnabled),
|
||||
REGISTER("enable_event_clone_object", eventCloneObjectEnabled),
|
||||
REGISTER("enable_floor_number", floorNumberEnabled),
|
||||
REGISTER("create_map_text_file", createMapTextFileEnabled),
|
||||
REGISTER("enable_triple_layer_metatiles", tripleLayerMetatilesEnabled),
|
||||
REGISTER("default_metatile_id", defaultMetatileId),
|
||||
REGISTER("default_elevation", defaultElevation),
|
||||
REGISTER("default_collision", defaultCollision),
|
||||
REGISTER("default_map_size", defaultMapSize),
|
||||
REGISTER("new_map_border_metatiles", newMapBorderMetatileIds),
|
||||
REGISTER("default_primary_tileset", defaultPrimaryTileset),
|
||||
REGISTER("default_secondary_tileset", defaultSecondaryTileset),
|
||||
REGISTER("tilesets_have_callback", tilesetsHaveCallback),
|
||||
REGISTER("tilesets_have_is_compressed", tilesetsHaveIsCompressed),
|
||||
REGISTER("transparency_color", transparencyColor),
|
||||
REGISTER("preserve_matching_only_data", preserveMatchingOnlyData),
|
||||
REGISTER("metatile_attributes_size", metatileAttributesSize),
|
||||
REGISTER("metatile_behavior_mask", metatileBehaviorMask),
|
||||
REGISTER("metatile_terrain_type_mask", metatileTerrainTypeMask),
|
||||
REGISTER("metatile_encounter_type_mask", metatileEncounterTypeMask),
|
||||
REGISTER("metatile_layer_type_mask", metatileLayerTypeMask),
|
||||
REGISTER("block_metatile_id_mask", blockMetatileIdMask),
|
||||
REGISTER("block_collision_mask", blockCollisionMask),
|
||||
REGISTER("block_elevation_mask", blockElevationMask),
|
||||
REGISTER("unused_tile_normal", unusedTileNormal),
|
||||
REGISTER("unused_tile_covered", unusedTileCovered),
|
||||
REGISTER("unused_tile_split", unusedTileSplit),
|
||||
REGISTER("enable_map_allow_flags", mapAllowFlagsEnabled),
|
||||
REGISTER("events_tab_icon_path", eventsTabIconPath),
|
||||
REGISTER("collision_sheet_path", collisionSheetPath),
|
||||
REGISTER("collision_sheet_size", collisionSheetSize),
|
||||
REGISTER("player_view_distance", playerViewDistance),
|
||||
REGISTER("warp_behaviors", warpBehaviors),
|
||||
REGISTER("max_events_per_group", maxEventsPerGroup),
|
||||
REGISTER("metatile_selector_width", metatileSelectorWidth),
|
||||
REGISTER("global_constants_filepaths", globalConstantsFilepaths),
|
||||
REGISTER("global_constants", globalConstants),
|
||||
REGISTER("custom_scripts", customScripts),
|
||||
REGISTER("event_icon_paths", eventIconPaths),
|
||||
REGISTER("pokemon_icon_paths", pokemonIconPaths),
|
||||
REGISTER("minimum_version", minimumVersion),
|
||||
FieldManager* getFieldManager() override {
|
||||
if (!m_fm) {
|
||||
m_fm = std::make_shared<FieldManager>();
|
||||
m_fm->addField(&this->baseGameVersion, "base_game_version");
|
||||
m_fm->addField(&this->usePoryScript, "use_poryscript");
|
||||
m_fm->addField(&this->useCustomBorderSize, "use_custom_border_size");
|
||||
m_fm->addField(&this->eventWeatherTriggerEnabled, "enable_event_weather_trigger");
|
||||
m_fm->addField(&this->eventSecretBaseEnabled, "enable_event_secret_base");
|
||||
m_fm->addField(&this->hiddenItemQuantityEnabled, "enable_hidden_item_quantity");
|
||||
m_fm->addField(&this->hiddenItemRequiresItemfinderEnabled, "enable_hidden_item_requires_itemfinder");
|
||||
m_fm->addField(&this->healLocationRespawnDataEnabled, "enable_heal_location_respawn_data");
|
||||
m_fm->addField(&this->eventCloneObjectEnabled, "enable_event_clone_object");
|
||||
m_fm->addField(&this->floorNumberEnabled, "enable_floor_number");
|
||||
m_fm->addField(&this->createMapTextFileEnabled, "create_map_text_file");
|
||||
m_fm->addField(&this->tripleLayerMetatilesEnabled, "enable_triple_layer_metatiles");
|
||||
m_fm->addField<uint16_t>(&this->defaultMetatileId, "default_metatile_id", 0, Block::MaxValue);
|
||||
m_fm->addField<uint16_t>(&this->defaultElevation, "default_elevation", 0, Block::MaxValue);
|
||||
m_fm->addField<uint16_t>(&this->defaultCollision, "default_collision", 0, Block::MaxValue);
|
||||
m_fm->addField(&this->defaultMapSize, "default_map_size");
|
||||
m_fm->addField(&this->newMapBorderMetatileIds, "new_map_border_metatiles");
|
||||
m_fm->addField(&this->defaultPrimaryTileset, "default_primary_tileset");
|
||||
m_fm->addField(&this->defaultSecondaryTileset, "default_secondary_tileset");
|
||||
m_fm->addField(&this->tilesetsHaveCallback, "tilesets_have_callback");
|
||||
m_fm->addField(&this->tilesetsHaveIsCompressed, "tilesets_have_is_compressed");
|
||||
m_fm->addField(&this->transparencyColor, "transparency_color");
|
||||
m_fm->addField(&this->preserveMatchingOnlyData, "preserve_matching_only_data");
|
||||
m_fm->addField(&this->metatileAttributesSize, "metatile_attributes_size");
|
||||
m_fm->addField(&this->metatileBehaviorMask, "metatile_behavior_mask");
|
||||
m_fm->addField(&this->metatileTerrainTypeMask, "metatile_terrain_type_mask");
|
||||
m_fm->addField(&this->metatileEncounterTypeMask, "metatile_encounter_type_mask");
|
||||
m_fm->addField(&this->metatileLayerTypeMask, "metatile_layer_type_mask");
|
||||
m_fm->addField<uint16_t>(&this->blockMetatileIdMask, "block_metatile_id_mask", 0, Block::MaxValue);
|
||||
m_fm->addField<uint16_t>(&this->blockCollisionMask, "block_collision_mask", 0, Block::MaxValue);
|
||||
m_fm->addField<uint16_t>(&this->blockElevationMask, "block_elevation_mask", 0, Block::MaxValue);
|
||||
m_fm->addField<uint16_t>(&this->unusedTileNormal, "unused_tile_normal", 0, Tile::MaxValue);
|
||||
m_fm->addField<uint16_t>(&this->unusedTileCovered, "unused_tile_covered", 0, Tile::MaxValue);
|
||||
m_fm->addField<uint16_t>(&this->unusedTileSplit, "unused_tile_split", 0, Tile::MaxValue);
|
||||
m_fm->addField(&this->mapAllowFlagsEnabled, "enable_map_allow_flags");
|
||||
m_fm->addField(&this->eventsTabIconPath, "events_tab_icon_path");
|
||||
m_fm->addField(&this->collisionSheetPath, "collision_sheet_path");
|
||||
m_fm->addField(&this->collisionSheetSize, "collision_sheet_size", QSize(1,1), QSize(Block::MaxValue, Block::MaxValue));
|
||||
m_fm->addField(&this->playerViewDistance, "player_view_distance", QMargins(0,0,0,0), QMargins(INT_MAX, INT_MAX, INT_MAX, INT_MAX));
|
||||
m_fm->addField(&this->warpBehaviors, "warp_behaviors");
|
||||
m_fm->addField(&this->maxEventsPerGroup, "max_events_per_group", 1, INT_MAX);
|
||||
m_fm->addField(&this->metatileSelectorWidth, "metatile_selector_width", 1, INT_MAX);
|
||||
m_fm->addField(&this->globalConstantsFilepaths, "global_constants_filepaths");
|
||||
m_fm->addField(&this->globalConstants, "global_constants");
|
||||
m_fm->addField(&this->customScripts, "custom_scripts");
|
||||
m_fm->addField(&this->eventIconPaths, "event_icon_paths");
|
||||
m_fm->addField(&this->pokemonIconPaths, "pokemon_icon_paths");
|
||||
m_fm->addField(&this->minimumVersion, "minimum_version");
|
||||
|
||||
REGISTER("custom_identifiers", identifiers),
|
||||
REGISTER("custom_file_paths", filePaths),
|
||||
};
|
||||
return fields;
|
||||
m_fm->addField(&this->identifiers, "custom_identifiers");
|
||||
m_fm->addField(&this->filePaths, "custom_file_paths");
|
||||
}
|
||||
return m_fm.get();
|
||||
}
|
||||
#undef REGISTER
|
||||
|
||||
protected:
|
||||
virtual bool parseLegacyKeyValue(const QString& key, const QString& value) override;
|
||||
@@ -538,6 +506,7 @@ private:
|
||||
ProjectFilePath reverseDefaultPaths(const QString& str);
|
||||
ProjectIdentifier reverseDefaultIdentifier(const QString& str);
|
||||
|
||||
std::shared_ptr<FieldManager> m_fm = nullptr;
|
||||
QMap<ProjectIdentifier, QString> identifiers;
|
||||
QMap<ProjectFilePath, QString> filePaths;
|
||||
};
|
||||
@@ -565,18 +534,20 @@ protected:
|
||||
virtual bool parseLegacyKeyValue(const QString& key, const QString& value) override;
|
||||
virtual QJsonObject getDefaultJson() const override;
|
||||
|
||||
#define REGISTER(key, member) {QStringLiteral(key), makeFieldManager<UserConfig, &UserConfig::member>()}
|
||||
const QHash<QString,FieldManager>& registeredFields() const override {
|
||||
static const QHash<QString,FieldManager> fields = {
|
||||
REGISTER("recent_map_or_layout", recentMapOrLayout),
|
||||
REGISTER("prefabs_filepath", prefabsFilepath),
|
||||
REGISTER("prefabs_import_prompted", prefabsImportPrompted),
|
||||
REGISTER("use_encounter_json", useEncounterJson),
|
||||
REGISTER("custom_scripts", customScripts),
|
||||
};
|
||||
return fields;
|
||||
FieldManager* getFieldManager() override {
|
||||
if (!m_fm) {
|
||||
m_fm = std::make_shared<FieldManager>();
|
||||
m_fm->addField(&this->recentMapOrLayout, "recent_map_or_layout");
|
||||
m_fm->addField(&this->prefabsFilepath, "prefabs_filepath");
|
||||
m_fm->addField(&this->prefabsImportPrompted, "prefabs_import_prompted");
|
||||
m_fm->addField(&this->useEncounterJson, "use_encounter_json");
|
||||
m_fm->addField(&this->customScripts, "custom_scripts");
|
||||
}
|
||||
return m_fm.get();
|
||||
}
|
||||
#undef REGISTER
|
||||
|
||||
private:
|
||||
std::shared_ptr<FieldManager> m_fm = nullptr;
|
||||
};
|
||||
|
||||
extern UserConfig userConfig;
|
||||
@@ -591,7 +562,7 @@ public:
|
||||
setRoot(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
||||
}
|
||||
|
||||
virtual QJsonObject toJson() const override;
|
||||
virtual QJsonObject toJson() override;
|
||||
virtual void loadFromJson(const QJsonObject& obj) override;
|
||||
|
||||
// Call this before applying user shortcuts so that the user can restore defaults.
|
||||
|
||||
@@ -47,6 +47,8 @@
|
||||
Appropriately implementing 'toString'/'fromString' has the added benefit that your type
|
||||
can automatically be used as a JSON key if it for example appears as the key in a QMap.
|
||||
|
||||
// TODO: Document clamp
|
||||
|
||||
*/
|
||||
|
||||
|
||||
@@ -69,6 +71,19 @@ struct DefaultConverter {
|
||||
// Default to identity
|
||||
static QString toString(const T& value) {return value;}
|
||||
static T fromString(const QString& string, QStringList* = nullptr) {return string;}
|
||||
|
||||
static T clamp(const T& value, const T& min, const T& max, QStringList* errors = nullptr) {
|
||||
Q_ASSERT(min <= max);
|
||||
if (value < min) {
|
||||
if (errors) errors->append("Value too low");
|
||||
return min;
|
||||
}
|
||||
if (value > max) {
|
||||
if (errors) errors->append("Value too high");
|
||||
return max;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
@@ -189,6 +204,17 @@ struct Converter<BaseGame::Version> : DefaultStringConverter<BaseGame::Version>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Converter<std::optional<T>> : DefaultConverter<std::optional<T>> {
|
||||
static QJsonValue toJson(const std::optional<T>& optional) {
|
||||
return optional.has_value() ? Converter<T>::toJson(optional.value()) : QJsonValue();
|
||||
}
|
||||
static std::optional<T> fromJson(const QJsonValue& json, QStringList* errors = nullptr) {
|
||||
if (json.isNull()) return {};
|
||||
return Converter<T>::fromJson(json, errors);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Converter<QList<T>> : DefaultConverter<QList<T>> {
|
||||
static QJsonValue toJson(const QList<T>& list) {
|
||||
@@ -277,6 +303,14 @@ struct Converter<QSize> : DefaultConverter<QSize> {
|
||||
size.setHeight(obj.value("height").toInt());
|
||||
return size;
|
||||
}
|
||||
static QSize clamp(const QSize& value, const QSize& min, const QSize& max, const QStringList* = nullptr) {
|
||||
Q_ASSERT(min.width() <= max.width());
|
||||
Q_ASSERT(min.height() <= max.height());
|
||||
QSize size = value;
|
||||
if (value.width() < min.width() || value.height() < min.height()) size = value.expandedTo(min);
|
||||
if (value.width() > max.width() || value.height() > max.height()) size = value.boundedTo(max);
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
@@ -298,6 +332,18 @@ struct Converter<QMargins> : DefaultConverter<QMargins> {
|
||||
margins.setRight(obj.value("right").toInt());
|
||||
return margins;
|
||||
}
|
||||
static QMargins clamp(const QMargins& value, const QMargins& min, const QMargins& max, const QStringList* = nullptr) {
|
||||
Q_ASSERT(min.left() <= max.left());
|
||||
Q_ASSERT(min.right() <= max.right());
|
||||
Q_ASSERT(min.top() <= max.top());
|
||||
Q_ASSERT(min.bottom() <= max.bottom());
|
||||
QMargins margins = value;
|
||||
margins.setLeft( std::clamp(value.left(), min.left(), max.left()));
|
||||
margins.setRight( std::clamp(value.right(), min.right(), max.right()));
|
||||
margins.setTop( std::clamp(value.top(), min.top(), max.top()));
|
||||
margins.setBottom(std::clamp(value.bottom(), min.bottom(), max.bottom()));
|
||||
return margins;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
|
||||
171
include/core/fieldmanager.h
Normal file
171
include/core/fieldmanager.h
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "converter.h"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// A FieldInterface provides a simple interface for converting a field to/from JSON.
|
||||
// It's constructed with a pointer to some data, and has two functions:
|
||||
// - 'get' returns the pointed-to data, converted to JSON
|
||||
// - 'set' assigns the pointed-to data to a given QJsonValue, with appropriate conversion.
|
||||
// Returns any errors that occur during conversion/assignment.
|
||||
//
|
||||
// A FieldInterface is normally constructed using the 'makeFieldInterface' function:
|
||||
// Example:
|
||||
// int someField = 0;
|
||||
// FieldInterface* fi = makeFieldInterface(&someField);
|
||||
// fi->set(QJsonValue("5")); // someField is now 5
|
||||
//
|
||||
// There are additional implementations of 'makeFieldInterface' that let you specify what valid values are.
|
||||
// Example:
|
||||
// int someField = 0;
|
||||
// int min = 1, max = 4;
|
||||
// FieldInterface* fi = makeFieldInterface(&someField, min, max);
|
||||
// fi->set(QJsonValue("5")); // someField is now 4 (defaults to closest bound), error messages returned
|
||||
// or
|
||||
// QString someField = "";
|
||||
// QList<QString> options = {"hello","hi there"};
|
||||
// FieldInterface* fi = makeFieldInterface(&someField, options);
|
||||
// fi->set(QJsonValue("5")); // someField is now "hello" (defaults to first element), error messages returned
|
||||
|
||||
|
||||
// Base class lets us use the interface without any type information.
|
||||
class FieldInterface {
|
||||
public:
|
||||
FieldInterface(){};
|
||||
virtual ~FieldInterface() {};
|
||||
virtual QJsonValue get() const = 0;
|
||||
virtual QStringList set(const QJsonValue& json) const = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class BasicFieldInterface : public FieldInterface {
|
||||
public:
|
||||
BasicFieldInterface(T* field) : m_field(field) {
|
||||
Q_ASSERT(m_field);
|
||||
};
|
||||
virtual ~BasicFieldInterface() {};
|
||||
virtual QJsonValue get() const override {return Converter<T>::toJson(*m_field);}
|
||||
virtual QStringList set(const QJsonValue& json) const override {
|
||||
QStringList errors;
|
||||
auto value = Converter<T>::fromJson(json, &errors);
|
||||
if (errors.isEmpty()) *m_field = value; // Don't bother changing the value if conversion failed
|
||||
return errors;
|
||||
}
|
||||
protected:
|
||||
T* m_field;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static FieldInterface* makeFieldInterface(T* field) {
|
||||
return new BasicFieldInterface<T>(field);
|
||||
}
|
||||
|
||||
// Create a regular FieldInterface, but override 'set' to use the given min/max.
|
||||
template <typename T>
|
||||
static FieldInterface* makeFieldInterface(T* field, const T& min, const T& max) {
|
||||
class BoundedFieldInterface : public BasicFieldInterface<T> {
|
||||
public:
|
||||
BoundedFieldInterface(T* field, const T& min, const T& max)
|
||||
: BasicFieldInterface<T>(field), m_min(min), m_max(max) {};
|
||||
virtual ~BoundedFieldInterface() {};
|
||||
virtual QStringList set(const QJsonValue& json) const override {
|
||||
QStringList errors;
|
||||
auto value = Converter<T>::fromJson(json, &errors);
|
||||
if (errors.isEmpty()) { // Don't bother changing the value if conversion failed
|
||||
value = Converter<T>::clamp(value, m_min, m_max, &errors);
|
||||
*this->m_field = value;
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
private:
|
||||
const T m_min;
|
||||
const T m_max;
|
||||
};
|
||||
return new BoundedFieldInterface(field, min, max);
|
||||
}
|
||||
|
||||
// Create a regular FieldInterface, but override 'set' to use the given 'acceptableValues'.
|
||||
template <typename T>
|
||||
static FieldInterface* makeFieldInterface(T* field, const QList<T>& acceptableValues) {
|
||||
Q_ASSERT(!acceptableValues.isEmpty());
|
||||
class BoundedFieldInterface : public BasicFieldInterface<T> {
|
||||
public:
|
||||
BoundedFieldInterface(T* field, const QList<T>& acceptableValues)
|
||||
: BasicFieldInterface<T>(field),
|
||||
m_acceptableValues(acceptableValues.begin(), acceptableValues.end()),
|
||||
m_defaultValue(acceptableValues.first()) {};
|
||||
virtual ~BoundedFieldInterface() {};
|
||||
virtual QStringList set(const QJsonValue& json) const override {
|
||||
QStringList errors;
|
||||
auto value = Converter<T>::fromJson(json, &errors);
|
||||
if (errors.isEmpty()) {
|
||||
if (!m_acceptableValues.contains(value)) {
|
||||
value = m_defaultValue;
|
||||
errors.append("Invalid value.");
|
||||
}
|
||||
*this->m_field = value;
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
private:
|
||||
// The order of the list only matters for determining the default value,
|
||||
// so save that separately and convert the list to a set for better lookup speed.
|
||||
const QSet<T> m_acceptableValues;
|
||||
const T m_defaultValue;
|
||||
};
|
||||
return new BoundedFieldInterface(field, acceptableValues);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FieldManager manages a QHash mapping string keys to FieldInterfaces.
|
||||
// This makes it easy to map many fields to/from JSON without explicitly serializing anything.
|
||||
|
||||
class FieldManager {
|
||||
public:
|
||||
~FieldManager(){ clear(); }
|
||||
|
||||
QStringList setField(const QString& key, const QJsonValue& value) const {
|
||||
auto it = m_fields.find(key);
|
||||
return (it != m_fields.end()) ? it.value()->set(value) : QStringList();
|
||||
}
|
||||
|
||||
QJsonValue getField(const QString& key) const {
|
||||
auto it = m_fields.find(key);
|
||||
return (it != m_fields.end()) ? it.value()->get() : QJsonValue();
|
||||
}
|
||||
|
||||
QJsonObject getFields() const {
|
||||
QJsonObject obj;
|
||||
for (auto it = m_fields.constBegin(); it != m_fields.constEnd(); it++) {
|
||||
obj[it.key()] = it.value()->get();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
qDeleteAll(m_fields);
|
||||
m_fields.clear();
|
||||
}
|
||||
|
||||
bool hasField(const QString& key) const {return m_fields.contains(key);}
|
||||
|
||||
template <typename T>
|
||||
void addField(T* field, const QString& key) {
|
||||
Q_ASSERT(!m_fields.contains(key));
|
||||
m_fields.insert(key, makeFieldInterface<T>(field));
|
||||
}
|
||||
template <typename T>
|
||||
void addField(T* field, const QString& key, const T& min, const T& max) {
|
||||
Q_ASSERT(!m_fields.contains(key));
|
||||
m_fields.insert(key, makeFieldInterface<T>(field, min, max));
|
||||
}
|
||||
template <typename T>
|
||||
void addField(T* field, const QString& key, const QList<T>& acceptableValues) {
|
||||
Q_ASSERT(!m_fields.contains(key));
|
||||
m_fields.insert(key, makeFieldInterface<T>(field, acceptableValues));
|
||||
}
|
||||
|
||||
private:
|
||||
QHash<QString, FieldInterface*> m_fields;
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
#define ORDERED_SET_H
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <set>
|
||||
|
||||
template <typename T>
|
||||
class OrderedSet : public std::set<T>
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Util {
|
||||
void setErrorStylesheet(QLineEdit *lineEdit, bool isError);
|
||||
QString toStylesheetString(const QFont &font);
|
||||
void show(QWidget *w);
|
||||
QColorSpace toColorSpace(int colorSpaceInt);
|
||||
QString mkpath(const QString& dirPath);
|
||||
QString getFileHash(const QString &filepath);
|
||||
|
||||
|
||||
@@ -199,24 +199,19 @@ void KeyValueConfigBase::loadFromJson(const QJsonObject& obj) {
|
||||
}
|
||||
|
||||
bool KeyValueConfigBase::parseJsonKeyValue(const QString& key, const QJsonValue& value) {
|
||||
const auto fields = registeredFields();
|
||||
auto it = fields.find(key);
|
||||
if (it == fields.end()) return false;
|
||||
auto fieldManager = getFieldManager();
|
||||
if (!fieldManager || !fieldManager->hasField(key)) return false;
|
||||
|
||||
// Recognized 'key' as a registered field. Let the FieldManager try to assign the value.
|
||||
const QStringList errors = it.value().set(this, value);
|
||||
const QStringList errors = fieldManager->setField(key, value);
|
||||
if (errors.length() == 1) logWarn(QString("Failed to read config key '%1': %2").arg(key).arg(errors.at(0)));
|
||||
else if (errors.length() > 1) logWarn(QString("Failed to read config key '%1':\n%2").arg(key).arg(errors.join("\n")));
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonObject KeyValueConfigBase::toJson() const {
|
||||
QJsonObject obj;
|
||||
const auto fields = registeredFields();
|
||||
for (auto it = fields.begin(); it != fields.end(); it++) {
|
||||
obj[it.key()] = it.value().get(this);
|
||||
}
|
||||
return obj;
|
||||
QJsonObject KeyValueConfigBase::toJson() {
|
||||
auto fieldManager = getFieldManager();
|
||||
return fieldManager ? fieldManager->getFields() : QJsonObject();
|
||||
}
|
||||
|
||||
bool KeyValueConfigBase::save() {
|
||||
@@ -279,6 +274,9 @@ void PorymapConfig::loadFromJson(const QJsonObject& obj) {
|
||||
this->geometryVersion = CurrentGeometryVersion;
|
||||
this->savedGeometryMap.clear();
|
||||
}
|
||||
|
||||
this->gridSettings.offsetX = std::clamp(this->gridSettings.offsetX, 0, 999);
|
||||
this->gridSettings.offsetY = std::clamp(this->gridSettings.offsetY, 0, 999);
|
||||
}
|
||||
|
||||
QJsonObject PorymapConfig::getDefaultJson() const {
|
||||
@@ -606,7 +604,7 @@ void ShortcutsConfig::loadFromJson(const QJsonObject& obj) {
|
||||
this->user_shortcuts = Converter<QMultiMap<QString, QKeySequence>>::fromJson(obj);
|
||||
}
|
||||
|
||||
QJsonObject ShortcutsConfig::toJson() const {
|
||||
QJsonObject ShortcutsConfig::toJson() {
|
||||
return Converter<QMultiMap<QString, QKeySequence>>::toJson(this->user_shortcuts);
|
||||
}
|
||||
|
||||
|
||||
@@ -112,7 +112,6 @@ bool PorymapConfig::parseLegacyKeyValue(const QString &key, const QString &value
|
||||
this->emergeMapOpacity = toInt(value, 10, 90, 15);
|
||||
} else if (key == "collision_opacity") {
|
||||
this->collisionOpacity = toInt(value, 0, 100, 50);
|
||||
} else if (key == "tileset_editor_geometry") {
|
||||
} else if (key == "metatiles_zoom") {
|
||||
this->metatilesZoom = toInt(value, 10, 100, 30);
|
||||
} else if (key == "collision_zoom") {
|
||||
@@ -225,8 +224,6 @@ bool PorymapConfig::parseLegacyKeyValue(const QString &key, const QString &value
|
||||
} else if (key == "map_list_font") {
|
||||
this->mapListFont = QFont();
|
||||
this->mapListFont.fromString(value);
|
||||
} else if (key == "image_export_color_space_id") {
|
||||
this->imageExportColorSpaceId = toInt(value, 0, 8);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -129,26 +129,6 @@ void Util::show(QWidget *w) {
|
||||
}
|
||||
}
|
||||
|
||||
// Safe conversion from an int representing a QColorSpace::NamedColorSpace to a QColorSpace.
|
||||
// This lets us use 0 to mean "no color space".
|
||||
QColorSpace Util::toColorSpace(int colorSpaceInt) {
|
||||
QColorSpace colorSpace;
|
||||
|
||||
int min = static_cast<int>(QColorSpace::SRgb);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
|
||||
// Qt 6.8.0 introduced additional color spaces
|
||||
int max = static_cast<int>(QColorSpace::Bt2100Hlg);
|
||||
#else
|
||||
int max = static_cast<int>(QColorSpace::ProPhotoRgb);
|
||||
#endif
|
||||
|
||||
if (colorSpaceInt >= min && colorSpaceInt <= max) {
|
||||
return QColorSpace(static_cast<QColorSpace::NamedColorSpace>(colorSpaceInt));
|
||||
} else {
|
||||
return QColorSpace();
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a directory named 'dirPath', including any non-existent parent directories. Returns an error message, if any.
|
||||
// If 'dirPath' already exists it's considered an error unless the directory has no files.
|
||||
QString Util::mkpath(const QString& dirPath) {
|
||||
|
||||
@@ -82,7 +82,7 @@ QVersionNumber Project::getMinimumVersion(QString *errorOut) const {
|
||||
|
||||
// No explicitly supported version, we can use the project's git history (if it has one, and we're able to get it) to make a reasonable guess.
|
||||
// We know the hashes of the commits in the base repos that contain breaking changes, so if we find one of these then the project
|
||||
// should support at least up to that Porymap major version. If this fails for any reason it returns a version of -1.
|
||||
// should support at least up to that Porymap major version. If this fails for any reason it returns an empty QVersionNumber.
|
||||
|
||||
// This has relatively tight timeout windows (500ms for each process, compared to the default 30,000ms). This version check
|
||||
// is not important enough to significantly slow down project launch, we'd rather just timeout.
|
||||
|
||||
@@ -40,16 +40,16 @@ bool CustomScriptsListItem::scriptEnabled() const {
|
||||
return ui->checkBox_Enable->isChecked();
|
||||
}
|
||||
|
||||
// TODO: The two functions below should read/write from/to the UI whether the script belongs to the project or user.
|
||||
void CustomScriptsListItem::setSettings(const ScriptSettings& settings) {
|
||||
setPath(settings.path);
|
||||
setScriptEnabled(settings.enabled);
|
||||
// TODO: Read userOnly
|
||||
}
|
||||
|
||||
ScriptSettings CustomScriptsListItem::getSettings() const {
|
||||
return {
|
||||
.path = path(),
|
||||
.enabled = scriptEnabled(),
|
||||
.userOnly = true, // TODO
|
||||
.userOnly = true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,4 +54,4 @@ bool GeometrySaver::eventFilter(QObject *object, QEvent *event) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,7 +565,9 @@ void MapImageExporter::updatePreview(bool forceUpdate) {
|
||||
}
|
||||
progress.close();
|
||||
|
||||
m_previewImage.setColorSpace(Util::toColorSpace(porymapConfig.imageExportColorSpaceId));
|
||||
if (porymapConfig.imageExportColorSpace) {
|
||||
m_previewImage.setColorSpace(QColorSpace(porymapConfig.imageExportColorSpace.value()));
|
||||
}
|
||||
m_preview->setPixmap(QPixmap::fromImage(m_previewImage));
|
||||
m_scene->setSceneRect(m_scene->itemsBoundingRect());
|
||||
scalePreview();
|
||||
|
||||
@@ -276,7 +276,9 @@ void MetatileImageExporter::updatePreview() {
|
||||
m_layerOrder);
|
||||
}
|
||||
|
||||
m_previewImage.setColorSpace(Util::toColorSpace(porymapConfig.imageExportColorSpaceId));
|
||||
if (porymapConfig.imageExportColorSpace) {
|
||||
m_previewImage.setColorSpace(QColorSpace(porymapConfig.imageExportColorSpace.value()));
|
||||
}
|
||||
m_preview->setPixmap(QPixmap::fromImage(m_previewImage));
|
||||
m_scene->setSceneRect(m_scene->itemsBoundingRect());
|
||||
m_previewUpdateQueued = false;
|
||||
|
||||
@@ -81,7 +81,11 @@ void PreferenceEditor::updateFields() {
|
||||
} else if (porymapConfig.eventSelectionShapeMode == QGraphicsPixmapItem::BoundingRectShape) {
|
||||
ui->radioButton_WithinRect->setChecked(true);
|
||||
}
|
||||
ui->comboBox_ColorSpace->setNumberItem(porymapConfig.imageExportColorSpaceId);
|
||||
if (porymapConfig.imageExportColorSpace) {
|
||||
ui->comboBox_ColorSpace->setNumberItem(porymapConfig.imageExportColorSpace.value());
|
||||
} else {
|
||||
ui->comboBox_ColorSpace->setNumberItem(0);
|
||||
}
|
||||
ui->lineEdit_TextEditorOpenFolder->setText(porymapConfig.textEditorOpenFolder);
|
||||
ui->lineEdit_TextEditorGotoLine->setText(porymapConfig.textEditorGotoLine);
|
||||
ui->checkBox_MonitorProjectFiles->setChecked(porymapConfig.monitorFiles);
|
||||
@@ -133,7 +137,12 @@ void PreferenceEditor::saveFields() {
|
||||
emit scriptSettingsChanged(scriptAutocompleteMode);
|
||||
}
|
||||
|
||||
porymapConfig.imageExportColorSpaceId = ui->comboBox_ColorSpace->currentData().toInt();
|
||||
auto colorSpace = magic_enum::enum_cast<QColorSpace::NamedColorSpace>(ui->comboBox_ColorSpace->currentData().toInt());
|
||||
if (colorSpace.has_value()) {
|
||||
porymapConfig.imageExportColorSpace = colorSpace.value();
|
||||
} else {
|
||||
porymapConfig.imageExportColorSpace.reset();
|
||||
}
|
||||
porymapConfig.eventSelectionShapeMode = ui->radioButton_OnSprite->isChecked() ? QGraphicsPixmapItem::MaskShape : QGraphicsPixmapItem::BoundingRectShape;
|
||||
porymapConfig.textEditorOpenFolder = ui->lineEdit_TextEditorOpenFolder->text();
|
||||
porymapConfig.textEditorGotoLine = ui->lineEdit_TextEditorGotoLine->text();
|
||||
|
||||
Reference in New Issue
Block a user