Open Save in the project folder and offer the last name with a number counted on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Si
2026-09-26 20:24:10 +02:00
co-authored by Claude Opus 5.5
parent ac8b62208c
commit 8537a3cf0a
6 changed files with 68 additions and 2 deletions
+18
View File
@@ -672,4 +672,22 @@ Project loadProject(const std::string& path) {
}
}
std::string nextExportPath(const std::string& previous) {
const std::filesystem::path path(previous);
std::string stem = path.stem().string();
long count = 0;
const auto dash = stem.rfind('-');
if (dash != std::string::npos && dash + 1 < stem.size() && stem.size() - dash <= 10 &&
stem.find_first_not_of("0123456789", dash + 1) == std::string::npos) {
count = std::strtol(stem.c_str() + dash + 1, nullptr, 10);
stem.resize(dash);
}
std::filesystem::path next;
std::error_code error;
do {
next = path.parent_path() / (stem + "-" + std::to_string(++count) + path.extension().string());
} while (std::filesystem::exists(next, error));
return next.string();
}
} // namespace effector
+5
View File
@@ -54,4 +54,9 @@ Project projectFromJson(const std::string& text, const std::string& directory =
void saveProject(const std::string& path, const Project& project);
Project loadProject(const std::string& path);
// The name to offer for the next export after `previous`: its stem with the
// trailing `-N` counted on (`city.png` → `city-1.png`, `city-4.png` →
// `city-5.png`), skipping any that already exist in its folder.
std::string nextExportPath(const std::string& previous);
} // namespace effector
+24 -2
View File
@@ -2476,6 +2476,7 @@ void EffectorWindow::applyProject(const Project& project, const std::string& pat
seedEntry_.set_text(std::to_string(seed_));
projectPath_ = path;
lastExport_.clear();
updateTitle();
clearError();
suspendRender_ = false;
@@ -2801,8 +2802,27 @@ void EffectorWindow::onSave() {
auto dialog = Gtk::FileDialog::create();
dialog->set_title(video ? "Save video" : "Save image");
dialog->set_initial_name("effector-" + chainSlug(stages_, video) + "-" + std::to_string(seed_) +
(video ? ".mp4" : ".png"));
// The project's own folder when it has one, else where the last save went.
std::filesystem::path folder;
if (!projectPath_.empty()) {
folder = std::filesystem::path(projectPath_).parent_path();
} else if (!lastExport_.empty()) {
folder = std::filesystem::path(lastExport_).parent_path();
}
if (!folder.empty()) dialog->set_initial_folder(Gio::File::create_for_path(folder.string()));
// The last save's name counted on, so saving again never offers to
// overwrite it; its extension only when it is the same kind of file.
std::string name;
if (lastExport_.empty()) {
name = "effector-" + chainSlug(stages_, video) + "-" + std::to_string(seed_) +
(video ? ".mp4" : ".png");
} else {
std::filesystem::path last(lastExport_);
if (isVideoPath(last.string()) != video) last.replace_extension(video ? ".mp4" : ".png");
if (!folder.empty()) last = folder / last.filename();
name = std::filesystem::path(nextExportPath(last.string())).filename().string();
}
dialog->set_initial_name(name);
dialog->save(*this, [this, dialog](const Glib::RefPtr<Gio::AsyncResult>& result) {
onSaveChosen(result, dialog);
});
@@ -2838,6 +2858,7 @@ void EffectorWindow::onSaveChosen(const Glib::RefPtr<Gio::AsyncResult>& result,
return;
}
clearError();
lastExport_ = path.string();
status_.set_text("Saved " + path.string());
status_.set_tooltip_text("Saved " + path.string());
}
@@ -2956,6 +2977,7 @@ void EffectorWindow::onExportFinished() {
return;
}
clearError();
lastExport_ = exportPath_;
status_.set_text("Saved " + exportPath_);
status_.set_tooltip_text("Saved " + exportPath_);
}
+3
View File
@@ -206,6 +206,9 @@ private:
Image result_;
std::uint64_t seed_ = 0;
std::string projectPath_; // empty until the project has been saved once
// The last image or video saved from this project, which the next Save
// offers again with its number counted on; empty until one is saved.
std::string lastExport_;
Glib::RefPtr<Gio::Menu> recentMenu_;
sigc::connection recentChanged_; // the recent list outlives the window
bool suspendRender_ = true;