2021-12-31 14:59:39 +01:00
|
|
|
#include "editor_state.hpp"
|
|
|
|
|
2022-01-06 22:38:51 +01:00
|
|
|
#include <algorithm>
|
2019-01-17 01:08:38 +01:00
|
|
|
#include <cmath>
|
2019-01-12 03:13:30 +01:00
|
|
|
#include <filesystem>
|
2019-01-12 17:16:20 +01:00
|
|
|
#include <imgui-SFML.h>
|
2021-12-31 14:59:39 +01:00
|
|
|
#include <imgui.h>
|
2019-01-16 01:59:02 +01:00
|
|
|
#include <imgui_internal.h>
|
2021-12-31 14:59:39 +01:00
|
|
|
#include <imgui_stdlib.h>
|
2021-12-31 00:57:06 +01:00
|
|
|
#include <tinyfiledialogs.h>
|
2019-01-03 23:20:35 +01:00
|
|
|
|
2022-01-04 01:31:17 +01:00
|
|
|
EditorState::EditorState(Fumen& fumen, std::filesystem::path assets) :
|
|
|
|
fumen(fumen),
|
|
|
|
playfield(assets),
|
|
|
|
linearView(assets)
|
|
|
|
{
|
|
|
|
reloadFromFumen(assets);
|
2019-01-13 03:53:42 +01:00
|
|
|
}
|
|
|
|
|
2022-01-04 01:31:17 +01:00
|
|
|
void EditorState::reloadFromFumen(std::filesystem::path assets) {
|
2019-01-03 23:20:35 +01:00
|
|
|
if (not this->fumen.Charts.empty()) {
|
2022-01-04 01:31:17 +01:00
|
|
|
this->chart.emplace(this->fumen.Charts.begin()->second, assets);
|
2019-01-16 19:12:01 +01:00
|
|
|
} else {
|
2019-03-02 13:47:26 +01:00
|
|
|
this->chart.reset();
|
2019-01-03 23:20:35 +01:00
|
|
|
}
|
2019-01-12 17:16:20 +01:00
|
|
|
reloadMusic();
|
2019-03-24 15:13:13 +01:00
|
|
|
reloadAlbumCover();
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reloads music from what's indicated in the "music path" field of the fumen
|
|
|
|
* Resets the music state in case anything fails
|
2019-03-27 14:41:16 +01:00
|
|
|
* Updates playbackPosition and previewEnd as well
|
2019-01-12 17:16:20 +01:00
|
|
|
*/
|
|
|
|
void EditorState::reloadMusic() {
|
2022-02-28 23:10:22 +01:00
|
|
|
const auto music_path = std::filesystem::path(fumen.path).parent_path() / fumen.musicPath;
|
|
|
|
try {
|
|
|
|
music.emplace(music_path);
|
|
|
|
} catch (const std::exception& e) {
|
2019-01-12 17:16:20 +01:00
|
|
|
music.reset();
|
2019-01-12 03:13:30 +01:00
|
|
|
}
|
2022-02-28 23:10:22 +01:00
|
|
|
|
2022-01-06 22:38:51 +01:00
|
|
|
reloadPreviewEnd();
|
2019-01-14 04:20:30 +01:00
|
|
|
|
2022-01-06 22:38:51 +01:00
|
|
|
auto seconds_position = std::clamp(playbackPosition.asSeconds(), -(fumen.offset), previewEnd.asSeconds());
|
|
|
|
playbackPosition = sf::seconds(seconds_position);
|
2019-01-17 03:02:37 +01:00
|
|
|
previousPos = playbackPosition;
|
2019-04-09 23:07:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::reloadPreviewEnd() {
|
2022-01-06 22:45:41 +01:00
|
|
|
auto old_previewEnd = previewEnd;
|
2022-01-05 12:44:56 +01:00
|
|
|
float music_duration = 0;
|
2019-01-14 04:20:30 +01:00
|
|
|
if (music) {
|
2022-01-05 12:44:56 +01:00
|
|
|
music_duration = music->getDuration().asSeconds();
|
|
|
|
}
|
|
|
|
|
|
|
|
float chart_runtime = 0;
|
|
|
|
if (chart) {
|
|
|
|
chart_runtime = fumen.getChartRuntime(chart->ref);
|
2019-01-14 04:20:30 +01:00
|
|
|
}
|
2022-01-05 12:44:56 +01:00
|
|
|
|
|
|
|
// Chart end in seconds using the music file "coordinate system"
|
|
|
|
// (beat 0 is at -offset seconds)
|
|
|
|
float chart_end = std::max(chart_runtime - fumen.offset, 0.f);
|
|
|
|
float preview_end_seconds = std::max(music_duration, chart_end) ;
|
|
|
|
|
|
|
|
// Add some extra time at the end to allow for more notes to be placed
|
|
|
|
// after the end of the chart
|
|
|
|
// TODO: is this really the way to do it ?
|
|
|
|
preview_end_seconds += 2.f;
|
|
|
|
previewEnd = sf::seconds(preview_end_seconds);
|
2022-01-06 22:45:41 +01:00
|
|
|
if (old_previewEnd != previewEnd and chart) {
|
|
|
|
chart->densityGraph.should_recompute = true;
|
|
|
|
}
|
2019-01-12 03:13:30 +01:00
|
|
|
}
|
|
|
|
|
2019-01-12 17:16:20 +01:00
|
|
|
/*
|
2021-12-31 14:59:39 +01:00
|
|
|
* Reloads the album cover from what's indicated in the "album cover path" field
|
|
|
|
* of the fumen Resets the album cover state if anything fails
|
2019-01-12 17:16:20 +01:00
|
|
|
*/
|
2019-03-24 15:13:13 +01:00
|
|
|
void EditorState::reloadAlbumCover() {
|
|
|
|
albumCover.emplace();
|
2019-03-27 14:41:16 +01:00
|
|
|
|
2021-12-31 14:59:39 +01:00
|
|
|
std::filesystem::path album_cover_path =
|
|
|
|
std::filesystem::path(fumen.path).parent_path() / fumen.albumCoverPath;
|
2019-03-27 14:41:16 +01:00
|
|
|
|
2021-12-31 14:59:39 +01:00
|
|
|
if (fumen.albumCoverPath.empty() or not std::filesystem::exists(album_cover_path)
|
|
|
|
or not albumCover->loadFromFile(album_cover_path.string())) {
|
2019-03-24 15:13:13 +01:00
|
|
|
albumCover.reset();
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-17 01:08:38 +01:00
|
|
|
void EditorState::setPlaybackAndMusicPosition(sf::Time newPosition) {
|
2019-04-09 23:07:22 +02:00
|
|
|
reloadPreviewEnd();
|
|
|
|
|
2022-01-06 22:38:51 +01:00
|
|
|
newPosition = sf::seconds(
|
|
|
|
std::clamp(
|
|
|
|
newPosition.asSeconds(),
|
|
|
|
-fumen.offset,
|
|
|
|
previewEnd.asSeconds()
|
|
|
|
)
|
|
|
|
);
|
2021-12-31 14:59:39 +01:00
|
|
|
previousPos = sf::seconds(newPosition.asSeconds() - 1.f / 60.f);
|
2019-01-17 01:08:38 +01:00
|
|
|
playbackPosition = newPosition;
|
|
|
|
if (music) {
|
|
|
|
if (playbackPosition.asSeconds() >= 0 and playbackPosition < music->getDuration()) {
|
|
|
|
music->setPlayingOffset(playbackPosition);
|
2019-03-24 21:51:33 +01:00
|
|
|
} else {
|
|
|
|
music->stop();
|
2019-01-17 01:08:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-17 15:37:15 +01:00
|
|
|
void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndingState) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_Once);
|
|
|
|
ImGui::SetNextWindowSizeConstraints(
|
|
|
|
ImVec2(0, 0),
|
|
|
|
ImVec2(FLT_MAX, FLT_MAX),
|
|
|
|
Toolbox::CustomConstraints::ContentSquare);
|
2019-01-14 21:43:56 +01:00
|
|
|
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::Begin("Playfield", &showPlayfield, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
2022-01-07 00:14:38 +01:00
|
|
|
if (not ImGui::IsWindowHovered() and chart and chart->creatingLongNote) {
|
|
|
|
// cancel long note creation if the mouse is or goes out of the playfield
|
|
|
|
chart->longNoteBeingCreated.reset();
|
|
|
|
chart->creatingLongNote = false;
|
|
|
|
}
|
2019-01-14 21:43:56 +01:00
|
|
|
float squareSize = ImGui::GetWindowSize().x / 4.f;
|
|
|
|
float TitlebarHeight = ImGui::GetWindowSize().y - ImGui::GetWindowSize().x;
|
2019-03-24 21:51:33 +01:00
|
|
|
int ImGuiIndex = 0;
|
2019-02-13 00:44:46 +01:00
|
|
|
|
2019-03-02 13:47:26 +01:00
|
|
|
if (chart) {
|
2019-04-04 00:26:38 +02:00
|
|
|
playfield.resize(static_cast<unsigned int>(ImGui::GetWindowSize().x));
|
|
|
|
|
2021-12-31 14:59:39 +01:00
|
|
|
auto longNoteDummy =
|
|
|
|
chart->makeLongNoteDummy(static_cast<int>(roundf(getCurrentTick())));
|
2019-03-28 02:16:29 +01:00
|
|
|
if (longNoteDummy) {
|
2021-12-31 14:59:39 +01:00
|
|
|
playfield.drawLongNote(
|
|
|
|
*longNoteDummy,
|
|
|
|
playbackPosition,
|
|
|
|
getCurrentTick(),
|
|
|
|
fumen.BPM,
|
|
|
|
getResolution());
|
2019-03-28 02:16:29 +01:00
|
|
|
}
|
|
|
|
|
2019-03-24 21:51:33 +01:00
|
|
|
for (auto const& note : visibleNotes) {
|
2021-12-31 14:59:39 +01:00
|
|
|
float note_offset =
|
|
|
|
(playbackPosition.asSeconds() - getSecondsAt(note.getTiming()));
|
2022-01-04 01:31:17 +01:00
|
|
|
// auto frame = static_cast<long long
|
|
|
|
// int>(std::floor(note_offset * 30.f));
|
2019-03-24 21:51:33 +01:00
|
|
|
int x = note.getPos() % 4;
|
|
|
|
int y = note.getPos() / 4;
|
|
|
|
|
2019-02-09 16:05:46 +01:00
|
|
|
if (note.getLength() == 0) {
|
2019-03-28 02:16:29 +01:00
|
|
|
// Display normal note
|
2019-03-24 21:51:33 +01:00
|
|
|
|
|
|
|
auto t = marker.getSprite(markerEndingState, note_offset);
|
2019-02-13 00:44:46 +01:00
|
|
|
|
2019-02-09 16:05:46 +01:00
|
|
|
if (t) {
|
2019-03-24 21:51:33 +01:00
|
|
|
ImGui::SetCursorPos({x * squareSize, TitlebarHeight + y * squareSize});
|
2019-02-09 16:05:46 +01:00
|
|
|
ImGui::PushID(ImGuiIndex);
|
2019-03-24 21:51:33 +01:00
|
|
|
ImGui::Image(*t, {squareSize, squareSize});
|
2019-02-09 16:05:46 +01:00
|
|
|
ImGui::PopID();
|
2019-02-13 00:44:46 +01:00
|
|
|
++ImGuiIndex;
|
2019-02-09 16:05:46 +01:00
|
|
|
}
|
2019-02-13 00:44:46 +01:00
|
|
|
|
2019-02-09 16:05:46 +01:00
|
|
|
} else {
|
2021-12-31 14:59:39 +01:00
|
|
|
playfield.drawLongNote(
|
|
|
|
note,
|
|
|
|
playbackPosition,
|
|
|
|
getCurrentTick(),
|
|
|
|
fumen.BPM,
|
|
|
|
getResolution(),
|
|
|
|
marker,
|
|
|
|
markerEndingState);
|
2019-01-14 21:43:56 +01:00
|
|
|
}
|
|
|
|
}
|
2019-04-04 00:26:38 +02:00
|
|
|
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetCursorPos({0, TitlebarHeight});
|
2022-01-05 23:18:13 +01:00
|
|
|
ImGui::Image(playfield.longNoteLayer);
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetCursorPos({0, TitlebarHeight});
|
2022-01-05 23:18:13 +01:00
|
|
|
ImGui::Image(playfield.markerLayer);
|
2019-01-14 21:43:56 +01:00
|
|
|
}
|
2019-02-09 16:05:46 +01:00
|
|
|
|
2019-03-24 21:51:33 +01:00
|
|
|
// Display button grid
|
2019-01-14 21:43:56 +01:00
|
|
|
for (int y = 0; y < 4; ++y) {
|
|
|
|
for (int x = 0; x < 4; ++x) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::PushID(x + 4 * y);
|
|
|
|
ImGui::SetCursorPos({x * squareSize, TitlebarHeight + y * squareSize});
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4) ImColor::HSV(0, 0, 0, 0));
|
|
|
|
ImGui::PushStyleColor(
|
|
|
|
ImGuiCol_ButtonHovered,
|
|
|
|
(ImVec4) ImColor::HSV(0, 0, 1.f, 0.1f));
|
|
|
|
ImGui::PushStyleColor(
|
|
|
|
ImGuiCol_ButtonActive,
|
|
|
|
(ImVec4) ImColor::HSV(0, 0, 1.f, 0.5f));
|
|
|
|
if (ImGui::ImageButton(playfield.button, {squareSize, squareSize}, 0)) {
|
|
|
|
toggleNoteAtCurrentTime(x + 4 * y);
|
2019-01-17 19:39:59 +01:00
|
|
|
}
|
2022-01-07 00:14:38 +01:00
|
|
|
if (ImGui::IsItemHovered() and chart and chart->creatingLongNote) {
|
2019-03-28 02:16:29 +01:00
|
|
|
// Deal with long note creation stuff
|
2022-01-07 00:14:38 +01:00
|
|
|
if (not chart->longNoteBeingCreated) {
|
|
|
|
Note current_note =
|
|
|
|
Note(x + 4 * y, static_cast<int>(roundf(getCurrentTick())));
|
|
|
|
chart->longNoteBeingCreated =
|
|
|
|
std::make_pair(current_note, current_note);
|
|
|
|
} else {
|
|
|
|
chart->longNoteBeingCreated->second =
|
|
|
|
Note(x + 4 * y, static_cast<int>(roundf(getCurrentTick())));
|
2019-03-28 02:16:29 +01:00
|
|
|
}
|
|
|
|
}
|
2019-01-14 21:43:56 +01:00
|
|
|
ImGui::PopStyleColor(3);
|
|
|
|
ImGui::PopID();
|
|
|
|
}
|
|
|
|
}
|
2019-03-24 21:51:33 +01:00
|
|
|
|
|
|
|
if (chart) {
|
2019-03-27 20:37:30 +01:00
|
|
|
// Check for collisions then display them
|
2021-12-31 14:59:39 +01:00
|
|
|
auto ticks_threshold =
|
|
|
|
static_cast<int>((1.f / 60.f) * fumen.BPM * getResolution());
|
2019-03-25 19:16:45 +01:00
|
|
|
|
2019-03-24 21:51:33 +01:00
|
|
|
std::array<bool, 16> collisions = {};
|
|
|
|
|
|
|
|
for (auto const& note : visibleNotes) {
|
2021-12-31 14:59:39 +01:00
|
|
|
if (chart->ref.is_colliding(note, ticks_threshold)) {
|
2019-03-25 19:16:45 +01:00
|
|
|
collisions[note.getPos()] = true;
|
2019-03-24 21:51:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
|
|
if (collisions.at(i)) {
|
2021-12-31 14:59:39 +01:00
|
|
|
int x = i % 4;
|
|
|
|
int y = i / 4;
|
2019-03-24 21:51:33 +01:00
|
|
|
ImGui::SetCursorPos({x * squareSize, TitlebarHeight + y * squareSize});
|
|
|
|
ImGui::PushID(ImGuiIndex);
|
|
|
|
ImGui::Image(playfield.note_collision, {squareSize, squareSize});
|
|
|
|
ImGui::PopID();
|
|
|
|
++ImGuiIndex;
|
|
|
|
}
|
|
|
|
}
|
2019-03-27 20:37:30 +01:00
|
|
|
|
|
|
|
// Display selected notes
|
|
|
|
for (auto const& note : visibleNotes) {
|
|
|
|
if (chart->selectedNotes.find(note) != chart->selectedNotes.end()) {
|
2021-12-31 14:59:39 +01:00
|
|
|
int x = note.getPos() % 4;
|
|
|
|
int y = note.getPos() / 4;
|
2019-03-27 20:37:30 +01:00
|
|
|
ImGui::SetCursorPos({x * squareSize, TitlebarHeight + y * squareSize});
|
|
|
|
ImGui::PushID(ImGuiIndex);
|
|
|
|
ImGui::Image(playfield.note_selected, {squareSize, squareSize});
|
|
|
|
ImGui::PopID();
|
|
|
|
++ImGuiIndex;
|
|
|
|
}
|
|
|
|
}
|
2019-03-24 21:51:33 +01:00
|
|
|
}
|
2019-01-14 21:43:56 +01:00
|
|
|
}
|
|
|
|
ImGui::End();
|
2019-01-13 22:29:29 +01:00
|
|
|
}
|
|
|
|
|
2019-01-12 17:16:20 +01:00
|
|
|
/*
|
|
|
|
* Display all metadata in an editable form
|
|
|
|
*/
|
|
|
|
void EditorState::displayProperties() {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(500, 240));
|
|
|
|
ImGui::Begin("Properties", &showProperties, ImGuiWindowFlags_NoResize);
|
2019-01-12 17:16:20 +01:00
|
|
|
{
|
|
|
|
ImGui::Columns(2, nullptr, false);
|
|
|
|
|
2019-03-24 15:13:13 +01:00
|
|
|
if (albumCover) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::Image(*albumCover, sf::Vector2f(200, 200));
|
2019-01-12 17:16:20 +01:00
|
|
|
} else {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::BeginChild("Album Cover", ImVec2(200, 200), true);
|
2019-01-12 17:16:20 +01:00
|
|
|
ImGui::EndChild();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::NextColumn();
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::InputText("Title", &fumen.songTitle);
|
|
|
|
ImGui::InputText("Artist", &fumen.artist);
|
|
|
|
if (Toolbox::InputTextColored(
|
|
|
|
music.has_value(),
|
|
|
|
"Invalid Music Path",
|
|
|
|
"Music",
|
|
|
|
&fumen.musicPath)) {
|
2019-01-12 17:16:20 +01:00
|
|
|
reloadMusic();
|
2019-01-16 13:23:36 +01:00
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
if (Toolbox::InputTextColored(
|
|
|
|
albumCover.has_value(),
|
|
|
|
"Invalid Album Cover Path",
|
|
|
|
"Album Cover",
|
|
|
|
&fumen.albumCoverPath)) {
|
2019-03-24 15:13:13 +01:00
|
|
|
reloadAlbumCover();
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::InputFloat("BPM", &fumen.BPM, 1.0f, 10.0f)) {
|
2019-02-09 06:26:57 +01:00
|
|
|
if (fumen.BPM <= 0.0f) {
|
|
|
|
fumen.BPM = 0.0f;
|
|
|
|
}
|
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::InputFloat("offset", &fumen.offset, 0.01f, 1.f);
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2021-12-31 14:59:39 +01:00
|
|
|
* Display any information that would be useful for the user to troubleshoot the
|
|
|
|
* status of the editor will appear in the "Editor Status" window
|
2019-01-12 17:16:20 +01:00
|
|
|
*/
|
|
|
|
void EditorState::displayStatus() {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::Begin("Status", &showStatus, ImGuiWindowFlags_AlwaysAutoResize);
|
2019-01-12 17:16:20 +01:00
|
|
|
{
|
|
|
|
if (not music) {
|
|
|
|
if (not fumen.musicPath.empty()) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextColored(
|
|
|
|
ImVec4(1, 0.42, 0.41, 1),
|
|
|
|
"Invalid music path : %s",
|
|
|
|
fumen.musicPath.c_str());
|
2019-01-12 17:16:20 +01:00
|
|
|
} else {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextColored(
|
|
|
|
ImVec4(1, 0.42, 0.41, 1),
|
|
|
|
"No music file loaded");
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-24 15:13:13 +01:00
|
|
|
if (not albumCover) {
|
|
|
|
if (not fumen.albumCoverPath.empty()) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextColored(
|
|
|
|
ImVec4(1, 0.42, 0.41, 1),
|
|
|
|
"Invalid albumCover path : %s",
|
|
|
|
fumen.albumCoverPath.c_str());
|
2019-01-12 17:16:20 +01:00
|
|
|
} else {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextColored(
|
|
|
|
ImVec4(1, 0.42, 0.41, 1),
|
|
|
|
"No albumCover loaded");
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::SliderInt("Music Volume", &musicVolume, 0, 10)) {
|
2019-03-24 23:19:06 +01:00
|
|
|
setMusicVolume(musicVolume);
|
2019-01-17 01:08:38 +01:00
|
|
|
}
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::displayPlaybackStatus() {
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetNextWindowPos(
|
|
|
|
ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y - 25),
|
|
|
|
ImGuiCond_Always,
|
|
|
|
ImVec2(0.5f, 0.5f));
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
|
2019-01-12 17:16:20 +01:00
|
|
|
ImGui::Begin(
|
2021-12-31 14:59:39 +01:00
|
|
|
"Playback Status",
|
|
|
|
&showPlaybackStatus,
|
|
|
|
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs
|
|
|
|
| ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize);
|
2019-01-12 17:16:20 +01:00
|
|
|
{
|
2019-03-02 13:47:26 +01:00
|
|
|
if (chart) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::Text("%s %d", chart->ref.dif_name.c_str(), chart->ref.level);
|
|
|
|
ImGui::SameLine();
|
2019-01-16 13:23:36 +01:00
|
|
|
} else {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextDisabled("No chart selected");
|
|
|
|
ImGui::SameLine();
|
2019-01-16 13:23:36 +01:00
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextDisabled("Snap : ");
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Text("%s", Toolbox::toOrdinal(snap * 4).c_str());
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::TextColored(ImVec4(0.53, 0.53, 0.53, 1), "Beats :");
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Text("%02.2f", this->getBeats());
|
|
|
|
ImGui::SameLine();
|
2019-01-12 17:16:20 +01:00
|
|
|
if (music) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextColored(
|
|
|
|
ImVec4(0.53, 0.53, 0.53, 1),
|
|
|
|
"Music File Offset :");
|
|
|
|
ImGui::SameLine();
|
2022-02-28 23:10:22 +01:00
|
|
|
ImGui::TextUnformatted(Toolbox::to_string(music->getPrecisePlayingOffset()).c_str());
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SameLine();
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextColored(ImVec4(0.53, 0.53, 0.53, 1), "Timeline Position :");
|
|
|
|
ImGui::SameLine();
|
2019-01-14 12:21:58 +01:00
|
|
|
ImGui::TextUnformatted(Toolbox::to_string(playbackPosition).c_str());
|
2019-01-03 23:20:35 +01:00
|
|
|
}
|
2019-01-12 17:16:20 +01:00
|
|
|
ImGui::End();
|
|
|
|
ImGui::PopStyleVar();
|
2019-01-03 23:20:35 +01:00
|
|
|
}
|
2019-01-13 03:53:42 +01:00
|
|
|
|
|
|
|
void EditorState::displayTimeline() {
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
2019-03-25 19:16:45 +01:00
|
|
|
|
|
|
|
float height = io.DisplaySize.y * 0.9f;
|
|
|
|
|
|
|
|
if (chart) {
|
2019-04-06 22:35:17 +02:00
|
|
|
if (chart->densityGraph.should_recompute) {
|
|
|
|
chart->densityGraph.should_recompute = false;
|
2022-01-04 18:54:40 +01:00
|
|
|
chart->densityGraph.update(
|
2021-12-31 14:59:39 +01:00
|
|
|
static_cast<int>(height),
|
|
|
|
getChartRuntime(),
|
|
|
|
chart->ref,
|
|
|
|
fumen.BPM,
|
|
|
|
getResolution());
|
2019-03-25 19:16:45 +01:00
|
|
|
} else {
|
2019-04-06 22:35:17 +02:00
|
|
|
if (chart->densityGraph.last_height) {
|
|
|
|
if (static_cast<int>(height) != *(chart->densityGraph.last_height)) {
|
2022-01-04 18:54:40 +01:00
|
|
|
chart->densityGraph.update(
|
2021-12-31 14:59:39 +01:00
|
|
|
static_cast<int>(height),
|
|
|
|
getChartRuntime(),
|
|
|
|
chart->ref,
|
|
|
|
fumen.BPM,
|
|
|
|
getResolution());
|
2019-03-25 19:16:45 +01:00
|
|
|
}
|
|
|
|
} else {
|
2022-01-04 18:54:40 +01:00
|
|
|
chart->densityGraph.update(
|
2021-12-31 14:59:39 +01:00
|
|
|
static_cast<int>(height),
|
|
|
|
getChartRuntime(),
|
|
|
|
chart->ref,
|
|
|
|
fumen.BPM,
|
|
|
|
getResolution());
|
2019-03-25 19:16:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetNextWindowPos(
|
|
|
|
ImVec2(io.DisplaySize.x - 35, io.DisplaySize.y * 0.5f),
|
|
|
|
ImGuiCond_Always,
|
|
|
|
ImVec2(0.5f, 0.5f));
|
|
|
|
ImGui::SetNextWindowSize({45, height}, ImGuiCond_Always);
|
2019-04-06 22:48:18 +02:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1);
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 0));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0, 0, 0, 0));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0, 0, 0, 0));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0, 1.0, 1.1, 1.0));
|
2019-03-25 19:16:45 +01:00
|
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.240f, 0.520f, 0.880f, 0.500f));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.240f, 0.520f, 0.880f, 0.700f));
|
2019-01-13 03:53:42 +01:00
|
|
|
ImGui::Begin(
|
2021-12-31 14:59:39 +01:00
|
|
|
"Timeline",
|
|
|
|
&showTimeline,
|
|
|
|
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration
|
|
|
|
| ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
|
2019-01-13 03:53:42 +01:00
|
|
|
{
|
2022-01-05 12:44:56 +01:00
|
|
|
if (chart) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetCursorPos({0, 0});
|
2022-01-05 23:18:13 +01:00
|
|
|
ImGui::Image(chart->densityGraph.graph);
|
2021-12-31 14:59:39 +01:00
|
|
|
AffineTransform<float> scroll(-fumen.offset, previewEnd.asSeconds(), 1.f, 0.f);
|
2019-01-14 04:20:30 +01:00
|
|
|
float slider_pos = scroll.transform(playbackPosition.asSeconds());
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetCursorPos({0, 0});
|
2022-01-05 12:44:56 +01:00
|
|
|
if (ImGui::VSliderFloat("TimelineSlider", ImGui::GetContentRegionMax(), &slider_pos, 0.f, 1.f, "")) {
|
|
|
|
setPlaybackAndMusicPosition(sf::seconds(scroll.backwards_transform(slider_pos)));
|
2019-01-13 03:53:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
2019-03-25 19:16:45 +01:00
|
|
|
ImGui::PopStyleColor(6);
|
2019-01-13 03:53:42 +01:00
|
|
|
ImGui::PopStyleVar(3);
|
|
|
|
}
|
|
|
|
|
2022-01-04 01:31:17 +01:00
|
|
|
void EditorState::displayChartList(std::filesystem::path assets) {
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::Begin("Chart List", &showChartList, ImGuiWindowFlags_AlwaysAutoResize)) {
|
2019-01-16 01:59:02 +01:00
|
|
|
if (this->fumen.Charts.empty()) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::Dummy({100, 0});
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Text("- no charts -");
|
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Dummy({100, 0});
|
2019-01-16 01:59:02 +01:00
|
|
|
} else {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::Dummy(ImVec2(300, 0));
|
2019-01-16 01:59:02 +01:00
|
|
|
ImGui::Columns(3, "mycolumns");
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextDisabled("Difficulty");
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::TextDisabled("Level");
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::TextDisabled("Note Count");
|
|
|
|
ImGui::NextColumn();
|
2019-01-16 01:59:02 +01:00
|
|
|
ImGui::Separator();
|
2019-01-18 14:02:55 +01:00
|
|
|
for (auto& tuple : fumen.Charts) {
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::Selectable(
|
|
|
|
tuple.first.c_str(),
|
|
|
|
chart ? chart->ref == tuple.second : false,
|
|
|
|
ImGuiSelectableFlags_SpanAllColumns)) {
|
2019-03-28 02:16:29 +01:00
|
|
|
ESHelper::save(*this);
|
2022-01-04 01:31:17 +01:00
|
|
|
chart.emplace(tuple.second, assets);
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
|
|
|
ImGui::NextColumn();
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::Text("%d", tuple.second.level);
|
|
|
|
ImGui::NextColumn();
|
|
|
|
ImGui::Text("%d", static_cast<int>(tuple.second.Notes.size()));
|
|
|
|
ImGui::NextColumn();
|
2019-01-16 13:23:36 +01:00
|
|
|
ImGui::PushID(&tuple);
|
|
|
|
ImGui::PopID();
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
2019-02-09 16:05:46 +01:00
|
|
|
|
2019-03-26 00:04:29 +01:00
|
|
|
void EditorState::displayLinearView() {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(204, 400), ImGuiCond_Once);
|
|
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(204, 204), ImVec2(FLT_MAX, FLT_MAX));
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(2, 2));
|
2022-01-05 23:18:45 +01:00
|
|
|
if (ImGui::Begin("Linear View", &showLinearView, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
2019-03-27 14:41:16 +01:00
|
|
|
if (chart) {
|
2021-12-31 14:59:39 +01:00
|
|
|
linearView.update(
|
|
|
|
chart,
|
|
|
|
playbackPosition,
|
|
|
|
getCurrentTick(),
|
|
|
|
fumen.BPM,
|
|
|
|
getResolution(),
|
|
|
|
ImGui::GetContentRegionMax());
|
2022-01-05 23:18:13 +01:00
|
|
|
auto cursor_y = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.f;
|
|
|
|
ImGui::SetCursorPos({0, cursor_y});
|
|
|
|
ImGui::Image(linearView.view);
|
2019-03-27 14:41:16 +01:00
|
|
|
} else {
|
|
|
|
ImGui::TextDisabled("- no chart selected -");
|
|
|
|
}
|
2019-03-26 00:04:29 +01:00
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
ImGui::PopStyleVar(2);
|
|
|
|
}
|
|
|
|
|
2019-03-28 10:07:19 +01:00
|
|
|
saveChangesResponses EditorState::alertSaveChanges() {
|
2019-03-28 02:16:29 +01:00
|
|
|
if (chart and (not chart->history.empty())) {
|
2021-12-31 14:59:39 +01:00
|
|
|
int response = tinyfd_messageBox(
|
|
|
|
"Warning",
|
|
|
|
"Do you want to save changes ?",
|
|
|
|
"yesnocancel",
|
|
|
|
"warning",
|
|
|
|
1);
|
2019-03-28 02:16:29 +01:00
|
|
|
switch (response) {
|
|
|
|
// cancel
|
|
|
|
case 0:
|
2019-03-28 10:07:19 +01:00
|
|
|
return saveChangesCancel;
|
2019-03-28 02:16:29 +01:00
|
|
|
// yes
|
|
|
|
case 1:
|
2019-03-28 10:07:19 +01:00
|
|
|
return saveChangesYes;
|
2019-03-28 02:16:29 +01:00
|
|
|
// no
|
|
|
|
case 2:
|
2019-03-28 10:07:19 +01:00
|
|
|
return saveChangesNo;
|
2019-03-28 02:16:29 +01:00
|
|
|
default:
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << "Got unexcpected result from tinyfd_messageBox : " << response;
|
|
|
|
throw std::runtime_error(ss.str());
|
|
|
|
}
|
|
|
|
} else {
|
2019-03-28 10:07:19 +01:00
|
|
|
return saveChangesDidNotDisplayDialog;
|
2019-03-28 02:16:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-28 10:54:50 +01:00
|
|
|
/*
|
|
|
|
* Saves if asked and returns false if user canceled
|
|
|
|
*/
|
|
|
|
bool EditorState::saveChangesOrCancel() {
|
2021-12-31 14:59:39 +01:00
|
|
|
switch (alertSaveChanges()) {
|
2019-03-28 10:54:50 +01:00
|
|
|
case saveChangesYes:
|
|
|
|
ESHelper::save(*this);
|
|
|
|
case saveChangesNo:
|
|
|
|
case saveChangesDidNotDisplayDialog:
|
|
|
|
return true;
|
|
|
|
case saveChangesCancel:
|
2019-03-28 22:06:44 +01:00
|
|
|
default:
|
2019-03-28 10:54:50 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-09 16:05:46 +01:00
|
|
|
/*
|
|
|
|
* This SCREAAAAMS for optimisation, but in the meantime it works !
|
|
|
|
*/
|
2019-01-17 19:39:59 +01:00
|
|
|
void EditorState::updateVisibleNotes() {
|
2019-01-17 03:02:37 +01:00
|
|
|
visibleNotes.clear();
|
2019-01-16 01:59:02 +01:00
|
|
|
|
2019-03-02 13:47:26 +01:00
|
|
|
if (chart) {
|
2019-02-09 16:05:46 +01:00
|
|
|
float position = playbackPosition.asSeconds();
|
2019-01-14 21:43:56 +01:00
|
|
|
|
2019-03-02 13:47:26 +01:00
|
|
|
for (auto const& note : chart->ref.Notes) {
|
2019-02-09 16:05:46 +01:00
|
|
|
float note_timing_in_seconds = getSecondsAt(note.getTiming());
|
|
|
|
|
2021-12-31 14:59:39 +01:00
|
|
|
// we can leave early if the note is happening too far after the
|
|
|
|
// position
|
|
|
|
if (position > note_timing_in_seconds - 16.f / 30.f) {
|
2019-02-09 16:05:46 +01:00
|
|
|
if (note.getLength() == 0) {
|
2021-12-31 14:59:39 +01:00
|
|
|
if (position < note_timing_in_seconds + 16.f / 30.f) {
|
2019-02-09 16:05:46 +01:00
|
|
|
visibleNotes.insert(note);
|
|
|
|
}
|
|
|
|
} else {
|
2021-12-31 14:59:39 +01:00
|
|
|
float tail_end_in_seconds =
|
|
|
|
getSecondsAt(note.getTiming() + note.getLength());
|
|
|
|
if (position < tail_end_in_seconds + 16.f / 30.f) {
|
2019-02-09 16:05:46 +01:00
|
|
|
visibleNotes.insert(note);
|
|
|
|
}
|
|
|
|
}
|
2019-01-17 19:39:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a note is visible for the given pos, delete it
|
|
|
|
* Otherwise create note at nearest tick
|
|
|
|
*/
|
|
|
|
void EditorState::toggleNoteAtCurrentTime(int pos) {
|
2019-03-02 13:47:26 +01:00
|
|
|
if (chart) {
|
|
|
|
std::set<Note> toggledNotes = {};
|
|
|
|
|
2019-01-18 13:48:44 +01:00
|
|
|
bool deleted_something = false;
|
2019-01-17 19:39:59 +01:00
|
|
|
for (auto note : visibleNotes) {
|
|
|
|
if (note.getPos() == pos) {
|
2019-03-02 13:47:26 +01:00
|
|
|
toggledNotes.insert(note);
|
|
|
|
chart->ref.Notes.erase(note);
|
2019-01-18 13:48:44 +01:00
|
|
|
deleted_something = true;
|
2019-03-25 19:16:45 +01:00
|
|
|
break;
|
2019-01-14 21:43:56 +01:00
|
|
|
}
|
|
|
|
}
|
2019-01-18 13:48:44 +01:00
|
|
|
if (not deleted_something) {
|
2021-12-31 14:59:39 +01:00
|
|
|
toggledNotes.emplace(pos, static_cast<int>(roundf(getCurrentTick())));
|
|
|
|
chart->ref.Notes.emplace(pos, static_cast<int>(roundf(getCurrentTick())));
|
2019-01-18 13:48:44 +01:00
|
|
|
}
|
2019-03-02 13:47:26 +01:00
|
|
|
|
|
|
|
chart->history.push(std::make_shared<ToggledNotes>(toggledNotes, not deleted_something));
|
2019-04-06 22:35:17 +02:00
|
|
|
chart->densityGraph.should_recompute = true;
|
2019-01-14 21:43:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-24 23:19:06 +01:00
|
|
|
void EditorState::setMusicSpeed(int newMusicSpeed) {
|
2021-12-31 14:59:39 +01:00
|
|
|
musicSpeed = std::clamp(newMusicSpeed, 1, 20);
|
2019-03-24 23:19:06 +01:00
|
|
|
if (music) {
|
2021-12-31 14:59:39 +01:00
|
|
|
music->setPitch(musicSpeed / 10.f);
|
2019-03-24 23:19:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-09 02:53:41 +01:00
|
|
|
void EditorState::musicSpeedUp() {
|
|
|
|
setMusicSpeed(musicSpeed + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::musicSpeedDown() {
|
|
|
|
setMusicSpeed(musicSpeed - 1);
|
|
|
|
}
|
|
|
|
|
2019-03-24 23:19:06 +01:00
|
|
|
void EditorState::setMusicVolume(int newMusicVolume) {
|
2021-12-31 14:59:39 +01:00
|
|
|
musicVolume = std::clamp(newMusicVolume, 0, 10);
|
2019-03-24 23:19:06 +01:00
|
|
|
if (music) {
|
2022-02-09 02:53:41 +01:00
|
|
|
music->setVolume(Toolbox::convertVolumeToNormalizedDB(musicVolume)*100.f);
|
2019-03-24 23:19:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-09 02:53:41 +01:00
|
|
|
void EditorState::musicVolumeUp() {
|
|
|
|
setMusicVolume(musicVolume + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::musicVolumeDown() {
|
|
|
|
setMusicVolume(musicVolume -1 );
|
|
|
|
}
|
|
|
|
|
2021-12-31 14:59:39 +01:00
|
|
|
const sf::Time& EditorState::getPreviewEnd() {
|
2019-04-09 23:07:22 +02:00
|
|
|
reloadPreviewEnd();
|
|
|
|
return previewEnd;
|
|
|
|
}
|
|
|
|
|
2019-01-13 22:29:29 +01:00
|
|
|
void ESHelper::save(EditorState& ed) {
|
2019-01-13 03:53:42 +01:00
|
|
|
try {
|
2019-01-13 22:29:29 +01:00
|
|
|
ed.fumen.autoSaveAsMemon();
|
2019-01-13 03:53:42 +01:00
|
|
|
} catch (const std::exception& e) {
|
2021-12-31 14:59:39 +01:00
|
|
|
tinyfd_messageBox("Error", e.what(), "ok", "error", 1);
|
2019-01-13 03:53:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 01:31:17 +01:00
|
|
|
void ESHelper::open(std::optional<EditorState>& ed, std::filesystem::path assets, std::filesystem::path settings) {
|
2021-12-31 14:59:39 +01:00
|
|
|
const char* _filepath =
|
|
|
|
tinyfd_openFileDialog("Open File", nullptr, 0, nullptr, nullptr, false);
|
2019-01-13 03:53:42 +01:00
|
|
|
if (_filepath != nullptr) {
|
2022-01-04 01:31:17 +01:00
|
|
|
auto filepath = std::filesystem::path{_filepath};
|
|
|
|
ESHelper::openFromFile(ed, filepath, assets, settings);
|
2019-01-13 03:53:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-04 01:31:17 +01:00
|
|
|
void ESHelper::openFromFile(
|
|
|
|
std::optional<EditorState>& ed,
|
|
|
|
std::filesystem::path file,
|
|
|
|
std::filesystem::path assets,
|
|
|
|
std::filesystem::path settings
|
|
|
|
) {
|
2019-01-13 03:53:42 +01:00
|
|
|
try {
|
2022-01-04 01:31:17 +01:00
|
|
|
Fumen f(file);
|
2019-01-13 03:53:42 +01:00
|
|
|
f.autoLoadFromMemon();
|
2022-01-04 01:31:17 +01:00
|
|
|
ed.emplace(f, assets);
|
|
|
|
Toolbox::pushNewRecentFile(std::filesystem::canonical(ed->fumen.path), settings);
|
2021-12-31 14:59:39 +01:00
|
|
|
} catch (const std::exception& e) {
|
2019-01-13 03:53:42 +01:00
|
|
|
tinyfd_messageBox("Error", e.what(), "ok", "error", 1);
|
|
|
|
}
|
|
|
|
}
|
2019-01-16 01:59:02 +01:00
|
|
|
|
|
|
|
/*
|
2019-03-28 10:54:50 +01:00
|
|
|
* returns true if user saved or if saving wasn't necessary
|
|
|
|
* returns false if user canceled
|
|
|
|
*/
|
|
|
|
bool ESHelper::saveOrCancel(std::optional<EditorState>& ed) {
|
|
|
|
if (ed) {
|
|
|
|
return ed->saveChangesOrCancel();
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns the newly created chart if there is one
|
2019-01-16 01:59:02 +01:00
|
|
|
*/
|
2021-12-31 14:59:39 +01:00
|
|
|
std::optional<Chart> ESHelper::NewChartDialog::display(EditorState& editorState) {
|
2019-01-16 01:59:02 +01:00
|
|
|
std::optional<Chart> newChart;
|
|
|
|
if (ImGui::Begin(
|
|
|
|
"New Chart",
|
|
|
|
&editorState.showNewChartDialog,
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
|
2019-01-16 01:59:02 +01:00
|
|
|
if (showCustomDifName) {
|
|
|
|
comboPreview = "Custom";
|
|
|
|
} else {
|
|
|
|
if (difficulty.empty()) {
|
|
|
|
comboPreview = "Choose One";
|
|
|
|
} else {
|
|
|
|
comboPreview = difficulty;
|
|
|
|
}
|
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::BeginCombo("Difficulty", comboPreview.c_str())) {
|
|
|
|
for (auto dif_name : {"BSC", "ADV", "EXT"}) {
|
|
|
|
if (editorState.fumen.Charts.find(dif_name)
|
|
|
|
== editorState.fumen.Charts.end()) {
|
|
|
|
if (ImGui::Selectable(dif_name, dif_name == difficulty)) {
|
2019-01-16 01:59:02 +01:00
|
|
|
showCustomDifName = false;
|
|
|
|
difficulty = dif_name;
|
|
|
|
}
|
|
|
|
} else {
|
2020-05-22 11:02:47 +02:00
|
|
|
ImGui::TextDisabled("%s", dif_name);
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::Separator();
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::Selectable("Custom", &showCustomDifName)) {
|
2019-01-16 01:59:02 +01:00
|
|
|
difficulty = "";
|
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
|
|
|
if (showCustomDifName) {
|
2019-01-16 13:23:36 +01:00
|
|
|
Toolbox::InputTextColored(
|
2021-12-31 14:59:39 +01:00
|
|
|
editorState.fumen.Charts.find(difficulty)
|
|
|
|
== editorState.fumen.Charts.end(),
|
|
|
|
"Chart name has to be unique",
|
|
|
|
"Difficulty Name",
|
|
|
|
&difficulty);
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::InputInt("Level", &level);
|
2019-01-16 13:23:36 +01:00
|
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::TreeNode("Advanced##New Chart")) {
|
|
|
|
ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::InputInt("Resolution", &resolution)) {
|
2019-01-16 13:23:36 +01:00
|
|
|
if (resolution < 1) {
|
|
|
|
resolution = 1;
|
|
|
|
}
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
2019-01-16 13:23:36 +01:00
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::TextDisabled("(?)");
|
|
|
|
if (ImGui::IsItemHovered()) {
|
|
|
|
ImGui::BeginTooltip();
|
|
|
|
ImGui::TextUnformatted("Number of ticks in a beat");
|
|
|
|
ImGui::BulletText("Has nothing to do with time signature");
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::BulletText(
|
|
|
|
"Leave the default unless you know what you're doing");
|
2019-01-16 13:23:36 +01:00
|
|
|
ImGui::EndTooltip();
|
|
|
|
}
|
|
|
|
ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
|
|
|
|
ImGui::TreePop();
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
2019-01-16 13:23:36 +01:00
|
|
|
ImGui::Separator();
|
2021-12-31 14:59:39 +01:00
|
|
|
if (difficulty.empty()
|
|
|
|
or (editorState.fumen.Charts.find(difficulty)
|
|
|
|
!= editorState.fumen.Charts.end())) {
|
2019-01-16 01:59:02 +01:00
|
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
|
|
|
ImGui::Button("Create Chart##New Chart");
|
|
|
|
ImGui::PopItemFlag();
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
} else {
|
|
|
|
if (ImGui::Button("Create Chart##New Chart")) {
|
|
|
|
try {
|
2021-12-31 14:59:39 +01:00
|
|
|
newChart.emplace(difficulty, level, resolution);
|
2019-01-16 01:59:02 +01:00
|
|
|
} catch (const std::exception& e) {
|
2021-12-31 14:59:39 +01:00
|
|
|
tinyfd_messageBox("Error", e.what(), "ok", "error", 1);
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
return newChart;
|
|
|
|
}
|
2019-01-16 19:12:01 +01:00
|
|
|
|
2022-01-04 01:31:17 +01:00
|
|
|
void ESHelper::ChartPropertiesDialog::display(EditorState& editorState, std::filesystem::path assets) {
|
2019-03-02 13:47:26 +01:00
|
|
|
assert(editorState.chart.has_value());
|
2019-01-16 19:12:01 +01:00
|
|
|
|
|
|
|
if (this->shouldRefreshValues) {
|
|
|
|
shouldRefreshValues = false;
|
|
|
|
|
|
|
|
difNamesInUse.clear();
|
2019-03-02 13:47:26 +01:00
|
|
|
this->level = editorState.chart->ref.level;
|
|
|
|
this->difficulty_name = editorState.chart->ref.dif_name;
|
2021-12-31 14:59:39 +01:00
|
|
|
std::set<std::string> difNames {"BSC", "ADV", "EXT"};
|
2019-03-02 13:47:26 +01:00
|
|
|
showCustomDifName = (difNames.find(difficulty_name) == difNames.end());
|
2019-01-16 19:12:01 +01:00
|
|
|
|
|
|
|
for (auto const& tuple : editorState.fumen.Charts) {
|
2019-03-02 13:47:26 +01:00
|
|
|
if (tuple.second != editorState.chart->ref) {
|
2019-01-16 19:12:01 +01:00
|
|
|
difNamesInUse.insert(tuple.first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::Begin(
|
|
|
|
"Chart Properties",
|
|
|
|
&editorState.showChartProperties,
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
|
2019-01-16 19:12:01 +01:00
|
|
|
if (showCustomDifName) {
|
|
|
|
comboPreview = "Custom";
|
|
|
|
} else {
|
2019-03-02 13:47:26 +01:00
|
|
|
if (difficulty_name.empty()) {
|
2019-01-16 19:12:01 +01:00
|
|
|
comboPreview = "Choose One";
|
|
|
|
} else {
|
2019-03-02 13:47:26 +01:00
|
|
|
comboPreview = difficulty_name;
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::BeginCombo("Difficulty", comboPreview.c_str())) {
|
|
|
|
for (auto dif_name : {"BSC", "ADV", "EXT"}) {
|
2019-01-16 19:12:01 +01:00
|
|
|
if (difNamesInUse.find(dif_name) == difNamesInUse.end()) {
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::Selectable(dif_name, dif_name == difficulty_name)) {
|
2019-01-16 19:12:01 +01:00
|
|
|
showCustomDifName = false;
|
2019-03-02 13:47:26 +01:00
|
|
|
difficulty_name = dif_name;
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
|
|
|
} else {
|
2020-05-22 11:02:47 +02:00
|
|
|
ImGui::TextDisabled("%s", dif_name);
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::Separator();
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::Selectable("Custom", &showCustomDifName)) {
|
2019-03-02 13:47:26 +01:00
|
|
|
difficulty_name = "";
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
|
|
|
if (showCustomDifName) {
|
|
|
|
Toolbox::InputTextColored(
|
2021-12-31 14:59:39 +01:00
|
|
|
difNamesInUse.find(difficulty_name) == difNamesInUse.end(),
|
|
|
|
"Chart name has to be unique",
|
|
|
|
"Difficulty Name",
|
|
|
|
&difficulty_name);
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::InputInt("Level", &level);
|
2019-01-16 19:12:01 +01:00
|
|
|
ImGui::Separator();
|
2021-12-31 14:59:39 +01:00
|
|
|
if (difficulty_name.empty()
|
|
|
|
or (difNamesInUse.find(difficulty_name) != difNamesInUse.end())) {
|
2019-01-16 19:12:01 +01:00
|
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
|
|
|
ImGui::Button("Apply##New Chart");
|
|
|
|
ImGui::PopItemFlag();
|
|
|
|
ImGui::PopStyleVar();
|
|
|
|
} else {
|
|
|
|
if (ImGui::Button("Apply##New Chart")) {
|
|
|
|
try {
|
2021-12-31 14:59:39 +01:00
|
|
|
Chart modified_chart =
|
|
|
|
editorState.fumen.Charts.at(editorState.chart->ref.dif_name);
|
2019-03-02 13:47:26 +01:00
|
|
|
editorState.fumen.Charts.erase(editorState.chart->ref.dif_name);
|
|
|
|
modified_chart.dif_name = this->difficulty_name;
|
|
|
|
modified_chart.level = this->level;
|
2021-12-31 14:59:39 +01:00
|
|
|
if (not(editorState.fumen.Charts.emplace(modified_chart.dif_name, modified_chart))
|
|
|
|
.second) {
|
|
|
|
throw std::runtime_error(
|
|
|
|
"Could not insert modified chart in fumen");
|
2019-01-16 19:12:01 +01:00
|
|
|
} else {
|
2021-12-31 14:59:39 +01:00
|
|
|
editorState.chart.emplace(
|
2022-01-04 01:31:17 +01:00
|
|
|
editorState.fumen.Charts.at(modified_chart.dif_name),
|
|
|
|
assets
|
|
|
|
);
|
2019-01-16 19:12:01 +01:00
|
|
|
shouldRefreshValues = true;
|
|
|
|
}
|
|
|
|
} catch (const std::exception& e) {
|
2021-12-31 14:59:39 +01:00
|
|
|
tinyfd_messageBox("Error", e.what(), "ok", "error", 1);
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|