@@ -945,6 +945,10 @@ MainWindow::MainWindow(QWidget* parent)
945945 auto* openAction = new QAction(QIcon(":/icons/open.png"), "&Open...", this);
946946 auto* saveAction = new QAction(QIcon(":/icons/save.png"), "&Save", this);
947947 auto* saveAsAction = new QAction("Save &As...", this);
948+ saveAction->setShortcut(QKeySequence::Save);
949+ saveAction->setShortcutContext(Qt::ApplicationShortcut);
950+ saveAsAction->setShortcut(QKeySequence::SaveAs);
951+ saveAsAction->setShortcutContext(Qt::ApplicationShortcut);
948952 auto* importImageAction = new QAction(QIcon(":/icons/import.png"), "Import &Image...", this);
949953 auto* exitAction = new QAction("E&xit", this);
950954 auto* addFrameAction = new QAction(QIcon(":/icons/add.png"), "Add &Frame", this);
@@ -1165,10 +1169,16 @@ MainWindow::MainWindow(QWidget* parent)
11651169 updateMetadataForFrame(-1);
11661170 updateMetadataForSprite(-1);
11671171 populateBookmarks({}, {});
1172+ m_hasLegacyRoundTrip = false;
1173+ m_legacyRoundTrip = LegacyRoundTripData{};
11681174 statusBar()->showMessage("New project (stub)", 3000);
11691175 });
11701176 connect(openAction, &QAction::triggered, this, [this]() {
1171- const QString filename = QFileDialog::getOpenFileName(this, "Open Project", QString(), "Serum Projects (*.crom *.cROM *.crp *.cRP);;All Files (*.*)");
1177+ const QString filename = QFileDialog::getOpenFileName(
1178+ this,
1179+ "Open Project",
1180+ QString(),
1181+ "Serum Projects (*.crom *.cROM *.crp *.cRP);;All Files (*.*)");
11721182 if (!filename.isEmpty()) {
11731183 openProjectFile(filename);
11741184 }
@@ -1188,12 +1198,21 @@ MainWindow::MainWindow(QWidget* parent)
11881198 }
11891199 });
11901200 connect(saveAsAction, &QAction::triggered, this, [this]() {
1191- const QString filename = QFileDialog::getSaveFileName(this, "Save Project As", QString(), "Serum Projects (*.crom *.cROM *.crp *.cRP)");
1201+ const QString filename = QFileDialog::getSaveFileName(
1202+ this,
1203+ "Save Project As",
1204+ QString(),
1205+ "Serum Projects (*.crom *.cROM *.crp *.cRP)");
1206+ if (filename.isEmpty()) {
1207+ return;
1208+ }
11921209 if (filename.isEmpty()) {
11931210 return;
11941211 }
11951212 if (saveProjectToPath(filename)) {
1196- statusBar()->showMessage(QString("Save As: %1").arg(m_state->projectPath()), 5000);
1213+ updateWindowTitle();
1214+ persistRecentFiles();
1215+ statusBar()->showMessage(QString("Save As: %1").arg(filename), 5000);
11971216 } else {
11981217 statusBar()->showMessage("Save failed", 5000);
11991218 }
@@ -4420,6 +4439,8 @@ void MainWindow::openProjectFile(const QString& filename)
44204439 updateMetadataForFrame(-1);
44214440 updateMetadataForSprite(-1);
44224441 populateBookmarks({}, {});
4442+ m_hasLegacyRoundTrip = false;
4443+ m_legacyRoundTrip = LegacyRoundTripData{};
44234444 if (LoadProjectJson(*m_state, filename, &error)) {
44244445 resetUndoStacks();
44254446 resetNavigationHistory();
@@ -4459,6 +4480,33 @@ void MainWindow::openProjectFile(const QString& filename)
44594480 statusBar()->showMessage(QString("Open failed: %1").arg(QString::fromStdString(error)), 5000);
44604481 return;
44614482 }
4483+ m_hasLegacyRoundTrip = true;
4484+ m_legacyRoundTrip = LegacyRoundTripData{};
4485+ m_legacyRoundTrip.name = legacy.name;
4486+ m_legacyRoundTrip.frame_width_x = legacy.frame_width_x;
4487+ m_legacyRoundTrip.frame_height_x = legacy.frame_height_x;
4488+ m_legacyRoundTrip.hash_codes = legacy.hash_codes;
4489+ m_legacyRoundTrip.active_frames = legacy.active_frames;
4490+ m_legacyRoundTrip.trigger_ids = legacy.trigger_ids;
4491+ m_legacyRoundTrip.dynashadow_dir = legacy.dynashadow_dir;
4492+ m_legacyRoundTrip.dynashadow_col = legacy.dynashadow_col;
4493+ m_legacyRoundTrip.dynashadow_dir_x = legacy.dynashadow_dir_x;
4494+ m_legacyRoundTrip.dynashadow_col_x = legacy.dynashadow_col_x;
4495+ m_legacyRoundTrip.active_col_sets = legacy.active_col_sets;
4496+ m_legacyRoundTrip.mask_names = legacy.mask_names;
4497+ m_legacyRoundTrip.draw_col_mode = legacy.draw_col_mode;
4498+ m_legacyRoundTrip.draw_mode = legacy.draw_mode;
4499+ m_legacyRoundTrip.mask_sel_mode = legacy.mask_sel_mode;
4500+ m_legacyRoundTrip.fill_mode = legacy.fill_mode;
4501+ m_legacyRoundTrip.edit_colors = legacy.edit_colors;
4502+ m_legacyRoundTrip.n_image_pos_saves = legacy.n_image_pos_saves;
4503+ m_legacyRoundTrip.image_pos_names = legacy.image_pos_names;
4504+ m_legacyRoundTrip.image_pos_data = legacy.image_pos_data;
4505+ m_legacyRoundTrip.is_imported = legacy.is_imported;
4506+ m_legacyRoundTrip.time_elapsed = legacy.time_elapsed;
4507+ m_legacyRoundTrip.is_pup_pack = legacy.is_pup_pack;
4508+ m_legacyRoundTrip.pup_pack = legacy.pup_pack;
4509+ m_legacyRoundTrip.preview_reduced_palette = legacy.preview_reduced_palette;
44624510
44634511 m_imageStore->clear();
44644512 m_frameStore->clear();
@@ -4640,6 +4688,8 @@ void MainWindow::openProjectFile(const QString& filename)
46404688 updateMetadataForFrame(-1);
46414689 updateMetadataForSprite(-1);
46424690 populateBookmarks({}, {});
4691+ m_hasLegacyRoundTrip = false;
4692+ m_legacyRoundTrip = LegacyRoundTripData{};
46434693 if (LoadProjectJson(*m_state, filename, &error)) {
46444694 statusBar()->showMessage(QString("Open: %1").arg(filename), 5000);
46454695 persistRecentFiles();
@@ -4703,8 +4753,42 @@ LegacyProject MainWindow::buildLegacyProject(const QString& baseName) const
47034753{
47044754 LegacyProject project;
47054755 project.name = baseName.toStdString();
4756+ if (m_hasLegacyRoundTrip) {
4757+ if (!m_legacyRoundTrip.name.empty()) {
4758+ project.name = m_legacyRoundTrip.name;
4759+ }
4760+ project.frame_width_x = m_legacyRoundTrip.frame_width_x;
4761+ project.frame_height_x = m_legacyRoundTrip.frame_height_x;
4762+ project.hash_codes = m_legacyRoundTrip.hash_codes;
4763+ project.active_frames = m_legacyRoundTrip.active_frames;
4764+ project.trigger_ids = m_legacyRoundTrip.trigger_ids;
4765+ project.dynashadow_dir = m_legacyRoundTrip.dynashadow_dir;
4766+ project.dynashadow_col = m_legacyRoundTrip.dynashadow_col;
4767+ project.dynashadow_dir_x = m_legacyRoundTrip.dynashadow_dir_x;
4768+ project.dynashadow_col_x = m_legacyRoundTrip.dynashadow_col_x;
4769+ project.active_col_sets = m_legacyRoundTrip.active_col_sets;
4770+ project.mask_names = m_legacyRoundTrip.mask_names;
4771+ project.draw_col_mode = m_legacyRoundTrip.draw_col_mode;
4772+ project.draw_mode = m_legacyRoundTrip.draw_mode;
4773+ project.mask_sel_mode = m_legacyRoundTrip.mask_sel_mode;
4774+ project.fill_mode = m_legacyRoundTrip.fill_mode;
4775+ project.edit_colors = m_legacyRoundTrip.edit_colors;
4776+ project.n_image_pos_saves = m_legacyRoundTrip.n_image_pos_saves;
4777+ project.image_pos_names = m_legacyRoundTrip.image_pos_names;
4778+ project.image_pos_data = m_legacyRoundTrip.image_pos_data;
4779+ project.is_imported = m_legacyRoundTrip.is_imported;
4780+ project.time_elapsed = m_legacyRoundTrip.time_elapsed;
4781+ project.is_pup_pack = m_legacyRoundTrip.is_pup_pack;
4782+ project.pup_pack = m_legacyRoundTrip.pup_pack;
4783+ project.preview_reduced_palette = m_legacyRoundTrip.preview_reduced_palette;
4784+ }
47064785
47074786 const int frameCount = m_frameStore->count();
4787+ if (m_hasLegacyRoundTrip) {
4788+ project.hash_codes.resize(static_cast<std::size_t>(frameCount), 0);
4789+ project.active_frames.resize(static_cast<std::size_t>(frameCount), 0);
4790+ project.trigger_ids.resize(static_cast<std::size_t>(frameCount), 0xffffffffu);
4791+ }
47084792 project.frames.reserve(static_cast<std::size_t>(frameCount));
47094793 for (int i = 0; i < frameCount; ++i) {
47104794 if (const cv::Mat* frame = m_frameStore->at(i)) {
@@ -4783,7 +4867,11 @@ LegacyProject MainWindow::buildLegacyProject(const QString& baseName) const
47834867 project.background_masks = m_frameBackgroundMasks;
47844868 project.background_masks_x = m_frameBackgroundMasksX;
47854869 project.active_reduced_palette = static_cast<uint8_t>(std::max(0, std::min(m_reducedPaletteIndex, kReducedPaletteCount - 1)));
4786- project.preview_reduced_palette = project.active_reduced_palette;
4870+ if (!m_hasLegacyRoundTrip) {
4871+ project.preview_reduced_palette = project.active_reduced_palette;
4872+ } else if (project.preview_reduced_palette >= kReducedPaletteCount) {
4873+ project.preview_reduced_palette = project.active_reduced_palette;
4874+ }
47874875 project.reduced_palettes = m_reducedPaletteIndices;
47884876 project.reduced_palette_names = m_reducedPaletteNames;
47894877 project.palettes.clear();
@@ -12438,7 +12526,28 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event)
1243812526 pos = m_framePreviewList->viewport()->mapFrom(m_framePreviewList, pos);
1243912527 }
1244012528 QListWidgetItem* hit = m_framePreviewList->itemAt(pos);
12441- if (!hit) {
12529+ bool hitContent = false;
12530+ if (hit) {
12531+ const QRect itemRect = m_framePreviewList->visualItemRect(hit);
12532+ const QFontMetrics metrics(m_framePreviewList->font());
12533+ const int padding = 6;
12534+ const int textHeight = metrics.height() + 4;
12535+ QRect contentRect = itemRect.adjusted(padding, padding, -padding, -padding);
12536+ const QVariant sizeData = hit->data(kPreviewIconSizeRole);
12537+ const QSize iconSize = sizeData.isValid() && sizeData.toSize().isValid()
12538+ ? sizeData.toSize()
12539+ : m_framePreviewList->iconSize().isValid()
12540+ ? m_framePreviewList->iconSize()
12541+ : QSize(kPreviewIconWidth, kPreviewIconHeight);
12542+ const int iconX = contentRect.left() + (contentRect.width() - iconSize.width()) / 2;
12543+ QRect iconRect(iconX,
12544+ contentRect.top() + textHeight + 2,
12545+ iconSize.width(),
12546+ contentRect.height() - textHeight - 2);
12547+ QRect textRect(iconRect.left(), contentRect.top(), iconRect.width(), textHeight);
12548+ hitContent = iconRect.contains(pos) || textRect.contains(pos);
12549+ }
12550+ if (!hit || !hitContent) {
1244212551 clearPreviewSelection();
1244312552 return true;
1244412553 }
0 commit comments