diff --git a/src/editor_state.cpp b/src/editor_state.cpp index 50929a9..2de4363 100644 --- a/src/editor_state.cpp +++ b/src/editor_state.cpp @@ -32,6 +32,7 @@ #include "long_note_dummy.hpp" #include "notifications_queue.hpp" #include "special_numeric_types.hpp" +#include "src/better_metadata.hpp" #include "src/custom_sfml_audio/synced_sound_streams.hpp" #include "variant_visitor.hpp" @@ -485,59 +486,102 @@ void EditorState::display_file_properties() { ImGui::EndChild(); } - ImGui::InputText("Title", &song.metadata.title); - ImGui::InputText("Artist", &song.metadata.artist); + auto edited_title = song.metadata.title; + ImGui::InputText("Title", &edited_title); + if (ImGui::IsItemDeactivatedAfterEdit()) { + history.push(std::make_shared(song.metadata.title, edited_title)); + song.metadata.title = edited_title; + } + auto edited_artist = song.metadata.artist; + ImGui::InputText("Artist", &edited_artist); + if (ImGui::IsItemDeactivatedAfterEdit()) { + history.push(std::make_shared(song.metadata.artist, edited_artist)); + song.metadata.artist = edited_artist; + } - if (feis::InputTextColored( + auto edited_audio = song.metadata.audio; + feis::InputTextColored( "Audio", - &song.metadata.audio, + &edited_audio, music.has_value(), "Invalid Audio Path" - )) { + ); + if (ImGui::IsItemDeactivatedAfterEdit()) { + history.push(std::make_shared(song.metadata.audio, edited_audio)); + song.metadata.audio = edited_audio; reload_music(); } - if (feis::InputTextColored( + auto edited_jacket = song.metadata.jacket; + feis::InputTextColored( "Jacket", - &song.metadata.jacket, + &edited_jacket, jacket.has_value(), "Invalid Jacket Path" - )) { + ); + if (ImGui::IsItemDeactivatedAfterEdit()) { + history.push(std::make_shared(song.metadata.jacket, edited_jacket)); + song.metadata.jacket = edited_jacket; reload_jacket(); } ImGui::Separator(); ImGui::Text("Preview"); - ImGui::Checkbox("Use separate preview file", &song.metadata.use_preview_file); + if (ImGui::Checkbox("Use separate preview file", &song.metadata.use_preview_file)) { + if (song.metadata.use_preview_file) { + history.push(std::make_shared(song.metadata.preview_loop, song.metadata.preview_file)); + } else { + history.push(std::make_shared(song.metadata.preview_file, song.metadata.preview_loop)); + } + }; if (song.metadata.use_preview_file) { - if (feis::InputTextColored( + auto edited_preview_file = song.metadata.preview_file; + feis::InputTextColored( "File", - &song.metadata.preview_file, + &edited_preview_file, preview_audio.has_value(), "Invalid Path" - )) { + ); + if (ImGui::IsItemDeactivatedAfterEdit()) { + history.push(std::make_shared(song.metadata.preview_file, edited_preview_file)); + song.metadata.preview_file = edited_preview_file; reload_preview_audio(); } } else { - if (feis::InputDecimal("Start", &song.metadata.preview_loop.start)) { - song.metadata.preview_loop.start = std::max( + auto edited_loop_start = song.metadata.preview_loop.start; + feis::InputDecimal( + "Start", + &edited_loop_start + ); + if (ImGui::IsItemDeactivatedAfterEdit()) { + edited_loop_start = std::max( Decimal{0}, - song.metadata.preview_loop.start + edited_loop_start ); if (music.has_value()) { - song.metadata.preview_loop.start = std::min( + edited_loop_start = std::min( Decimal{(**music).getDuration().asMicroseconds()} / 1000000, - song.metadata.preview_loop.start + edited_loop_start ); } + history.push(std::make_shared( + song.metadata.preview_loop, + better::PreviewLoop{edited_loop_start, song.metadata.preview_loop.duration} + )); + song.metadata.preview_loop.start = edited_loop_start; } - if (feis::InputDecimal("Duration", &song.metadata.preview_loop.duration)) { - song.metadata.preview_loop.duration = std::max( + auto edited_loop_duration = song.metadata.preview_loop.duration; + feis::InputDecimal( + "Duration", + &edited_loop_duration + ); + if (ImGui::IsItemDeactivatedAfterEdit()) { + edited_loop_duration = std::max( Decimal{0}, - song.metadata.preview_loop.duration + edited_loop_duration ); if (music.has_value()) { - song.metadata.preview_loop.start = std::min( + edited_loop_duration = std::min( ( Decimal{ (**music) @@ -546,9 +590,14 @@ void EditorState::display_file_properties() { } / 1000000 - song.metadata.preview_loop.start ), - song.metadata.preview_loop.start + edited_loop_duration ); } + history.push(std::make_shared( + song.metadata.preview_loop, + better::PreviewLoop{song.metadata.preview_loop.start, edited_loop_duration} + )); + song.metadata.preview_loop.duration = edited_loop_duration; } } diff --git a/src/editor_state.hpp b/src/editor_state.hpp index 2de3b95..90117dc 100644 --- a/src/editor_state.hpp +++ b/src/editor_state.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "custom_sfml_audio/beat_ticks.hpp" @@ -13,6 +14,7 @@ #include "custom_sfml_audio/note_claps.hpp" #include "custom_sfml_audio/open_music.hpp" #include "custom_sfml_audio/synced_sound_streams.hpp" +#include "src/history_item.hpp" #include "widgets/linear_view.hpp" #include "better_note.hpp" #include "better_song.hpp" @@ -188,6 +190,10 @@ public: void update_visible_notes(); + void reload_jacket(); + void reload_music(); + void reload_preview_audio(); + private: int volume = 10; // 0 -> 10 @@ -207,10 +213,7 @@ private: Interval editable_range; void reload_editable_range(); Interval choose_editable_range(); - void reload_jacket(); - void reload_music(); void clear_music(); - void reload_preview_audio(); better::Timing& applicable_timing; void reload_applicable_timing(); diff --git a/src/history_item.cpp b/src/history_item.cpp index 063f93d..035317c 100644 --- a/src/history_item.cpp +++ b/src/history_item.cpp @@ -5,9 +5,11 @@ #include #include +#include #include "better_song.hpp" #include "editor_state.hpp" +#include "src/better_metadata.hpp" const std::string& HistoryItem::get_message() const { @@ -160,3 +162,102 @@ void RenameChart::undo_action(EditorState& ed) const { ); } } + +ChangeTitle::ChangeTitle( + const std::string& old_value, + const std::string& new_value +) : + ChangeMetadataValue(old_value, new_value) +{ + message = fmt::format( + "Change song title : \"{}\" -> \"{}\"", + old_value, + new_value + ); +} + +void ChangeTitle::set_value(EditorState& ed, const std::string& value) const { + ed.song.metadata.title = value; +} + +ChangeArtist::ChangeArtist( + const std::string& old_value, + const std::string& new_value +) : + ChangeMetadataValue(old_value, new_value) +{ + message = fmt::format( + "Change song artist : \"{}\" -> \"{}\"", + old_value, + new_value + ); +} + +void ChangeArtist::set_value(EditorState& ed, const std::string& value) const { + ed.song.metadata.artist = value; +} + +ChangeAudio::ChangeAudio( + const std::string& old_value, + const std::string& new_value +) : + ChangeMetadataValue(old_value, new_value) +{ + message = fmt::format( + "Change audio : \"{}\" -> \"{}\"", + old_value, + new_value + ); +} + +void ChangeAudio::set_value(EditorState& ed, const std::string& value) const { + ed.song.metadata.audio = value; + ed.reload_music(); +} + +ChangeJacket::ChangeJacket( + const std::string& old_value, + const std::string& new_value +) : + ChangeMetadataValue(old_value, new_value) +{ + message = fmt::format( + "Change jacket : \"{}\" -> \"{}\"", + old_value, + new_value + ); +} + +void ChangeJacket::set_value(EditorState& ed, const std::string& value) const { + ed.song.metadata.jacket = value; + ed.reload_jacket(); +} + +ChangePreview::ChangePreview( + const PreviewState& old_value, + const PreviewState& new_value +) : + ChangeMetadataValue(old_value, new_value) +{ + message = fmt::format( + "Change preview : {} -> {}", + old_value, + new_value + ); +} + +void ChangePreview::set_value(EditorState& ed, const PreviewState& value) const { + const auto set_value_ = VariantVisitor { + [&](const better::PreviewLoop& loop) { + ed.song.metadata.use_preview_file = false; + ed.song.metadata.preview_loop.start = loop.start; + ed.song.metadata.preview_loop.duration = loop.duration; + }, + [&](const std::string& file) { + ed.song.metadata.use_preview_file = true; + ed.song.metadata.preview_file = file; + ed.reload_preview_audio(); + }, + }; + std::visit(set_value_, value); +} diff --git a/src/history_item.hpp b/src/history_item.hpp index 32c3380..75f85bf 100644 --- a/src/history_item.hpp +++ b/src/history_item.hpp @@ -5,6 +5,7 @@ #include #include +#include "better_metadata.hpp" #include "better_notes.hpp" #include "better_song.hpp" @@ -87,3 +88,82 @@ protected: std::string old_name; std::string new_name; }; + +template +class ChangeMetadataValue : public HistoryItem { +public: + + ChangeMetadataValue(const T& old_value, const T& new_value) : + old_value(old_value), + new_value(new_value) + { + + } + + void do_action(EditorState& ed) const override { + set_value(ed, new_value); + } + + void undo_action(EditorState& ed) const override { + set_value(ed, old_value); + } +protected: + virtual void set_value(EditorState& ed, const T& value) const = 0; + + T old_value; + T new_value; +}; + +class ChangeTitle : public ChangeMetadataValue { +public: + ChangeTitle(const std::string& old_value, const std::string& new_value); +protected: + void set_value(EditorState& ed, const std::string& value) const override; +}; + +class ChangeArtist : public ChangeMetadataValue { +public: + ChangeArtist(const std::string& old_value, const std::string& new_value); +protected: + void set_value(EditorState& ed, const std::string& value) const override; +}; + +class ChangeAudio : public ChangeMetadataValue { +public: + ChangeAudio(const std::string& old_value, const std::string& new_value); +protected: + void set_value(EditorState& ed, const std::string& value) const override; +}; + +class ChangeJacket : public ChangeMetadataValue { +public: + ChangeJacket(const std::string& old_value, const std::string& new_value);; +protected: + void set_value(EditorState& ed, const std::string& value) const override; +}; + +using PreviewState = std::variant; + +template <> +struct fmt::formatter: formatter { + // parse is inherited from formatter. + template + auto format(const PreviewState& c, FormatContext& ctx) { + const auto format_ = VariantVisitor { + [&](const better::PreviewLoop& loop) { + return format_to(ctx.out(), "{}", loop); + }, + [&](const std::string& file) { + return format_to(ctx.out(), "\"{}\"", file); + }, + }; + return std::visit(format_, c); + } +}; + +class ChangePreview : public ChangeMetadataValue { +public: + ChangePreview(const PreviewState& old_value, const PreviewState& new_value); +protected: + void set_value(EditorState& ed, const PreviewState& value) const override; +}; diff --git a/src/imgui_extras.cpp b/src/imgui_extras.cpp index 7aef1b8..270c9c5 100644 --- a/src/imgui_extras.cpp +++ b/src/imgui_extras.cpp @@ -1,6 +1,7 @@ #include "imgui_extras.hpp" #include +#include "imgui.h" bool feis::ColorEdit4(const char* label, sf::Color& col, ImGuiColorEditFlags flags) { float array_col[4] = { @@ -20,9 +21,13 @@ bool feis::ColorEdit4(const char* label, sf::Color& col, ImGuiColorEditFlags fla return false; } -bool feis::InputDecimal(const char *label, Decimal* value) { +bool feis::InputDecimal( + const char *label, + Decimal* value, + const ImGuiInputTextFlags flags +) { auto s = value->format("f"); - if (ImGui::InputText(label, &s, ImGuiInputTextFlags_CharsDecimal)) { + if (ImGui::InputText(label, &s, flags | ImGuiInputTextFlags_CharsDecimal)) { Decimal new_value; try { new_value = Decimal{s}; @@ -44,11 +49,12 @@ bool feis::InputTextColored( const char* label, std::string* str, bool isValid, - const std::string& hoverHelpText + const std::string& hoverHelpText, + const ImGuiInputTextFlags flags ) { bool return_value; if (str->empty()) { - return ImGui::InputText(label, str); + return ImGui::InputText(label, str, flags); } else { if (not isValid) { ImGui::PushStyleColor(ImGuiCol_FrameBg, colors::red.Value); diff --git a/src/imgui_extras.hpp b/src/imgui_extras.hpp index 1e647b1..3530f27 100644 --- a/src/imgui_extras.hpp +++ b/src/imgui_extras.hpp @@ -10,8 +10,18 @@ namespace feis { bool ColorEdit4(const char* label, sf::Color& col, ImGuiColorEditFlags flags = 0); - bool InputDecimal(const char *label, Decimal* value); - bool InputTextColored(const char* label, std::string* str, bool isValid, const std::string& hoverHelpText); + bool InputDecimal( + const char *label, + Decimal* value, + const ImGuiInputTextFlags flags = ImGuiInputTextFlags_None + ); + bool InputTextColored( + const char* label, + std::string* str, + bool isValid, + const std::string& hoverHelpText, + const ImGuiInputTextFlags flags = ImGuiInputTextFlags_None + ); void HelpMarker(const char* desc); } diff --git a/src/main.cpp b/src/main.cpp index bcafcbc..4beea4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -258,44 +258,48 @@ int main() { } break; case sf::Keyboard::Left: - if (event.key.shift) { - if (editor_state) { - editor_state->speed_down(); - notificationsQueue.push(std::make_shared(fmt::format( - "Speed : {}%", - editor_state->get_speed() * 10 - ))); - } - } else { - if (editor_state and editor_state->chart_state) { - editor_state->snap = Toolbox::getPreviousDivisor(240, editor_state->snap); - notificationsQueue.push( - std::make_shared(fmt::format( - "Snap : {}", - Toolbox::toOrdinal(4 * editor_state->snap) - )) - ); + if (not ImGui::GetIO().WantTextInput) { + if (event.key.shift) { + if (editor_state) { + editor_state->speed_down(); + notificationsQueue.push(std::make_shared(fmt::format( + "Speed : {}%", + editor_state->get_speed() * 10 + ))); + } + } else { + if (editor_state and editor_state->chart_state) { + editor_state->snap = Toolbox::getPreviousDivisor(240, editor_state->snap); + notificationsQueue.push( + std::make_shared(fmt::format( + "Snap : {}", + Toolbox::toOrdinal(4 * editor_state->snap) + )) + ); + } } } break; case sf::Keyboard::Right: - if (event.key.shift) { - if (editor_state) { - editor_state->speed_up(); - notificationsQueue.push(std::make_shared(fmt::format( - "Speed : {}%", - editor_state->get_speed() * 10 - ))); - } - } else { - if (editor_state and editor_state->chart_state) { - editor_state->snap = Toolbox::getNextDivisor(240, editor_state->snap); - notificationsQueue.push( - std::make_shared(fmt::format( - "Snap : {}", - Toolbox::toOrdinal(4 * editor_state->snap) - )) - ); + if (not ImGui::GetIO().WantTextInput) { + if (event.key.shift) { + if (editor_state) { + editor_state->speed_up(); + notificationsQueue.push(std::make_shared(fmt::format( + "Speed : {}%", + editor_state->get_speed() * 10 + ))); + } + } else { + if (editor_state and editor_state->chart_state) { + editor_state->snap = Toolbox::getNextDivisor(240, editor_state->snap); + notificationsQueue.push( + std::make_shared(fmt::format( + "Snap : {}", + Toolbox::toOrdinal(4 * editor_state->snap) + )) + ); + } } } break;