Remember window sizes, add new map popup window (#54)

* save window state on app close

* new map popup window

* window title, groupItem fix

* fix new map heal location generation

* fix newmappopup form size

* empty map name check, increase max dimensions

* tool tips

* delete new window

* fix mainwindow.ui new map action

* use new config for window state

* clean window restore config settings

* extend "Add New Map" to different MapSortOrders

* fix crash when creating new map from unloaded layout

* fix new map from layout extra files
This commit is contained in:
garak
2019-01-07 18:14:44 -05:00
committed by huderlem
parent aa0709243d
commit d13a4da50a
14 changed files with 736 additions and 31 deletions

View File

@@ -119,6 +119,16 @@ void PorymapConfig::parseConfigKeyValue(QString key, QString value) {
this->mapSortOrder = MapSortOrder::Group;
logWarn(QString("Invalid config value for map_sort_order: '%1'. Must be 'group', 'area', or 'layout'.").arg(value));
}
} else if (key == "window_geometry") {
this->windowGeometry = bytesFromString(value);
} else if (key == "window_state") {
this->windowState = bytesFromString(value);
} else if (key == "map_splitter_state") {
this->mapSplitterState = bytesFromString(value);
} else if (key == "events_splitter_state") {
this->eventsSlpitterState = bytesFromString(value);
} else if (key == "main_splitter_state") {
this->mainSplitterState = bytesFromString(value);
} else if (key == "collision_opacity") {
bool ok;
this->collisionOpacity = qMax(0, qMin(100, value.toInt(&ok)));
@@ -137,10 +147,32 @@ QMap<QString, QString> PorymapConfig::getKeyValueMap() {
map.insert("recent_map", this->recentMap);
map.insert("pretty_cursors", this->prettyCursors ? "1" : "0");
map.insert("map_sort_order", mapSortOrderMap.value(this->mapSortOrder));
map.insert("window_geometry", stringFromByteArray(this->windowGeometry));
map.insert("window_state", stringFromByteArray(this->windowState));
map.insert("map_splitter_state", stringFromByteArray(this->mapSplitterState));
map.insert("events_splitter_state", stringFromByteArray(this->eventsSlpitterState));
map.insert("main_splitter_state", stringFromByteArray(this->mainSplitterState));
map.insert("collision_opacity", QString("%1").arg(this->collisionOpacity));
return map;
}
QString PorymapConfig::stringFromByteArray(QByteArray bytearray) {
QString ret;
for (auto ch : bytearray) {
ret += QString::number(static_cast<int>(ch)) + ":";
}
return ret;
}
QByteArray PorymapConfig::bytesFromString(QString in) {
QByteArray ba;
QStringList split = in.split(":");
for (auto ch : split) {
ba.append(static_cast<char>(ch.toInt()));
}
return ba;
}
void PorymapConfig::setRecentProject(QString project) {
this->recentProject = project;
this->save();
@@ -161,6 +193,16 @@ void PorymapConfig::setPrettyCursors(bool enabled) {
this->save();
}
void PorymapConfig::setGeometry(QByteArray windowGeometry_, QByteArray windowState_, QByteArray mapSplitterState_,
QByteArray eventsSlpitterState_, QByteArray mainSplitterState_) {
this->windowGeometry = windowGeometry_;
this->windowState = windowState_;
this->mapSplitterState = mapSplitterState_;
this->eventsSlpitterState = eventsSlpitterState_;
this->mainSplitterState = mainSplitterState_;
this->save();
}
void PorymapConfig::setCollisionOpacity(int opacity) {
this->collisionOpacity = opacity;
// don't auto-save here because this can be called very frequently.
@@ -182,6 +224,18 @@ bool PorymapConfig::getPrettyCursors() {
return this->prettyCursors;
}
QMap<QString, QByteArray> PorymapConfig::getGeometry() {
QMap<QString, QByteArray> geometry;
geometry.insert("window_geometry", this->windowGeometry);
geometry.insert("window_state", this->windowState);
geometry.insert("map_splitter_state", this->mapSplitterState);
geometry.insert("events_splitter_state", this->eventsSlpitterState);
geometry.insert("main_splitter_state", this->mainSplitterState);
return geometry;
}
int PorymapConfig::getCollisionOpacity() {
return this->collisionOpacity;
}

View File

@@ -1129,6 +1129,9 @@ DraggablePixmapItem* Editor::addNewEvent(QString event_type) {
if (project && map) {
Event *event = Event::createNewEvent(event_type, map->name);
event->put("map_name", map->name);
if (event_type == "event_heal_location") {
event->put("index", project->flyableMaps.length());
}
map->addEvent(event);
project->loadEventPixmaps(map->getAllEvents());
DraggablePixmapItem *object = addMapEvent(event);

View File

@@ -63,6 +63,7 @@ void MainWindow::initWindow() {
this->initEditor();
this->initMiscHeapObjects();
this->initMapSortOrder();
this->restoreWindowState();
}
void MainWindow::initExtraShortcuts() {
@@ -210,6 +211,16 @@ void MainWindow::loadUserSettings() {
ui->horizontalSlider_CollisionTransparency->blockSignals(false);
}
void MainWindow::restoreWindowState() {
logInfo("Restoring window geometry from previous session.");
QMap<QString, QByteArray> geometry = porymapConfig.getGeometry();
this->restoreGeometry(geometry.value("window_geometry"));
this->restoreState(geometry.value("window_state"));
this->ui->splitter_map->restoreState(geometry.value("map_splitter_state"));
this->ui->splitter_events->restoreState(geometry.value("events_splitter_state"));
this->ui->splitter_main->restoreState(geometry.value("main_splitter_state"));
}
bool MainWindow::openRecentProject() {
QString default_dir = porymapConfig.getRecentProject();
if (!default_dir.isNull() && default_dir.length() > 0) {
@@ -722,25 +733,102 @@ void MainWindow::onOpenMapListContextMenu(const QPoint &point)
actions->addAction(menu->addAction("Add New Map to Group"))->setData(groupNum);
connect(actions, SIGNAL(triggered(QAction*)), this, SLOT(onAddNewMapToGroupClick(QAction*)));
menu->exec(QCursor::pos());
} else if (itemType == "map_sec") {
QString secName = selectedItem->data(Qt::UserRole).toString();
QMenu* menu = new QMenu(this);
QActionGroup* actions = new QActionGroup(menu);
actions->addAction(menu->addAction("Add New Map to Area"))->setData(secName);
connect(actions, SIGNAL(triggered(QAction*)), this, SLOT(onAddNewMapToAreaClick(QAction*)));
menu->exec(QCursor::pos());
} else if (itemType == "map_layout") {
QString layoutName = selectedItem->data(Qt::UserRole).toString();
QMenu* menu = new QMenu(this);
QActionGroup* actions = new QActionGroup(menu);
actions->addAction(menu->addAction("Add New Map with Layout"))->setData(layoutName);
connect(actions, SIGNAL(triggered(QAction*)), this, SLOT(onAddNewMapToLayoutClick(QAction*)));
menu->exec(QCursor::pos());
}
}
void MainWindow::onAddNewMapToGroupClick(QAction* triggeredAction)
{
int groupNum = triggeredAction->data().toInt();
QStandardItem* groupItem = mapGroupItemsList->at(groupNum);
openNewMapPopupWindow(MapSortOrder::Group, groupNum);
}
void MainWindow::onAddNewMapToAreaClick(QAction* triggeredAction)
{
QString secName = triggeredAction->data().toString();
openNewMapPopupWindow(MapSortOrder::Area, secName);
}
void MainWindow::onAddNewMapToLayoutClick(QAction* triggeredAction)
{
QString layoutName = triggeredAction->data().toString();
openNewMapPopupWindow(MapSortOrder::Layout, layoutName);
}
void MainWindow::onNewMapCreated() {
QString newMapName = this->newmapprompt->map->name;
int newMapGroup = this->newmapprompt->group;
Map *newMap_ = this->newmapprompt->map;
bool updateLayout = this->newmapprompt->changeLayout;
Map *newMap = editor->project->addNewMapToGroup(newMapName, newMapGroup, newMap_, updateLayout);
logInfo(QString("Created a new map named %1.").arg(newMapName));
QString newMapName = editor->project->getNewMapName();
Map* newMap = editor->project->addNewMapToGroup(newMapName, groupNum);
editor->project->saveMap(newMap);
editor->project->saveAllDataStructures();
QStandardItem* groupItem = mapGroupItemsList->at(newMapGroup);
int numMapsInGroup = groupItem->rowCount();
QStandardItem *newMapItem = createMapItem(newMapName, groupNum, numMapsInGroup);
QStandardItem *newMapItem = createMapItem(newMapName, newMapGroup, numMapsInGroup);
groupItem->appendRow(newMapItem);
mapListIndexes.insert(newMapName, newMapItem->index());
setMap(newMapName);
sortMapList();
setMap(newMapName, true);
if (newMap->isFlyable == "TRUE") {
addNewEvent("event_heal_location");
editor->project->saveHealLocationStruct(newMap);
editor->save();// required
}
disconnect(this->newmapprompt, SIGNAL(applied()), this, SLOT(onNewMapCreated()));
}
void MainWindow::openNewMapPopupWindow(int type, QVariant data) {
if (!this->newmapprompt) {
this->newmapprompt = new NewMapPopup(this, this->editor->project);
}
if (!this->newmapprompt->isVisible()) {
this->newmapprompt->show();
} else {
this->newmapprompt->raise();
this->newmapprompt->activateWindow();
}
switch (type)
{
case MapSortOrder::Group:
this->newmapprompt->init(type, data.toInt(), QString(), QString());
break;
case MapSortOrder::Area:
this->newmapprompt->init(type, 0, data.toString(), QString());
break;
case MapSortOrder::Layout:
this->newmapprompt->init(type, 0, QString(), data.toString());
break;
}
connect(this->newmapprompt, SIGNAL(applied()), this, SLOT(onNewMapCreated()));
connect(this->newmapprompt, &QObject::destroyed, [=](QObject *) { this->newmapprompt = nullptr; });
this->newmapprompt->setAttribute(Qt::WA_DeleteOnClose);
}
void MainWindow::on_action_NewMap_triggered() {
openNewMapPopupWindow(MapSortOrder::Group, 0);
}
void MainWindow::onTilesetChanged(QString mapName)
@@ -1758,6 +1846,13 @@ void MainWindow::on_actionTileset_Editor_triggered()
}
void MainWindow::closeEvent(QCloseEvent *event) {
porymapConfig.setGeometry(
this->saveGeometry(),
this->saveState(),
this->ui->splitter_map->saveState(),
this->ui->splitter_events->saveState(),
this->ui->splitter_main->saveState()
);
porymapConfig.save();
QMainWindow::closeEvent(event);

View File

@@ -850,9 +850,14 @@ void Project::saveMap(Map *map) {
logError(QString("Error: failed to create directory for new map: '%1'").arg(newMapDataDir));
}
QString newLayoutDir = QString(root + "/data/layouts/%1").arg(map->name);
if (!QDir::root().mkdir(newLayoutDir)) {
logError(QString("Error: failed to create directory for new layout: '%1'").arg(newLayoutDir));
if (map->needsLayoutDir) {
QString newLayoutDir = QString(root + "/data/layouts/%1").arg(map->name);
if (!QDir::root().mkdir(newLayoutDir)) {
logError(QString("Error: failed to create directory for new layout: '%1'").arg(newLayoutDir));
}
// Simply append to data/layouts.inc.
QString layout_text = QString("\t.include \"data/layouts/%1/layout.inc\"\n").arg(map->layout->name);
appendTextFile(root + "/data/layouts.inc", layout_text);
}
// TODO: In the future, these files needs more structure to allow for proper parsing/saving.
@@ -875,10 +880,6 @@ void Project::saveMap(Map *map) {
// Simply append to data/maps/headers.inc.
text = QString("\t.include \"data/maps/%1/header.inc\"\n").arg(map->name);
appendTextFile(root + "/data/maps/headers.inc", text);
// Simply append to data/layouts.inc.
text = QString("\t.include \"data/layouts/%1/layout.inc\"\n").arg(map->layout->name);
appendTextFile(root + "/data/layouts.inc", text);
}
saveMapBorder(map);
@@ -1247,6 +1248,50 @@ Map* Project::addNewMapToGroup(QString mapName, int groupNum) {
return map;
}
Map* Project::addNewMapToGroup(QString mapName, int groupNum, Map *newMap, bool updateLayout) {
mapNames->append(mapName);
map_groups->insert(mapName, groupNum);
groupedMapNames[groupNum].append(mapName);
Map *map = new Map;
map = newMap;
map->isPersistedToFile = false;
map->setName(mapName);
mapConstantsToMapNames->insert(map->constantName, map->name);
mapNamesToMapConstants->insert(map->name, map->constantName);
map->events_label = QString("%1_MapEvents").arg(map->name);;
map->scripts_label = QString("%1_MapScripts").arg(map->name);;
map->connections_label = "0x0";
map->song = "MUS_DAN02";
map->requiresFlash = "FALSE";
map->weather = "WEATHER_SUNNY";
map->unknown = "0";
map->show_location = "TRUE";
map->battle_scene = "MAP_BATTLE_SCENE_NORMAL";
if (!updateLayout) {
map->layout_id = QString("%1").arg(mapLayoutsTable.size() + 1);
mapLayouts.insert(map->layout->label, map->layout);
mapLayoutsTable.append(map->layout->label);
setNewMapBlockdata(map);
setNewMapBorder(map);
} else {
map->layout_id = QString("%1").arg(mapLayoutsTable.indexOf(map->layout->label) + 1);
}
loadMapTilesets(map);
setNewMapEvents(map);
setNewMapConnections(map);
map->commit();
map->metatileHistory.save();
return map;
}
QString Project::getNewMapName() {
// Ensure default name doesn't already exist.
int i = 0;

View File

@@ -1,5 +1,6 @@
#include "connectionpixmapitem.h"
#include <cmath>
#include <math.h>
void ConnectionPixmapItem::render(qreal opacity) {
QPixmap newPixmap = this->basePixmap.copy(0, 0, this->basePixmap.width(), this->basePixmap.height());

168
src/ui/newmappopup.cpp Normal file
View File

@@ -0,0 +1,168 @@
#include "newmappopup.h"
#include "event.h"
#include "maplayout.h"
#include "mainwindow.h"
#include "ui_newmappopup.h"
#include "config.h"
#include <QMap>
#include <QSet>
#include <QPalette>
#include <QStringList>
NewMapPopup::NewMapPopup(QWidget *parent, Project *project) :
QMainWindow(parent),
ui(new Ui::NewMapPopup)
{
ui->setupUi(this);
this->project = project;
this->changeLayout = false;
}
NewMapPopup::~NewMapPopup()
{
delete ui;
}
void NewMapPopup::init(int type, int group, QString sec, QString layout) {
switch (type)
{
case MapSortOrder::Group:
setDefaultValues(group, QString());
break;
case MapSortOrder::Area:
setDefaultValues(group, sec);
break;
case MapSortOrder::Layout:
useLayout(layout);
setDefaultValues(group, QString());
break;
}
}
void NewMapPopup::useLayout(QString mapName) {
this->changeLayout = true;
this->layoutName = mapName;
}
void NewMapPopup::setDefaultValues(int groupNum, QString mapSec) {
ui->lineEdit_NewMap_Name->setText(project->getNewMapName());
QMap<QString, QStringList> tilesets = project->getTilesets();
ui->comboBox_NewMap_Primary_Tileset->addItems(tilesets.value("primary"));
ui->comboBox_NewMap_Secondary_Tileset->addItems(tilesets.value("secondary"));
ui->comboBox_NewMap_Group->addItems(*project->groupNames);
ui->comboBox_NewMap_Group->setCurrentText("gMapGroup" + QString::number(groupNum));
if (changeLayout) {
ui->spinBox_NewMap_Width->setValue(project->mapLayouts.value(layoutName)->width.toInt(nullptr, 0));
ui->spinBox_NewMap_Height->setValue(project->mapLayouts.value(layoutName)->height.toInt(nullptr, 0));
ui->comboBox_NewMap_Primary_Tileset->setCurrentText(project->mapLayouts.value(layoutName)->tileset_primary_label);
ui->comboBox_NewMap_Secondary_Tileset->setCurrentText(project->mapLayouts.value(layoutName)->tileset_secondary_label);
ui->spinBox_NewMap_Width->setDisabled(true);
ui->spinBox_NewMap_Height->setDisabled(true);
ui->comboBox_NewMap_Primary_Tileset->setDisabled(true);
ui->comboBox_NewMap_Secondary_Tileset->setDisabled(true);
} else {
ui->spinBox_NewMap_Width->setValue(20);
ui->spinBox_NewMap_Height->setValue(20);
}
ui->comboBox_NewMap_Type->addItems(*project->mapTypes);
ui->comboBox_NewMap_Location->addItems(*project->regionMapSections);
if (!mapSec.isEmpty()) ui->comboBox_NewMap_Location->setCurrentText(mapSec);
ui->frame_NewMap_Options->setEnabled(true);
switch (projectConfig.getBaseGameVersion())
{
case BaseGameVersion::pokeruby:
ui->checkBox_NewMap_Allow_Running->setVisible(false);
ui->checkBox_NewMap_Allow_Biking->setVisible(false);
ui->checkBox_NewMap_Allow_Escape_Rope->setVisible(false);
ui->label_NewMap_Allow_Running->setVisible(false);
ui->label_NewMap_Allow_Biking->setVisible(false);
ui->label_NewMap_Allow_Escape_Rope->setVisible(false);
break;
case BaseGameVersion::pokeemerald:
ui->checkBox_NewMap_Allow_Running->setVisible(true);
ui->checkBox_NewMap_Allow_Biking->setVisible(true);
ui->checkBox_NewMap_Allow_Escape_Rope->setVisible(true);
ui->label_NewMap_Allow_Running->setVisible(true);
ui->label_NewMap_Allow_Biking->setVisible(true);
ui->label_NewMap_Allow_Escape_Rope->setVisible(true);
break;
}
}
void NewMapPopup::on_lineEdit_NewMap_Name_textChanged(const QString &text) {
if (project->mapNames->contains(text)) {
QPalette palette = this->ui->lineEdit_NewMap_Name->palette();
QColor color = Qt::red;
color.setAlpha(25);
palette.setColor(QPalette::Base, color);
this->ui->lineEdit_NewMap_Name->setPalette(palette);
} else {
this->ui->lineEdit_NewMap_Name->setPalette(QPalette());
}
}
void NewMapPopup::on_pushButton_NewMap_Accept_clicked() {
Map *newMap = new Map;
MapLayout *layout = new MapLayout;
// If map name is not unique, use default value. Also use only valid characters.
QString newMapName = this->ui->lineEdit_NewMap_Name->text().remove(QRegularExpression("[^a-zA-Z0-9_]+"));
if (project->mapNames->contains(newMapName) || newMapName.isEmpty()) {
newMapName = project->getNewMapName();
}
newMap->name = newMapName;
newMap->type = this->ui->comboBox_NewMap_Type->currentText();
newMap->location = this->ui->comboBox_NewMap_Location->currentText();
layout->width = QString::number(this->ui->spinBox_NewMap_Width->value());
layout->height = QString::number(this->ui->spinBox_NewMap_Height->value());
layout->tileset_primary_label = this->ui->comboBox_NewMap_Primary_Tileset->currentText();
layout->tileset_secondary_label = this->ui->comboBox_NewMap_Secondary_Tileset->currentText();
layout->border_label = QString("%1_MapBorder").arg(newMap->name);
layout->blockdata_label = QString("%1_MapBlockdata").arg(newMap->name);
if (changeLayout) {
layout->label = layoutName;
layout->name = MapLayout::getNameFromLabel(layout->label);
QString block_path = QString("%1/data/layout/%2/map.bin").arg(project->root).arg(MapLayout::getNameFromLabel(layoutName));
QString border_path = QString("%1/data/layout/%2/border.bin").arg(project->root).arg(MapLayout::getNameFromLabel(layoutName));
layout->blockdata = project->readBlockdata(block_path);
layout->border = project->readBlockdata(border_path);
newMap->needsLayoutDir = false;
} else {
layout->border_path = QString("data/layouts/%1/border.bin").arg(newMap->name);
layout->blockdata_path = QString("data/layouts/%1/map.bin").arg(newMap->name);
layout->label = QString("%1_Layout").arg(newMap->name);
layout->name = MapLayout::getNameFromLabel(layout->label);
}
if (this->ui->checkBox_NewMap_Flyable->isChecked()) {
newMap->isFlyable = "TRUE";
}
if (projectConfig.getBaseGameVersion() == BaseGameVersion::pokeemerald) {
newMap->allowRunning = this->ui->checkBox_NewMap_Allow_Running->isChecked() ? "1" : "0";
newMap->allowBiking = this->ui->checkBox_NewMap_Allow_Biking->isChecked() ? "1" : "0";
newMap->allowEscapeRope = this->ui->checkBox_NewMap_Allow_Escape_Rope->isChecked() ? "1" : "0";
}
group = this->ui->comboBox_NewMap_Group->currentText().remove("gMapGroup").toInt();
newMap->layout = layout;
newMap->layout_label = layout->label;
newMap->group_num = QString::number(group);
map = newMap;
emit applied();
this->close();
}