2021-12-31 14:59:39 +01:00
|
|
|
#include "editor_state.hpp"
|
|
|
|
|
2022-11-11 01:12:28 +01:00
|
|
|
#include <SFML/Audio/Music.hpp>
|
2022-05-11 00:09:02 +02:00
|
|
|
#include <SFML/Audio/SoundSource.hpp>
|
|
|
|
#include <SFML/Audio/SoundStream.hpp>
|
2022-04-22 01:56:28 +02:00
|
|
|
#include <SFML/System/Vector2.hpp>
|
2022-01-06 22:38:51 +01:00
|
|
|
#include <algorithm>
|
2019-01-17 01:08:38 +01:00
|
|
|
#include <cmath>
|
2022-11-15 22:04:29 +01:00
|
|
|
#include <cstddef>
|
2022-11-20 00:02:14 +01:00
|
|
|
#include <cstdint>
|
2019-01-12 03:13:30 +01:00
|
|
|
#include <filesystem>
|
2022-03-24 01:42:41 +01:00
|
|
|
|
|
|
|
#include <fmt/core.h>
|
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>
|
2022-10-07 01:47:45 +02:00
|
|
|
#include <initializer_list>
|
2022-06-01 00:26:36 +02:00
|
|
|
#include <memory>
|
2022-04-01 01:41:47 +02:00
|
|
|
#include <nowide/fstream.hpp>
|
2022-03-24 01:42:41 +01:00
|
|
|
#include <sstream>
|
|
|
|
#include <SFML/System/Time.hpp>
|
2022-04-01 01:41:47 +02:00
|
|
|
#include <stdexcept>
|
2022-04-04 04:30:02 +02:00
|
|
|
#include <string>
|
2021-12-31 00:57:06 +01:00
|
|
|
#include <tinyfiledialogs.h>
|
2022-04-16 05:25:03 +02:00
|
|
|
#include <variant>
|
2022-03-23 02:20:07 +01:00
|
|
|
|
|
|
|
#include "better_note.hpp"
|
2022-04-16 05:25:03 +02:00
|
|
|
#include "better_song.hpp"
|
2022-03-23 02:20:07 +01:00
|
|
|
#include "chart_state.hpp"
|
2022-11-15 22:04:29 +01:00
|
|
|
#include "compile_time_info.hpp"
|
2022-04-01 02:30:32 +02:00
|
|
|
#include "file_dialogs.hpp"
|
2022-04-09 00:54:06 +02:00
|
|
|
#include "history_item.hpp"
|
2022-03-24 01:42:41 +01:00
|
|
|
#include "imgui_extras.hpp"
|
2022-04-16 02:26:37 +02:00
|
|
|
#include "json_decimal_handling.hpp"
|
2022-04-16 05:25:03 +02:00
|
|
|
#include "long_note_dummy.hpp"
|
|
|
|
#include "notifications_queue.hpp"
|
2022-03-24 01:42:41 +01:00
|
|
|
#include "special_numeric_types.hpp"
|
2022-10-14 02:42:13 +02:00
|
|
|
#include "src/better_metadata.hpp"
|
2022-10-19 00:50:34 +02:00
|
|
|
#include "src/better_timing.hpp"
|
2022-05-11 00:09:02 +02:00
|
|
|
#include "src/custom_sfml_audio/synced_sound_streams.hpp"
|
2022-03-23 02:20:07 +01:00
|
|
|
#include "variant_visitor.hpp"
|
2022-12-27 18:46:14 +01:00
|
|
|
#include "utf8_strings.hpp"
|
2022-03-17 02:50:30 +01:00
|
|
|
|
2022-11-19 01:43:37 +01:00
|
|
|
EditorState::EditorState(const std::filesystem::path& assets_, config::Config& config_) :
|
|
|
|
config(config_),
|
2022-09-30 01:33:34 +02:00
|
|
|
note_claps(std::make_shared<NoteClaps>(nullptr, nullptr, assets_, 1.f)),
|
2022-10-07 01:47:45 +02:00
|
|
|
chord_claps(std::make_shared<ChordClaps>(nullptr, nullptr, assets_, 1.f)),
|
2022-09-30 01:33:34 +02:00
|
|
|
beat_ticks(std::make_shared<BeatTicks>(nullptr, assets_, 1.f)),
|
2022-04-01 02:30:32 +02:00
|
|
|
playfield(assets_),
|
2022-11-20 00:02:14 +01:00
|
|
|
linear_view(assets_, config_),
|
2022-04-01 02:30:32 +02:00
|
|
|
applicable_timing(song.timing),
|
|
|
|
assets(assets_)
|
|
|
|
{
|
|
|
|
reload_music();
|
|
|
|
reload_jacket();
|
|
|
|
};
|
|
|
|
|
2022-03-17 02:50:30 +01:00
|
|
|
EditorState::EditorState(
|
|
|
|
const better::Song& song_,
|
|
|
|
const std::filesystem::path& assets_,
|
2022-11-19 01:43:37 +01:00
|
|
|
const std::filesystem::path& song_path_,
|
|
|
|
config::Config& config_
|
2022-03-17 02:50:30 +01:00
|
|
|
) :
|
2022-11-19 01:43:37 +01:00
|
|
|
config(config_),
|
2022-04-01 02:30:32 +02:00
|
|
|
song(song_),
|
2022-11-19 01:43:37 +01:00
|
|
|
song_path(song_path_),
|
2022-09-30 01:33:34 +02:00
|
|
|
note_claps(std::make_shared<NoteClaps>(nullptr, nullptr, assets_, 1.f)),
|
2022-10-07 01:47:45 +02:00
|
|
|
chord_claps(std::make_shared<ChordClaps>(nullptr, nullptr, assets_, 1.f)),
|
2022-09-30 01:33:34 +02:00
|
|
|
beat_ticks(std::make_shared<BeatTicks>(nullptr, assets_, 1.f)),
|
2022-03-17 02:50:30 +01:00
|
|
|
playfield(assets_),
|
2022-11-20 00:02:14 +01:00
|
|
|
linear_view(assets_, config_),
|
2022-03-17 02:50:30 +01:00
|
|
|
applicable_timing(song.timing),
|
|
|
|
assets(assets_)
|
|
|
|
{
|
|
|
|
if (not song.charts.empty()) {
|
2022-04-04 04:30:02 +02:00
|
|
|
auto& [name, _] = *this->song.charts.begin();
|
|
|
|
open_chart(name);
|
2022-03-17 02:50:30 +01:00
|
|
|
}
|
2022-10-15 00:26:28 +02:00
|
|
|
history.mark_as_saved();
|
2022-03-17 02:50:30 +01:00
|
|
|
reload_music();
|
2022-03-23 02:20:07 +01:00
|
|
|
reload_jacket();
|
2022-11-11 01:12:28 +01:00
|
|
|
reload_preview_audio();
|
2022-03-17 02:50:30 +01:00
|
|
|
};
|
2019-01-03 23:20:35 +01:00
|
|
|
|
2022-04-07 00:14:01 +02:00
|
|
|
int EditorState::get_volume() const {
|
|
|
|
return volume;
|
|
|
|
}
|
|
|
|
|
2022-10-11 01:54:43 +02:00
|
|
|
void EditorState::set_volume(int volume_) {
|
2022-06-01 00:26:36 +02:00
|
|
|
if (music.has_value()) {
|
2022-10-11 01:54:43 +02:00
|
|
|
(**music).set_volume(volume_);
|
|
|
|
volume = (**music).get_volume();
|
2022-06-01 00:26:36 +02:00
|
|
|
}
|
2022-04-07 00:14:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::volume_up() {
|
|
|
|
set_volume(volume + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::volume_down() {
|
|
|
|
set_volume(volume - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int EditorState::get_speed() const {
|
|
|
|
return speed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::set_speed(int newMusicSpeed) {
|
|
|
|
speed = std::clamp(newMusicSpeed, 1, 20);
|
2022-05-11 00:09:02 +02:00
|
|
|
set_pitch(speed / 10.f);
|
2022-04-07 00:14:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::speed_up() {
|
|
|
|
set_speed(speed + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::speed_down() {
|
|
|
|
set_speed(speed - 1);
|
|
|
|
}
|
|
|
|
|
2022-11-11 01:12:28 +01:00
|
|
|
void EditorState::play_music_preview() {
|
|
|
|
if (song.metadata.use_preview_file) {
|
|
|
|
if (preview_audio) {
|
|
|
|
if (preview_audio->getStatus() == sf::Music::Playing) {
|
|
|
|
preview_audio->stop();
|
|
|
|
} else {
|
|
|
|
preview_audio->stop();
|
|
|
|
preview_audio->play();
|
|
|
|
}
|
|
|
|
}
|
2022-11-14 22:27:53 +01:00
|
|
|
} else {
|
|
|
|
set_playback_position(sf::seconds(std::stod(song.metadata.preview_loop.start.format("f"))));
|
|
|
|
play();
|
|
|
|
is_playing_preview_music_from_sss = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::stop_music_preview() {
|
|
|
|
if (song.metadata.use_preview_file) {
|
|
|
|
if (preview_audio) {
|
|
|
|
preview_audio->stop();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
stop();
|
|
|
|
is_playing_preview_music_from_sss = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EditorState::music_preview_is_playing() const {
|
|
|
|
if (song.metadata.use_preview_file) {
|
|
|
|
if (preview_audio) {
|
|
|
|
return preview_audio->getStatus() == sf::Music::Playing;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return is_playing_preview_music_from_sss;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sf::Time EditorState::music_preview_position() const {
|
|
|
|
if (not music_preview_is_playing()) {
|
|
|
|
return sf::Time::Zero;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (song.metadata.use_preview_file) {
|
|
|
|
if (preview_audio) {
|
|
|
|
return preview_audio->getPlayingOffset();
|
|
|
|
}
|
|
|
|
} else if (is_playing_preview_music_from_sss) {
|
|
|
|
return audio.getPlayingOffset() - sf::seconds(std::stod(song.metadata.preview_loop.start.format("f")));
|
|
|
|
}
|
|
|
|
|
|
|
|
return sf::Time::Zero;
|
|
|
|
}
|
|
|
|
|
|
|
|
sf::Time EditorState::music_preview_duration() const {
|
|
|
|
if (song.metadata.use_preview_file) {
|
|
|
|
if (preview_audio) {
|
|
|
|
return preview_audio->getDuration();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return sf::seconds(std::stod(song.metadata.preview_loop.duration.format("f")));
|
|
|
|
}
|
|
|
|
return sf::Time::Zero;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::update_music_preview_status() {
|
|
|
|
if (is_playing_preview_music_from_sss) {
|
|
|
|
if (music_preview_position() > music_preview_duration()) {
|
|
|
|
stop_music_preview();
|
|
|
|
}
|
2022-11-11 01:12:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 23:10:22 +01:00
|
|
|
|
2022-03-26 00:23:13 +01:00
|
|
|
const Interval<sf::Time>& EditorState::get_editable_range() {
|
2022-03-17 02:50:30 +01:00
|
|
|
reload_editable_range();
|
2022-03-23 02:20:07 +01:00
|
|
|
return editable_range;
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2019-01-12 17:16:20 +01:00
|
|
|
|
2022-10-11 01:54:43 +02:00
|
|
|
bool EditorState::has_any_audio() const {
|
|
|
|
return not audio.empty();
|
|
|
|
}
|
|
|
|
|
2022-06-09 00:28:21 +02:00
|
|
|
void EditorState::toggle_playback() {
|
|
|
|
if (get_status() != sf::SoundSource::Playing) {
|
|
|
|
play();
|
|
|
|
} else {
|
|
|
|
pause();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-07 01:47:45 +02:00
|
|
|
void EditorState::toggle_note_claps() {
|
|
|
|
if (
|
|
|
|
audio.contains_stream(note_clap_stream)
|
|
|
|
or audio.contains_stream(chord_clap_stream)
|
|
|
|
) {
|
|
|
|
audio.update_streams({}, {note_clap_stream, chord_clap_stream});
|
|
|
|
} else {
|
2022-10-26 02:08:19 +02:00
|
|
|
note_claps = note_claps->with_params(
|
2022-10-07 01:47:45 +02:00
|
|
|
get_pitch(),
|
|
|
|
not distinct_chord_clap,
|
|
|
|
clap_on_long_note_ends
|
|
|
|
);
|
|
|
|
std::map<std::string, NewStream> streams = {{note_clap_stream, {note_claps, true}}};
|
|
|
|
if (distinct_chord_clap) {
|
|
|
|
chord_claps = chord_claps->with_pitch(get_pitch());
|
|
|
|
streams[chord_clap_stream] = {chord_claps, true};
|
|
|
|
}
|
|
|
|
audio.update_streams(streams);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::toggle_clap_on_long_note_ends() {
|
|
|
|
clap_on_long_note_ends = not clap_on_long_note_ends;
|
2022-10-26 02:08:19 +02:00
|
|
|
note_claps = note_claps->with_params(
|
2022-10-07 01:47:45 +02:00
|
|
|
get_pitch(),
|
|
|
|
not distinct_chord_clap,
|
|
|
|
clap_on_long_note_ends
|
|
|
|
);
|
|
|
|
audio.update_streams({{note_clap_stream, {note_claps, true}}});
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::toggle_distinct_chord_claps() {
|
|
|
|
distinct_chord_clap = not distinct_chord_clap;
|
2022-10-26 02:08:19 +02:00
|
|
|
note_claps = note_claps->with_params(
|
2022-10-07 01:47:45 +02:00
|
|
|
get_pitch(),
|
|
|
|
not distinct_chord_clap,
|
|
|
|
clap_on_long_note_ends
|
|
|
|
);
|
|
|
|
if (distinct_chord_clap) {
|
|
|
|
chord_claps = chord_claps->with_pitch(get_pitch());
|
|
|
|
audio.update_streams(
|
|
|
|
{
|
|
|
|
{note_clap_stream, {note_claps, true}},
|
|
|
|
{chord_clap_stream, {chord_claps, true}}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
audio.update_streams(
|
|
|
|
{{note_clap_stream, {note_claps, true}}},
|
|
|
|
{chord_clap_stream}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-11 02:26:46 +02:00
|
|
|
void EditorState::toggle_beat_ticks() {
|
|
|
|
if (audio.contains_stream(beat_tick_stream)) {
|
|
|
|
audio.remove_stream(beat_tick_stream);
|
|
|
|
} else {
|
2022-10-05 01:42:50 +02:00
|
|
|
beat_ticks = beat_ticks->with_pitch(get_pitch());
|
2022-09-30 01:33:34 +02:00
|
|
|
audio.add_stream(beat_tick_stream, {beat_ticks, true});
|
2022-06-11 02:26:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-11 00:09:02 +02:00
|
|
|
void EditorState::play() {
|
2022-10-11 01:54:43 +02:00
|
|
|
status = sf::SoundSource::Playing;
|
2022-05-11 00:09:02 +02:00
|
|
|
audio.play();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::pause() {
|
2022-10-11 01:54:43 +02:00
|
|
|
status = sf::SoundSource::Paused;
|
2022-05-11 00:09:02 +02:00
|
|
|
audio.pause();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::stop() {
|
2022-10-11 01:54:43 +02:00
|
|
|
status = sf::SoundSource::Stopped;
|
2022-05-11 00:09:02 +02:00
|
|
|
audio.stop();
|
|
|
|
}
|
|
|
|
|
2022-06-01 00:26:36 +02:00
|
|
|
sf::SoundSource::Status EditorState::get_status() {
|
2022-10-11 01:54:43 +02:00
|
|
|
if (has_any_audio()) {
|
|
|
|
return audio.getStatus();
|
|
|
|
} else {
|
|
|
|
return status;
|
|
|
|
}
|
2022-05-11 00:09:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::set_pitch(float pitch) {
|
2022-09-30 01:33:34 +02:00
|
|
|
std::map<std::string, NewStream> update;
|
|
|
|
if (audio.contains_stream(note_clap_stream)) {
|
|
|
|
note_claps = note_claps->with_pitch(pitch);
|
|
|
|
update[note_clap_stream] = {note_claps, true};
|
|
|
|
}
|
|
|
|
if (audio.contains_stream(beat_tick_stream)) {
|
|
|
|
beat_ticks = beat_ticks->with_pitch(pitch);
|
|
|
|
update[beat_tick_stream] = {beat_ticks, true};
|
|
|
|
}
|
2022-10-07 01:47:45 +02:00
|
|
|
if (audio.contains_stream(chord_clap_stream)) {
|
|
|
|
chord_claps = chord_claps->with_pitch(pitch);
|
|
|
|
update[chord_clap_stream] = {chord_claps, true};
|
|
|
|
}
|
2022-10-08 01:01:49 +02:00
|
|
|
audio.update_streams(update, {}, pitch);
|
2022-05-11 00:09:02 +02:00
|
|
|
}
|
|
|
|
|
2022-10-05 01:42:50 +02:00
|
|
|
float EditorState::get_pitch() const {
|
|
|
|
return speed / 10.f;
|
|
|
|
}
|
|
|
|
|
2022-04-16 05:25:03 +02:00
|
|
|
void EditorState::set_playback_position(std::variant<sf::Time, Fraction> newPosition) {
|
|
|
|
const auto clamp_ = VariantVisitor {
|
|
|
|
[this](const sf::Time& seconds) {
|
|
|
|
return std::variant<sf::Time, Fraction>(
|
|
|
|
std::clamp(
|
|
|
|
seconds,
|
|
|
|
this->editable_range.start,
|
|
|
|
this->editable_range.end
|
|
|
|
)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
[this](const Fraction& beats) {
|
|
|
|
return std::variant<sf::Time, Fraction>(
|
|
|
|
std::clamp(
|
|
|
|
beats,
|
|
|
|
this->beats_at(this->editable_range.start),
|
|
|
|
this->beats_at(this->editable_range.end)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
newPosition = std::visit(clamp_, newPosition);
|
|
|
|
previous_playback_position = playback_position;
|
2022-03-16 02:10:18 +01:00
|
|
|
playback_position = newPosition;
|
2022-05-11 00:09:02 +02:00
|
|
|
const auto now = current_time();
|
|
|
|
if (now >= sf::Time::Zero and now < editable_range.end) {
|
|
|
|
audio.setPlayingOffset(now);
|
|
|
|
} else {
|
|
|
|
stop();
|
2019-01-17 01:08:38 +01:00
|
|
|
}
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2019-01-17 01:08:38 +01:00
|
|
|
|
2022-06-08 01:05:11 +02:00
|
|
|
sf::Time EditorState::get_precise_playback_position() {
|
2022-06-11 03:31:33 +02:00
|
|
|
return audio.getPrecisePlayingOffset();
|
2022-05-11 00:09:02 +02:00
|
|
|
}
|
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
Fraction EditorState::current_exact_beats() const {
|
2022-04-16 05:25:03 +02:00
|
|
|
const auto current_exact_beats_ = VariantVisitor {
|
|
|
|
[this](const sf::Time& seconds) { return this->beats_at(seconds); },
|
|
|
|
[](const Fraction& beats) { return beats; },
|
|
|
|
};
|
|
|
|
return std::visit(current_exact_beats_, playback_position);
|
2022-03-17 02:50:30 +01:00
|
|
|
};
|
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
Fraction EditorState::current_snaped_beats() const {
|
|
|
|
const auto exact = current_exact_beats();
|
|
|
|
return round_beats(exact, snap);
|
2022-03-17 02:50:30 +01:00
|
|
|
};
|
|
|
|
|
2022-04-16 05:25:03 +02:00
|
|
|
Fraction EditorState::previous_exact_beats() const {
|
|
|
|
const auto current_exact_beats_ = VariantVisitor {
|
|
|
|
[this](const sf::Time& seconds) { return this->beats_at(seconds); },
|
|
|
|
[](const Fraction& beats) { return beats; },
|
|
|
|
};
|
|
|
|
return std::visit(current_exact_beats_, previous_playback_position);
|
|
|
|
}
|
|
|
|
|
|
|
|
sf::Time EditorState::current_time() const {
|
|
|
|
const auto current_time_ = VariantVisitor {
|
|
|
|
[](const sf::Time& seconds) { return seconds; },
|
|
|
|
[this](const Fraction& beats) { return this->time_at(beats); },
|
|
|
|
};
|
|
|
|
return std::visit(current_time_, playback_position);
|
|
|
|
}
|
|
|
|
|
|
|
|
sf::Time EditorState::previous_time() const {
|
|
|
|
const auto current_time_ = VariantVisitor {
|
|
|
|
[](const sf::Time& seconds) { return seconds; },
|
|
|
|
[this](const Fraction& beats) { return this->time_at(beats); },
|
|
|
|
};
|
|
|
|
return std::visit(current_time_, previous_playback_position);
|
|
|
|
}
|
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
Fraction EditorState::beats_at(sf::Time time) const {
|
2022-10-26 02:08:19 +02:00
|
|
|
return applicable_timing->beats_at(time);
|
2022-03-17 02:50:30 +01:00
|
|
|
};
|
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
sf::Time EditorState::time_at(Fraction beat) const {
|
2022-10-26 02:08:19 +02:00
|
|
|
return applicable_timing->time_at(beat);
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
Fraction EditorState::get_snap_step() const {
|
2022-03-17 02:50:30 +01:00
|
|
|
return Fraction{1, snap};
|
|
|
|
};
|
|
|
|
|
2022-04-09 00:54:06 +02:00
|
|
|
void EditorState::display_playfield(Marker& marker, Judgement 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),
|
2022-03-23 02:20:07 +01:00
|
|
|
Toolbox::CustomConstraints::ContentSquare
|
|
|
|
);
|
2019-01-14 21:43:56 +01:00
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
if (ImGui::Begin("Playfield", &show_playfield, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
2022-03-17 02:50:30 +01:00
|
|
|
if (
|
|
|
|
not ImGui::IsWindowHovered()
|
|
|
|
and chart_state
|
|
|
|
and chart_state->creating_long_note
|
|
|
|
) {
|
2022-01-07 00:14:38 +01:00
|
|
|
// cancel long note creation if the mouse is or goes out of the playfield
|
2022-03-17 02:50:30 +01:00
|
|
|
chart_state->long_note_being_created.reset();
|
|
|
|
chart_state->creating_long_note = false;
|
2022-01-07 00:14:38 +01:00
|
|
|
}
|
2022-03-23 02:20:07 +01:00
|
|
|
|
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
|
|
|
|
2022-03-17 02:50:30 +01:00
|
|
|
if (chart_state) {
|
2019-04-04 00:26:38 +02:00
|
|
|
playfield.resize(static_cast<unsigned int>(ImGui::GetWindowSize().x));
|
2022-03-17 02:50:30 +01:00
|
|
|
if (chart_state->long_note_being_created) {
|
2022-03-23 02:20:07 +01:00
|
|
|
playfield.draw_tail_and_receptor(
|
2022-10-28 01:24:16 +02:00
|
|
|
make_long_note_dummy_for_playfield(
|
2022-03-23 02:20:07 +01:00
|
|
|
current_exact_beats(),
|
2022-04-16 05:25:03 +02:00
|
|
|
*chart_state->long_note_being_created,
|
|
|
|
get_snap_step()
|
2022-03-23 02:20:07 +01:00
|
|
|
),
|
2022-04-16 05:25:03 +02:00
|
|
|
current_time(),
|
2022-10-26 02:08:19 +02:00
|
|
|
*applicable_timing
|
2022-03-17 02:50:30 +01:00
|
|
|
);
|
2019-03-28 02:16:29 +01:00
|
|
|
}
|
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
auto display = VariantVisitor {
|
|
|
|
[&, this](const better::TapNote& tap_note){
|
2022-04-16 05:25:03 +02:00
|
|
|
auto note_offset = (this->current_time() - this->time_at(tap_note.get_time()));
|
2022-04-09 00:54:06 +02:00
|
|
|
auto t = marker.at(markerEndingState, note_offset);
|
2019-02-09 16:05:46 +01:00
|
|
|
if (t) {
|
2022-03-23 02:20:07 +01:00
|
|
|
ImGui::SetCursorPos({
|
|
|
|
tap_note.get_position().get_x() * squareSize,
|
|
|
|
TitlebarHeight + tap_note.get_position().get_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
|
|
|
}
|
2022-03-23 02:20:07 +01:00
|
|
|
},
|
|
|
|
[&, this](const better::LongNote& long_note){
|
|
|
|
this->playfield.draw_long_note(
|
|
|
|
long_note,
|
2022-04-16 05:25:03 +02:00
|
|
|
current_time(),
|
2022-10-26 02:08:19 +02:00
|
|
|
*applicable_timing,
|
2021-12-31 14:59:39 +01:00
|
|
|
marker,
|
2022-03-23 02:20:07 +01:00
|
|
|
markerEndingState
|
|
|
|
);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2022-04-19 02:07:56 +02:00
|
|
|
for (const auto& [_, note] : chart_state->visible_notes) {
|
2022-03-23 02:20:07 +01:00
|
|
|
note.visit(display);
|
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-03-23 02:20:07 +01:00
|
|
|
ImGui::Image(playfield.long_note.layer);
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetCursorPos({0, TitlebarHeight});
|
2022-03-17 02:50:30 +01:00
|
|
|
ImGui::Image(playfield.marker_layer);
|
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
|
2022-03-23 02:20:07 +01:00
|
|
|
for (unsigned int y = 0; y < 4; ++y) {
|
|
|
|
for (unsigned 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)) {
|
2022-03-27 03:32:13 +02:00
|
|
|
if (chart_state) {
|
2022-04-07 00:14:01 +02:00
|
|
|
chart_state->toggle_note(
|
2022-04-16 05:25:03 +02:00
|
|
|
current_time(),
|
2022-04-07 00:14:01 +02:00
|
|
|
snap,
|
|
|
|
{x, y},
|
2022-10-26 02:08:19 +02:00
|
|
|
*applicable_timing
|
2022-04-07 00:14:01 +02:00
|
|
|
);
|
2022-10-26 02:08:19 +02:00
|
|
|
reload_sounds_that_depend_on_notes();
|
2022-03-27 03:32:13 +02:00
|
|
|
}
|
2019-01-17 19:39:59 +01:00
|
|
|
}
|
2022-03-17 02:50:30 +01:00
|
|
|
if (ImGui::IsItemHovered() and chart_state and chart_state->creating_long_note) {
|
2019-03-28 02:16:29 +01:00
|
|
|
// Deal with long note creation stuff
|
2022-03-23 02:20:07 +01:00
|
|
|
if (not chart_state->long_note_being_created) {
|
|
|
|
better::TapNote current_note{current_snaped_beats(), {x, y}};
|
|
|
|
chart_state->long_note_being_created.emplace(current_note, current_note);
|
2022-01-07 00:14:38 +01:00
|
|
|
} else {
|
2022-03-23 02:20:07 +01:00
|
|
|
chart_state->long_note_being_created->second = better::TapNote{
|
|
|
|
current_snaped_beats(), {x, y}
|
|
|
|
};
|
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
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
if (chart_state) {
|
2022-11-21 23:55:25 +01:00
|
|
|
// Check for real (+ potential if requested) collisions
|
|
|
|
// then display them
|
2019-03-24 21:51:33 +01:00
|
|
|
std::array<bool, 16> collisions = {};
|
2022-04-19 02:07:56 +02:00
|
|
|
for (const auto& [_, note] : chart_state->visible_notes) {
|
2022-11-20 00:02:14 +01:00
|
|
|
if (chart_state->chart.notes->is_colliding(note, *applicable_timing, config.editor.collision_zone)) {
|
2022-03-23 02:20:07 +01:00
|
|
|
collisions[note.get_position().index()] = true;
|
2019-03-24 21:51:33 +01:00
|
|
|
}
|
|
|
|
}
|
2022-11-21 23:55:25 +01:00
|
|
|
if (show_free_buttons) {
|
|
|
|
for (unsigned int i = 0; i < 16; i++) {
|
|
|
|
unsigned int x = i % 4;
|
|
|
|
unsigned int y = i / 4;
|
|
|
|
if (chart_state->chart.notes->would_collide(
|
|
|
|
better::TapNote{current_exact_beats(), {x, y}},
|
|
|
|
*applicable_timing,
|
|
|
|
config.editor.collision_zone
|
|
|
|
)) {
|
|
|
|
collisions[i] = 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
|
2022-04-19 02:07:56 +02:00
|
|
|
for (const auto& [_, note] : chart_state->visible_notes) {
|
2022-11-09 01:01:58 +01:00
|
|
|
if (chart_state->selected_stuff.notes.contains(note)) {
|
2022-03-23 02:20:07 +01:00
|
|
|
ImGui::SetCursorPos({
|
|
|
|
note.get_position().get_x() * squareSize,
|
|
|
|
TitlebarHeight + note.get_position().get_y() * squareSize
|
|
|
|
});
|
2019-03-27 20:37:30 +01:00
|
|
|
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();
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
2019-01-13 22:29:29 +01:00
|
|
|
|
2019-01-12 17:16:20 +01:00
|
|
|
/*
|
2022-03-23 02:20:07 +01:00
|
|
|
Display all metadata in an editable form
|
|
|
|
*/
|
2022-10-11 21:23:29 +02:00
|
|
|
void EditorState::display_file_properties() {
|
2022-04-19 23:13:18 +02:00
|
|
|
if (ImGui::Begin(
|
2022-10-11 21:23:29 +02:00
|
|
|
"File Properties",
|
|
|
|
&show_file_properties,
|
2022-04-19 23:13:18 +02:00
|
|
|
ImGuiWindowFlags_NoResize
|
|
|
|
| ImGuiWindowFlags_AlwaysAutoResize
|
|
|
|
)) {
|
|
|
|
if (jacket) {
|
2022-03-23 02:20:07 +01:00
|
|
|
if (jacket) {
|
2022-04-19 23:13:18 +02:00
|
|
|
ImGui::Image(*jacket, sf::Vector2f(300, 300));
|
2022-03-23 02:20:07 +01:00
|
|
|
}
|
2022-04-19 23:13:18 +02:00
|
|
|
} else {
|
|
|
|
ImGui::BeginChild("Album Cover", ImVec2(300, 300), true, ImGuiWindowFlags_NoResize);
|
|
|
|
ImGui::EndChild();
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
2022-03-23 02:20:07 +01:00
|
|
|
|
2022-10-14 02:42:13 +02:00
|
|
|
auto edited_title = song.metadata.title;
|
|
|
|
ImGui::InputText("Title", &edited_title);
|
|
|
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
|
|
|
history.push(std::make_shared<ChangeTitle>(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<ChangeArtist>(song.metadata.artist, edited_artist));
|
|
|
|
song.metadata.artist = edited_artist;
|
|
|
|
}
|
2019-01-12 17:16:20 +01:00
|
|
|
|
2022-10-14 02:42:13 +02:00
|
|
|
auto edited_audio = song.metadata.audio;
|
|
|
|
feis::InputTextColored(
|
2022-03-23 02:20:07 +01:00
|
|
|
"Audio",
|
2022-10-14 02:42:13 +02:00
|
|
|
&edited_audio,
|
2022-06-01 00:26:36 +02:00
|
|
|
music.has_value(),
|
2022-03-23 02:20:07 +01:00
|
|
|
"Invalid Audio Path"
|
2022-10-14 02:42:13 +02:00
|
|
|
);
|
|
|
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
|
|
|
history.push(std::make_shared<ChangeAudio>(song.metadata.audio, edited_audio));
|
|
|
|
song.metadata.audio = edited_audio;
|
2022-03-16 02:10:18 +01:00
|
|
|
reload_music();
|
2019-01-16 13:23:36 +01:00
|
|
|
}
|
2022-10-14 02:42:13 +02:00
|
|
|
auto edited_jacket = song.metadata.jacket;
|
|
|
|
feis::InputTextColored(
|
2022-03-23 02:20:07 +01:00
|
|
|
"Jacket",
|
2022-10-14 02:42:13 +02:00
|
|
|
&edited_jacket,
|
2022-03-23 02:20:07 +01:00
|
|
|
jacket.has_value(),
|
|
|
|
"Invalid Jacket Path"
|
2022-10-14 02:42:13 +02:00
|
|
|
);
|
|
|
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
|
|
|
history.push(std::make_shared<ChangeJacket>(song.metadata.jacket, edited_jacket));
|
|
|
|
song.metadata.jacket = edited_jacket;
|
2022-03-23 02:20:07 +01:00
|
|
|
reload_jacket();
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
2022-03-23 02:20:07 +01:00
|
|
|
|
|
|
|
ImGui::Separator();
|
|
|
|
|
|
|
|
ImGui::Text("Preview");
|
2022-10-14 02:42:13 +02:00
|
|
|
if (ImGui::Checkbox("Use separate preview file", &song.metadata.use_preview_file)) {
|
2022-11-14 22:27:53 +01:00
|
|
|
stop_music_preview();
|
2022-10-14 02:42:13 +02:00
|
|
|
if (song.metadata.use_preview_file) {
|
|
|
|
history.push(std::make_shared<ChangePreview>(song.metadata.preview_loop, song.metadata.preview_file));
|
|
|
|
} else {
|
|
|
|
history.push(std::make_shared<ChangePreview>(song.metadata.preview_file, song.metadata.preview_loop));
|
|
|
|
}
|
|
|
|
};
|
2022-03-31 03:50:15 +02:00
|
|
|
if (song.metadata.use_preview_file) {
|
2022-10-14 02:42:13 +02:00
|
|
|
auto edited_preview_file = song.metadata.preview_file;
|
|
|
|
feis::InputTextColored(
|
2022-03-23 02:20:07 +01:00
|
|
|
"File",
|
2022-10-14 02:42:13 +02:00
|
|
|
&edited_preview_file,
|
2022-03-23 02:20:07 +01:00
|
|
|
preview_audio.has_value(),
|
|
|
|
"Invalid Path"
|
2022-10-14 02:42:13 +02:00
|
|
|
);
|
|
|
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
|
|
|
history.push(std::make_shared<ChangePreview>(song.metadata.preview_file, edited_preview_file));
|
|
|
|
song.metadata.preview_file = edited_preview_file;
|
2022-03-23 02:20:07 +01:00
|
|
|
reload_preview_audio();
|
2019-02-09 06:26:57 +01:00
|
|
|
}
|
2022-03-23 02:20:07 +01:00
|
|
|
} else {
|
2022-10-14 02:42:13 +02:00
|
|
|
auto edited_loop_start = song.metadata.preview_loop.start;
|
|
|
|
feis::InputDecimal(
|
|
|
|
"Start",
|
|
|
|
&edited_loop_start
|
|
|
|
);
|
|
|
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
|
|
|
edited_loop_start = std::max(
|
2022-03-24 01:42:41 +01:00
|
|
|
Decimal{0},
|
2022-10-14 02:42:13 +02:00
|
|
|
edited_loop_start
|
2022-03-24 01:42:41 +01:00
|
|
|
);
|
2022-06-01 00:26:36 +02:00
|
|
|
if (music.has_value()) {
|
2022-10-14 02:42:13 +02:00
|
|
|
edited_loop_start = std::min(
|
2022-06-01 00:26:36 +02:00
|
|
|
Decimal{(**music).getDuration().asMicroseconds()} / 1000000,
|
2022-10-14 02:42:13 +02:00
|
|
|
edited_loop_start
|
2022-03-24 01:42:41 +01:00
|
|
|
);
|
|
|
|
}
|
2022-10-14 02:42:13 +02:00
|
|
|
history.push(std::make_shared<ChangePreview>(
|
|
|
|
song.metadata.preview_loop,
|
|
|
|
better::PreviewLoop{edited_loop_start, song.metadata.preview_loop.duration}
|
|
|
|
));
|
|
|
|
song.metadata.preview_loop.start = edited_loop_start;
|
2022-03-24 01:42:41 +01:00
|
|
|
}
|
2022-10-14 02:42:13 +02:00
|
|
|
auto edited_loop_duration = song.metadata.preview_loop.duration;
|
|
|
|
feis::InputDecimal(
|
|
|
|
"Duration",
|
|
|
|
&edited_loop_duration
|
|
|
|
);
|
|
|
|
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
|
|
|
edited_loop_duration = std::max(
|
2022-03-24 01:42:41 +01:00
|
|
|
Decimal{0},
|
2022-10-14 02:42:13 +02:00
|
|
|
edited_loop_duration
|
2022-03-24 01:42:41 +01:00
|
|
|
);
|
2022-06-01 00:26:36 +02:00
|
|
|
if (music.has_value()) {
|
2022-10-14 02:42:13 +02:00
|
|
|
edited_loop_duration = std::min(
|
2022-03-24 01:42:41 +01:00
|
|
|
(
|
|
|
|
Decimal{
|
2022-06-01 00:26:36 +02:00
|
|
|
(**music)
|
|
|
|
.getDuration()
|
2022-03-24 01:42:41 +01:00
|
|
|
.asMicroseconds()
|
|
|
|
} / 1000000
|
2022-03-31 03:50:15 +02:00
|
|
|
- song.metadata.preview_loop.start
|
2022-03-24 01:42:41 +01:00
|
|
|
),
|
2022-10-14 02:42:13 +02:00
|
|
|
edited_loop_duration
|
2022-03-24 01:42:41 +01:00
|
|
|
);
|
|
|
|
}
|
2022-10-14 02:42:13 +02:00
|
|
|
history.push(std::make_shared<ChangePreview>(
|
|
|
|
song.metadata.preview_loop,
|
|
|
|
better::PreviewLoop{song.metadata.preview_loop.start, edited_loop_duration}
|
|
|
|
));
|
|
|
|
song.metadata.preview_loop.duration = edited_loop_duration;
|
2022-03-24 01:42:41 +01:00
|
|
|
}
|
2022-11-17 02:08:11 +01:00
|
|
|
const bool should_display_quick_define = chart_state and chart_state->time_selection;
|
|
|
|
if (not should_display_quick_define) {
|
|
|
|
ImGui::BeginDisabled();
|
|
|
|
}
|
|
|
|
if (ImGui::Button("Define from time selection")) {
|
|
|
|
stop_music_preview();
|
|
|
|
const auto before = [&]() -> PreviewState {
|
|
|
|
if (song.metadata.use_preview_file) {
|
|
|
|
return song.metadata.preview_file;
|
|
|
|
} else {
|
|
|
|
return song.metadata.preview_loop;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
const auto start = time_at(chart_state->time_selection->start);
|
|
|
|
const auto end = time_at(chart_state->time_selection->end);
|
|
|
|
const auto duration = end - start;
|
|
|
|
song.metadata.use_preview_file = false;
|
|
|
|
song.metadata.preview_loop.start = Decimal{fmt::format("{:.03}", start.asSeconds())};
|
|
|
|
song.metadata.preview_loop.duration = Decimal{fmt::format("{:.03}", duration.asSeconds())};
|
|
|
|
history.push(std::make_shared<ChangePreview>(before, song.metadata.preview_loop));
|
|
|
|
}
|
|
|
|
if (not should_display_quick_define) {
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
|
|
|
|
ImGui::BeginTooltip();
|
|
|
|
ImGui::TextUnformatted(
|
|
|
|
"You must define a time selection in the linear view first !\n"
|
|
|
|
"Open up 'View' > 'Linear View' then use the Tab key to set the start, then the end"
|
|
|
|
);
|
|
|
|
ImGui::EndTooltip();
|
|
|
|
}
|
|
|
|
}
|
2019-02-09 06:26:57 +01:00
|
|
|
}
|
2022-11-14 22:27:53 +01:00
|
|
|
if (music_preview_is_playing()) {
|
2022-11-15 00:03:59 +01:00
|
|
|
if (feis::StopButton("##Stop Mucic Preview")) {
|
2022-11-14 22:27:53 +01:00
|
|
|
stop_music_preview();
|
|
|
|
}
|
|
|
|
} else {
|
2022-11-15 00:03:59 +01:00
|
|
|
if (ImGui::ArrowButton("##Play Music Preview", ImGuiDir_Right)) {
|
2022-11-14 22:27:53 +01:00
|
|
|
play_music_preview();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
const auto position = music_preview_position();
|
|
|
|
const auto duration = music_preview_duration();
|
|
|
|
float progress = [&](){
|
|
|
|
if (duration == sf::Time::Zero) {
|
|
|
|
return 0.f;
|
|
|
|
} else {
|
|
|
|
return position / duration;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
ImGui::PushItemWidth(ImGui::CalcItemWidth() - ImGui::GetCursorPosX() + ImGui::GetStyle().WindowPadding.x);
|
|
|
|
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
|
|
|
ImGui::SliderFloat(
|
|
|
|
"##Progress",
|
|
|
|
&progress,
|
|
|
|
0.f,
|
|
|
|
1.f,
|
|
|
|
fmt::format(
|
|
|
|
"{:02}:{:02} / {:02}:{:02}",
|
|
|
|
static_cast<unsigned int>(position / sf::seconds(60)),
|
|
|
|
static_cast<unsigned int>(position.asSeconds()) % 60,
|
|
|
|
static_cast<unsigned int>(duration / sf::seconds(60)),
|
|
|
|
static_cast<unsigned int>(duration.asSeconds()) % 60
|
|
|
|
).c_str()
|
|
|
|
);
|
|
|
|
ImGui::PopItemFlag();
|
|
|
|
ImGui::PopItemWidth();
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
|
|
|
ImGui::End();
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
2019-01-12 17:16:20 +01:00
|
|
|
|
|
|
|
/*
|
2022-03-23 02:20:07 +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
|
|
|
|
*/
|
|
|
|
void EditorState::display_status() {
|
2022-10-11 21:23:29 +02:00
|
|
|
ImGui::Begin("Status", &show_status, ImGuiWindowFlags_AlwaysAutoResize);
|
2019-01-12 17:16:20 +01:00
|
|
|
{
|
2022-06-01 00:26:36 +02:00
|
|
|
if (not music.has_value()) {
|
2022-03-31 03:50:15 +02:00
|
|
|
if (not song.metadata.audio.empty()) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextColored(
|
|
|
|
ImVec4(1, 0.42, 0.41, 1),
|
|
|
|
"Invalid music path : %s",
|
2022-03-31 03:50:15 +02:00
|
|
|
song.metadata.audio.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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-17 02:50:30 +01:00
|
|
|
if (not jacket) {
|
2022-03-31 03:50:15 +02:00
|
|
|
if (not song.metadata.jacket.empty()) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::TextColored(
|
|
|
|
ImVec4(1, 0.42, 0.41, 1),
|
2022-03-23 02:20:07 +01:00
|
|
|
"Invalid jacket path : %s",
|
2022-03-31 03:50:15 +02:00
|
|
|
song.metadata.jacket.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),
|
2022-03-23 02:20:07 +01:00
|
|
|
"No jacket loaded");
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
|
|
|
}
|
2022-10-05 01:42:50 +02:00
|
|
|
|
|
|
|
audio.display_debug();
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
|
|
|
ImGui::End();
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
2019-01-12 17:16:20 +01:00
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
void EditorState::display_playback_status() {
|
2019-01-12 17:16:20 +01:00
|
|
|
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",
|
2022-10-11 21:23:29 +02:00
|
|
|
&show_playback_status,
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs
|
|
|
|
| ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize);
|
2019-01-12 17:16:20 +01:00
|
|
|
{
|
2022-03-24 01:42:41 +01:00
|
|
|
if (chart_state) {
|
|
|
|
ImGui::TextUnformatted(
|
|
|
|
fmt::format(
|
|
|
|
"{} {}",
|
|
|
|
chart_state->difficulty_name,
|
2022-03-27 03:32:13 +02:00
|
|
|
better::stringify_level(chart_state->chart.level)
|
2022-03-24 01:42:41 +01:00
|
|
|
).c_str()
|
|
|
|
);
|
2021-12-31 14:59:39 +01:00
|
|
|
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
|
|
|
}
|
2022-03-24 01:42:41 +01:00
|
|
|
ImGui::TextDisabled("Snap :");
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SameLine();
|
|
|
|
ImGui::Text("%s", Toolbox::toOrdinal(snap * 4).c_str());
|
|
|
|
ImGui::SameLine();
|
2022-03-24 01:42:41 +01:00
|
|
|
ImGui::TextDisabled("Beats :");
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SameLine();
|
2022-04-13 02:29:33 +02:00
|
|
|
ImGui::TextUnformatted(fmt::format("{:.3f}", static_cast<double>(current_exact_beats())).c_str());
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SameLine();
|
2022-06-01 00:26:36 +02:00
|
|
|
if (music.has_value()) {
|
2022-03-24 01:42:41 +01:00
|
|
|
ImGui::TextDisabled("Music File Offset :");
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SameLine();
|
2022-06-01 00:26:36 +02:00
|
|
|
ImGui::TextUnformatted(Toolbox::to_string(audio.getPlayingOffset()).c_str());
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SameLine();
|
2019-01-12 17:16:20 +01:00
|
|
|
}
|
2022-03-24 01:42:41 +01:00
|
|
|
ImGui::TextDisabled("Timeline Position :");
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SameLine();
|
2022-04-16 05:25:03 +02:00
|
|
|
ImGui::TextUnformatted(Toolbox::to_string(current_time()).c_str());
|
2019-01-03 23:20:35 +01:00
|
|
|
}
|
2019-01-12 17:16:20 +01:00
|
|
|
ImGui::End();
|
|
|
|
ImGui::PopStyleVar();
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
2019-01-13 03:53:42 +01:00
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
void EditorState::display_timeline() {
|
2019-01-13 03:53:42 +01:00
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
2019-03-25 19:16:45 +01:00
|
|
|
|
2022-03-16 02:10:18 +01:00
|
|
|
float raw_height = io.DisplaySize.y * 0.9f;
|
|
|
|
auto height = static_cast<int>(raw_height);
|
2019-03-25 19:16:45 +01:00
|
|
|
|
2022-03-16 02:10:18 +01:00
|
|
|
if (
|
2022-03-24 01:42:41 +01:00
|
|
|
chart_state.has_value()
|
2022-03-16 02:10:18 +01:00
|
|
|
and (
|
2022-03-24 01:42:41 +01:00
|
|
|
chart_state->density_graph.should_recompute
|
|
|
|
or height != chart_state->density_graph.last_height.value_or(height)
|
2022-03-16 02:10:18 +01:00
|
|
|
)
|
|
|
|
) {
|
2022-03-24 01:42:41 +01:00
|
|
|
chart_state->density_graph.should_recompute = false;
|
|
|
|
chart_state->density_graph.update(
|
2022-03-23 02:20:07 +01:00
|
|
|
height,
|
2022-03-24 01:42:41 +01:00
|
|
|
chart_state->chart,
|
2022-10-26 02:08:19 +02:00
|
|
|
*applicable_timing,
|
2022-03-24 01:42:41 +01:00
|
|
|
editable_range.start,
|
|
|
|
editable_range.end
|
2022-03-23 02:20:07 +01:00
|
|
|
);
|
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));
|
2022-03-24 01:42:41 +01:00
|
|
|
ImGui::SetNextWindowSize({45.f, static_cast<float>(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",
|
2022-10-11 21:23:29 +02:00
|
|
|
&show_timeline,
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration
|
|
|
|
| ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove);
|
2019-01-13 03:53:42 +01:00
|
|
|
{
|
2022-03-24 01:42:41 +01:00
|
|
|
if (chart_state) {
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::SetCursorPos({0, 0});
|
2022-03-24 01:42:41 +01:00
|
|
|
ImGui::Image(chart_state->density_graph.graph);
|
|
|
|
// The output is reversed because we are repurposing a vertical
|
|
|
|
// slider, which goes from 0 AT THE BOTTOM to 1 AT THE TOP, which is
|
|
|
|
// the opposite of what we want
|
|
|
|
AffineTransform<float> scroll(
|
|
|
|
editable_range.start.asSeconds(),
|
|
|
|
editable_range.end.asSeconds(),
|
|
|
|
1.f,
|
|
|
|
0.f
|
|
|
|
);
|
2022-04-16 05:25:03 +02:00
|
|
|
float slider_pos = scroll.transform(current_time().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, "")) {
|
2022-03-24 01:42:41 +01:00
|
|
|
set_playback_position(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-03-23 02:20:07 +01:00
|
|
|
};
|
2019-01-13 03:53:42 +01:00
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
void EditorState::display_chart_list() {
|
2022-10-11 21:23:29 +02:00
|
|
|
if (ImGui::Begin("Chart List", &show_chart_list, ImGuiWindowFlags_AlwaysAutoResize)) {
|
2022-03-24 01:42:41 +01:00
|
|
|
if (this->song.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();
|
2022-03-24 01:42:41 +01:00
|
|
|
for (auto& [name, chart] : song.charts) {
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::Selectable(
|
2022-03-24 01:42:41 +01:00
|
|
|
name.c_str(),
|
|
|
|
chart_state ? chart_state->difficulty_name == name : false,
|
|
|
|
ImGuiSelectableFlags_SpanAllColumns
|
|
|
|
)) {
|
2022-04-04 04:30:02 +02:00
|
|
|
open_chart(name);
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
|
|
|
ImGui::NextColumn();
|
2022-03-27 03:32:13 +02:00
|
|
|
ImGui::TextUnformatted(better::stringify_level(chart.level).c_str());
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::NextColumn();
|
2022-10-26 02:08:19 +02:00
|
|
|
ImGui::Text("%d", static_cast<int>(chart.notes->size()));
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::NextColumn();
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
2019-02-09 16:05:46 +01:00
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
void EditorState::display_linear_view() {
|
2022-04-22 01:56:28 +02:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(304, 500), ImGuiCond_Once);
|
|
|
|
ImGui::SetNextWindowSizeConstraints(ImVec2(304, 304), ImVec2(FLT_MAX, FLT_MAX));
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
|
|
|
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(2, 2));
|
2022-10-27 22:08:56 +02:00
|
|
|
if (
|
|
|
|
ImGui::Begin(
|
|
|
|
"Linear View",
|
|
|
|
&show_linear_view,
|
|
|
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse
|
|
|
|
)
|
|
|
|
) {
|
2022-03-24 01:42:41 +01:00
|
|
|
if (chart_state) {
|
2022-04-22 01:56:28 +02:00
|
|
|
auto header_height = ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.f;
|
|
|
|
ImGui::SetCursorPos({0, header_height});
|
|
|
|
linear_view.draw(
|
|
|
|
ImGui::GetWindowDrawList(),
|
2022-03-25 02:20:22 +01:00
|
|
|
*chart_state,
|
2022-10-26 02:08:19 +02:00
|
|
|
*applicable_timing,
|
2022-04-16 05:25:03 +02:00
|
|
|
current_exact_beats(),
|
2022-04-19 12:59:17 +02:00
|
|
|
beats_at(editable_range.end),
|
2022-04-16 05:25:03 +02:00
|
|
|
get_snap_step(),
|
2022-04-22 01:56:28 +02:00
|
|
|
ImGui::GetContentRegionMax(),
|
|
|
|
ImGui::GetCursorScreenPos()
|
2022-03-26 00:23:13 +01:00
|
|
|
);
|
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);
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
2019-03-26 00:04:29 +01:00
|
|
|
|
2022-10-11 01:54:43 +02:00
|
|
|
void EditorState::display_sound_settings() {
|
2022-10-11 21:23:29 +02:00
|
|
|
if (ImGui::Begin("Sound Settings", &show_sound_settings)) {
|
2022-10-11 01:54:43 +02:00
|
|
|
if (ImGui::TreeNodeEx("Music", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
ImGui::BeginDisabled(not music.has_value());
|
|
|
|
{
|
|
|
|
auto volume = get_volume();
|
|
|
|
if (ImGui::SliderInt("Volume##Music", &volume, 0, 10)) {
|
|
|
|
set_volume(volume);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
|
|
|
if (ImGui::TreeNodeEx("Beat Tick", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
bool beat_tick = beat_ticks_are_on();
|
|
|
|
if (ImGui::Checkbox("On/Off##Beat Tick", &beat_tick)) {
|
|
|
|
toggle_beat_ticks();
|
|
|
|
}
|
|
|
|
ImGui::BeginDisabled(not beat_tick);
|
|
|
|
{
|
|
|
|
auto volume = beat_ticks->get_volume();
|
|
|
|
if (ImGui::SliderInt("Volume##Beat Tick", &volume, 0, 10)) {
|
|
|
|
beat_ticks->set_volume(volume);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
|
|
|
if (ImGui::TreeNodeEx("Note Clap", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
|
|
bool note_clap = note_claps_are_on();
|
|
|
|
if (ImGui::Checkbox("On/Off##Note Clap", ¬e_clap)) {
|
|
|
|
toggle_note_claps();
|
|
|
|
}
|
|
|
|
ImGui::BeginDisabled(not note_clap);
|
|
|
|
{
|
|
|
|
auto volume = note_claps->get_volume();
|
|
|
|
if (ImGui::SliderInt("Volume##Note Clap", &volume, 0, 10)) {
|
|
|
|
note_claps->set_volume(volume);
|
|
|
|
chord_claps->set_volume(volume);
|
|
|
|
}
|
|
|
|
if (ImGui::TreeNode("Advanced##Note Clap")) {
|
|
|
|
bool long_end = get_clap_on_long_note_ends();
|
|
|
|
if (ImGui::Checkbox("Clap on long note ends", &long_end)) {
|
|
|
|
toggle_clap_on_long_note_ends();
|
|
|
|
}
|
|
|
|
bool chord_clap = get_distinct_chord_claps();
|
|
|
|
if (ImGui::Checkbox("Distinct chord clap", &chord_clap)) {
|
|
|
|
toggle_distinct_chord_claps();
|
|
|
|
}
|
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
2022-10-11 21:23:29 +02:00
|
|
|
void EditorState::display_editor_settings() {
|
2022-11-20 00:23:04 +01:00
|
|
|
if (
|
|
|
|
ImGui::Begin(
|
|
|
|
"Editor Settings",
|
|
|
|
&show_editor_settings,
|
|
|
|
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize
|
|
|
|
)
|
|
|
|
) {
|
2022-10-11 21:23:29 +02:00
|
|
|
static const std::uint64_t step = 1;
|
2022-11-20 00:02:14 +01:00
|
|
|
if (ImGui::InputScalar("Snap##Editor Settings", ImGuiDataType_U64, &snap, &step, nullptr, "%d")) {
|
2022-12-27 14:17:08 +01:00
|
|
|
snap = std::clamp(snap, UINT64_C(1), UINT64_C(1000));
|
2022-10-11 21:23:29 +02:00
|
|
|
};
|
|
|
|
ImGui::SameLine();
|
|
|
|
feis::HelpMarker(
|
|
|
|
"Change the underlying snap value, this allows setting snap "
|
|
|
|
"values that aren't a divisor of 240. "
|
|
|
|
"This changes the underlying value that's multiplied "
|
|
|
|
"by 4 before being shown in the status bar"
|
|
|
|
);
|
2022-11-20 00:02:14 +01:00
|
|
|
int collision_zone_ms = config.editor.collision_zone.asMilliseconds();
|
|
|
|
if (ImGui::SliderInt("Collision Zone##Editor Settings", &collision_zone_ms, 100, 2000, "%d ms")) {
|
|
|
|
collision_zone_ms = std::clamp(collision_zone_ms, 100, 2000);
|
|
|
|
config.editor.collision_zone = sf::milliseconds(collision_zone_ms);
|
|
|
|
if (chart_state) {
|
|
|
|
chart_state->density_graph.should_recompute = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
|
|
feis::HelpMarker(
|
2022-11-20 00:23:04 +01:00
|
|
|
"Suggested minimal duration between two notes on the same "
|
|
|
|
"button.\n"
|
|
|
|
"If two notes are closer than this they \"collide\" and are "
|
|
|
|
"highlighted in red everywhere"
|
2022-11-20 00:02:14 +01:00
|
|
|
);
|
|
|
|
const std::array<std::pair<const char*, sf::Time>, 3> presets{{
|
|
|
|
{"F.E.I.S default", sf::seconds(1)},
|
|
|
|
{"Safe", sf::milliseconds(1066)},
|
|
|
|
{"jubeat plus", sf::milliseconds(1030)}
|
|
|
|
}};
|
2022-11-20 00:23:04 +01:00
|
|
|
if (ImGui::BeginCombo("Collision Zone Presets", "Choose ...")) {
|
2022-11-20 00:02:14 +01:00
|
|
|
for (const auto& [name, value] : presets) {
|
|
|
|
if (ImGui::Selectable(name, false)) {
|
|
|
|
config.editor.collision_zone = value;
|
|
|
|
if (chart_state) {
|
|
|
|
chart_state->density_graph.should_recompute = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
2022-10-11 21:23:29 +02:00
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
2022-10-13 00:59:40 +02:00
|
|
|
void EditorState::display_history() {
|
|
|
|
history.display(show_history);
|
|
|
|
}
|
|
|
|
|
2022-11-10 00:48:13 +01:00
|
|
|
void EditorState::display_timing_menu() {
|
|
|
|
if (ImGui::Begin("Adjust Timing", &show_timing_menu)) {
|
2022-10-19 00:50:34 +02:00
|
|
|
auto bpm = std::visit(
|
2022-10-26 02:08:19 +02:00
|
|
|
[&](const auto& pos){return applicable_timing->bpm_at(pos);},
|
2022-10-19 00:50:34 +02:00
|
|
|
playback_position
|
|
|
|
);
|
|
|
|
if (feis::InputDecimal("BPM", &bpm, ImGuiInputTextFlags_EnterReturnsTrue)) {
|
2022-11-20 01:38:56 +01:00
|
|
|
if (bpm > 0) {
|
|
|
|
const auto before = *applicable_timing;
|
|
|
|
applicable_timing->insert(better::BPMAtBeat{bpm, current_snaped_beats()});
|
|
|
|
if (*applicable_timing != before) {
|
|
|
|
reload_sounds_that_depend_on_timing();
|
|
|
|
history.push(std::make_shared<ChangeTiming>(before, *applicable_timing, timing_origin()));
|
|
|
|
}
|
|
|
|
if (chart_state) {
|
|
|
|
chart_state->density_graph.should_recompute = true;
|
|
|
|
}
|
2022-10-26 23:35:00 +02:00
|
|
|
}
|
2022-10-19 00:50:34 +02:00
|
|
|
}
|
2022-11-10 00:48:13 +01:00
|
|
|
auto offset = applicable_timing->get_offset();
|
|
|
|
if (feis::InputDecimal("beat zero offset", &offset, ImGuiInputTextFlags_EnterReturnsTrue)) {
|
|
|
|
applicable_timing->set_offset(offset);
|
|
|
|
reload_sounds_that_depend_on_timing();
|
2022-11-10 23:39:39 +01:00
|
|
|
set_playback_position(current_exact_beats());
|
2022-11-20 01:38:56 +01:00
|
|
|
if (chart_state) {
|
|
|
|
chart_state->density_graph.should_recompute = true;
|
|
|
|
}
|
2022-11-10 00:48:13 +01:00
|
|
|
}
|
2022-10-19 00:50:34 +02:00
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
|
|
|
|
2022-04-02 04:10:09 +02:00
|
|
|
bool EditorState::needs_to_save() const {
|
2022-10-13 00:59:40 +02:00
|
|
|
return not history.current_state_is_saved();
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2022-04-02 04:10:09 +02:00
|
|
|
|
|
|
|
EditorState::UserWantsToSave EditorState::ask_if_user_wants_to_save() const {
|
|
|
|
int response_code = tinyfd_messageBox(
|
|
|
|
"Warning",
|
2022-11-11 00:06:58 +01:00
|
|
|
"The currently open chart has unsaved changes, do you want to save ?",
|
2022-04-02 04:10:09 +02:00
|
|
|
"yesnocancel",
|
|
|
|
"warning",
|
|
|
|
1
|
|
|
|
);
|
|
|
|
switch (response_code) {
|
|
|
|
// cancel
|
|
|
|
case 0:
|
|
|
|
return EditorState::UserWantsToSave::Cancel;
|
|
|
|
// yes
|
|
|
|
case 1:
|
|
|
|
return EditorState::UserWantsToSave::Yes;
|
|
|
|
// no
|
|
|
|
case 2:
|
|
|
|
return EditorState::UserWantsToSave::No;
|
|
|
|
default:
|
|
|
|
throw std::runtime_error(fmt::format(
|
|
|
|
"Got unexcpected response code from tinyfd_messageBox : {}",
|
|
|
|
response_code
|
|
|
|
));
|
2019-03-28 02:16:29 +01:00
|
|
|
}
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
2019-03-28 02:16:29 +01:00
|
|
|
|
2022-04-09 00:54:06 +02:00
|
|
|
EditorState::SaveOutcome EditorState::save_if_needed_and_user_wants_to() {
|
2022-04-02 04:10:09 +02:00
|
|
|
if (not needs_to_save()) {
|
|
|
|
return EditorState::SaveOutcome::NoSavingNeeded;
|
|
|
|
}
|
|
|
|
switch (ask_if_user_wants_to_save()) {
|
|
|
|
case EditorState::UserWantsToSave::Yes:
|
2022-11-11 00:06:58 +01:00
|
|
|
return save_asking_for_path();
|
2022-04-02 04:10:09 +02:00
|
|
|
case EditorState::UserWantsToSave::No:
|
|
|
|
return EditorState::SaveOutcome::UserDeclindedSaving;
|
2022-04-07 00:14:01 +02:00
|
|
|
default:
|
2022-04-02 04:10:09 +02:00
|
|
|
return EditorState::SaveOutcome::UserCanceled;
|
|
|
|
}
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
EditorState::SaveOutcome EditorState::save_if_needed() {
|
|
|
|
if (not needs_to_save()) {
|
|
|
|
return EditorState::SaveOutcome::NoSavingNeeded;
|
|
|
|
} else {
|
2022-11-11 00:06:58 +01:00
|
|
|
return save_asking_for_path();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
EditorState::SaveOutcome EditorState::save_asking_for_path() {
|
|
|
|
const auto path = ask_for_save_path_if_needed();
|
|
|
|
if (not path) {
|
|
|
|
return EditorState::SaveOutcome::UserCanceled;
|
|
|
|
} else {
|
|
|
|
save(*path);
|
|
|
|
return EditorState::SaveOutcome::UserSaved;
|
2022-04-06 15:47:04 +02:00
|
|
|
}
|
|
|
|
};
|
2022-04-02 04:10:09 +02:00
|
|
|
|
2022-04-01 02:30:32 +02:00
|
|
|
std::optional<std::filesystem::path> EditorState::ask_for_save_path_if_needed() {
|
|
|
|
if (song_path) {
|
|
|
|
return song_path;
|
|
|
|
} else {
|
2022-04-09 00:54:06 +02:00
|
|
|
return feis::save_file_dialog();
|
2022-04-01 02:30:32 +02:00
|
|
|
}
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2022-04-01 02:30:32 +02:00
|
|
|
|
2022-10-26 02:08:19 +02:00
|
|
|
void EditorState::insert_long_note_just_created() {
|
|
|
|
if (not chart_state) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
chart_state->insert_long_note_just_created(snap);
|
2022-10-26 22:49:13 +02:00
|
|
|
reload_sounds_that_depend_on_notes();
|
2022-10-26 02:08:19 +02:00
|
|
|
}
|
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
void EditorState::move_backwards_in_time() {
|
2022-04-16 05:25:03 +02:00
|
|
|
auto beats = current_snaped_beats();
|
|
|
|
if (beats >= current_exact_beats()) {
|
2022-03-23 02:20:07 +01:00
|
|
|
beats -= get_snap_step();
|
|
|
|
}
|
2022-04-16 05:25:03 +02:00
|
|
|
set_playback_position(beats);
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
void EditorState::move_forwards_in_time() {
|
2022-04-16 05:25:03 +02:00
|
|
|
auto beats = current_snaped_beats();
|
|
|
|
if (beats <= current_exact_beats()) {
|
|
|
|
beats += get_snap_step();
|
|
|
|
}
|
|
|
|
set_playback_position(beats);
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
void EditorState::undo(NotificationsQueue& nq) {
|
2022-10-13 00:59:40 +02:00
|
|
|
auto previous = history.pop_previous();
|
|
|
|
if (previous) {
|
|
|
|
nq.push(std::make_shared<UndoNotification>(**previous));
|
|
|
|
(*previous)->undo_action(*this);
|
2022-03-23 02:20:07 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void EditorState::redo(NotificationsQueue& nq) {
|
2022-10-13 00:59:40 +02:00
|
|
|
auto next = history.pop_next();
|
|
|
|
if (next) {
|
|
|
|
nq.push(std::make_shared<RedoNotification>(**next));
|
|
|
|
(*next)->do_action(*this);
|
2022-03-23 02:20:07 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-11-09 01:01:58 +01:00
|
|
|
void EditorState::cut(NotificationsQueue& nq) {
|
|
|
|
if (chart_state) {
|
|
|
|
chart_state->cut(nq, *applicable_timing, timing_origin());
|
|
|
|
reload_all_sounds();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::copy(NotificationsQueue& nq) {
|
|
|
|
if (chart_state) {
|
|
|
|
chart_state->copy(nq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::paste(NotificationsQueue& nq) {
|
|
|
|
if (chart_state) {
|
|
|
|
chart_state->paste(
|
|
|
|
current_snaped_beats(),
|
|
|
|
nq,
|
|
|
|
*applicable_timing,
|
|
|
|
timing_origin()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::delete_(NotificationsQueue& nq) {
|
|
|
|
if (chart_state) {
|
|
|
|
chart_state->delete_(nq, *applicable_timing, timing_origin());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Discard, in that order :
|
|
|
|
// - time selection
|
|
|
|
// - notes & bpm event selection
|
|
|
|
void EditorState::discard_selection() {
|
|
|
|
if (chart_state) {
|
|
|
|
if (chart_state->time_selection) {
|
|
|
|
chart_state->time_selection.reset();
|
|
|
|
} else if (not chart_state->selected_stuff.empty()) {
|
|
|
|
chart_state->selected_stuff.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-10 23:39:39 +01:00
|
|
|
void EditorState::insert_chart(const std::string& name, const better::Chart& chart) {
|
|
|
|
if (song.charts.try_emplace(name, chart).second) {
|
|
|
|
open_chart(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::insert_chart_and_push_history(const std::string& name, const better::Chart& chart) {
|
|
|
|
insert_chart(name, chart);
|
|
|
|
history.push(std::make_shared<AddChart>(name, chart));
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::erase_chart(const std::string& name) {
|
|
|
|
const auto& it = song.charts.find(name);
|
|
|
|
if (it != song.charts.end()) {
|
|
|
|
if (chart_state and name == chart_state->difficulty_name) {
|
|
|
|
close_chart();
|
|
|
|
}
|
|
|
|
song.charts.erase(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EditorState::erase_chart_and_push_history(const std::string& name) {
|
|
|
|
const auto& it = song.charts.find(name);
|
|
|
|
if (it != song.charts.end()) {
|
|
|
|
if (chart_state and name == chart_state->difficulty_name) {
|
|
|
|
close_chart();
|
|
|
|
}
|
|
|
|
history.push(std::make_shared<RemoveChart>(name, it->second));
|
|
|
|
song.charts.erase(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-04 04:30:02 +02:00
|
|
|
void EditorState::open_chart(const std::string& name) {
|
|
|
|
auto& [name_ref, chart] = *song.charts.find(name);
|
2022-11-20 00:02:14 +01:00
|
|
|
chart_state.emplace(chart, name_ref, history, assets, config);
|
2022-04-04 04:30:02 +02:00
|
|
|
reload_editable_range();
|
|
|
|
reload_applicable_timing();
|
2022-10-26 02:08:19 +02:00
|
|
|
reload_all_sounds();
|
2022-04-04 04:30:02 +02:00
|
|
|
};
|
|
|
|
|
2022-11-10 23:39:39 +01:00
|
|
|
void EditorState::close_chart() {
|
|
|
|
chart_state.reset();
|
|
|
|
};
|
|
|
|
|
2022-04-07 00:14:01 +02:00
|
|
|
void EditorState::update_visible_notes() {
|
|
|
|
if (chart_state) {
|
|
|
|
chart_state->update_visible_notes(
|
2022-04-16 05:25:03 +02:00
|
|
|
current_time(),
|
2022-10-26 02:08:19 +02:00
|
|
|
*applicable_timing
|
2022-04-07 00:14:01 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
|
|
|
|
void EditorState::reload_editable_range() {
|
2022-04-19 02:07:56 +02:00
|
|
|
const auto old_range = this->editable_range;
|
|
|
|
this->editable_range = choose_editable_range();
|
|
|
|
if (old_range != this->editable_range and this->chart_state.has_value()) {
|
|
|
|
chart_state->density_graph.should_recompute = true;
|
2022-03-23 02:20:07 +01:00
|
|
|
}
|
2022-04-19 02:07:56 +02:00
|
|
|
};
|
2022-03-23 02:20:07 +01:00
|
|
|
|
2022-04-19 02:07:56 +02:00
|
|
|
Interval<sf::Time> EditorState::choose_editable_range() {
|
|
|
|
Interval<sf::Time> new_range{sf::Time::Zero, sf::Time::Zero};
|
2022-06-01 00:26:36 +02:00
|
|
|
if (music.has_value()) {
|
2022-04-19 02:07:56 +02:00
|
|
|
// If there is music, allow editing up to the end, but no further
|
|
|
|
// You've put notes *after* the end of the music ? fuck 'em.
|
2022-06-01 00:26:36 +02:00
|
|
|
new_range += (**music).getDuration();
|
2022-04-19 02:07:56 +02:00
|
|
|
return new_range;
|
|
|
|
} else {
|
|
|
|
// If there is no music :
|
|
|
|
// make sure we can edit 10 seconds after the end of the current chart
|
2022-10-26 02:08:19 +02:00
|
|
|
if (chart_state and not chart_state->chart.notes->empty()) {
|
|
|
|
const auto beat_of_last_event = chart_state->chart.notes->crbegin()->second.get_end();
|
2022-04-19 02:07:56 +02:00
|
|
|
new_range += time_at(beat_of_last_event) + sf::seconds(10);
|
|
|
|
}
|
|
|
|
// and at at least the first whole minute in any case
|
2022-03-23 02:20:07 +01:00
|
|
|
new_range += sf::seconds(60);
|
2022-04-19 02:07:56 +02:00
|
|
|
return new_range;
|
2022-03-23 02:20:07 +01:00
|
|
|
}
|
2022-04-19 02:07:56 +02:00
|
|
|
}
|
2022-03-23 02:20:07 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reloads the album cover from what's indicated in the "album cover path" field
|
|
|
|
* of the song Resets the album cover state if anything fails
|
|
|
|
*/
|
|
|
|
void EditorState::reload_jacket() {
|
2022-03-31 03:50:15 +02:00
|
|
|
if (not song_path.has_value() or song.metadata.jacket.empty()) {
|
2022-03-23 02:20:07 +01:00
|
|
|
jacket.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
jacket.emplace();
|
2022-03-31 03:50:15 +02:00
|
|
|
auto jacket_path = song_path->parent_path() / song.metadata.jacket;
|
2022-03-23 02:20:07 +01:00
|
|
|
|
|
|
|
if (
|
|
|
|
not std::filesystem::exists(jacket_path)
|
2022-12-27 18:46:14 +01:00
|
|
|
or not jacket->loadFromFile(to_utf8_encoded_string(jacket_path))
|
2022-03-23 02:20:07 +01:00
|
|
|
) {
|
|
|
|
jacket.reset();
|
2022-04-19 23:13:18 +02:00
|
|
|
} else {
|
|
|
|
jacket->setSmooth(true);
|
2022-03-23 02:20:07 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reloads music from what's indicated in the "music path" field of the song
|
|
|
|
* Resets the music state in case anything fails
|
|
|
|
* Updates playbackPosition and preview_end as well
|
|
|
|
*/
|
|
|
|
void EditorState::reload_music() {
|
2022-10-11 01:54:43 +02:00
|
|
|
const auto status_before = get_status();
|
2022-03-31 03:50:15 +02:00
|
|
|
if (not song_path.has_value() or song.metadata.audio.empty()) {
|
2022-06-08 01:05:11 +02:00
|
|
|
clear_music();
|
2022-03-23 02:20:07 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-31 03:50:15 +02:00
|
|
|
const auto absolute_music_path = song_path->parent_path() / song.metadata.audio;
|
2022-06-01 00:26:36 +02:00
|
|
|
try {
|
|
|
|
music.emplace(std::make_shared<OpenMusic>(absolute_music_path));
|
|
|
|
} catch (const std::exception& e) {
|
2022-06-08 01:05:11 +02:00
|
|
|
clear_music();
|
2022-06-01 00:26:36 +02:00
|
|
|
}
|
2022-03-23 02:20:07 +01:00
|
|
|
|
2022-03-17 02:50:30 +01:00
|
|
|
reload_editable_range();
|
2022-04-19 23:13:18 +02:00
|
|
|
set_speed(speed);
|
2022-06-01 00:26:36 +02:00
|
|
|
if (music.has_value()) {
|
2022-09-30 01:33:34 +02:00
|
|
|
audio.add_stream(music_stream, {*music, false});
|
2022-06-01 00:26:36 +02:00
|
|
|
} else {
|
|
|
|
audio.remove_stream(music_stream);
|
|
|
|
}
|
2022-10-11 01:54:43 +02:00
|
|
|
pause();
|
|
|
|
set_playback_position(current_time());
|
|
|
|
switch (status_before) {
|
|
|
|
case sf::SoundSource::Playing:
|
|
|
|
play();
|
|
|
|
break;
|
|
|
|
case sf::SoundSource::Paused:
|
|
|
|
pause();
|
|
|
|
break;
|
|
|
|
case sf::SoundSource::Stopped:
|
|
|
|
stop();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-23 02:20:07 +01:00
|
|
|
};
|
|
|
|
|
2022-06-08 01:05:11 +02:00
|
|
|
void EditorState::clear_music() {
|
|
|
|
audio.remove_stream(music_stream);
|
|
|
|
music.reset();
|
|
|
|
}
|
|
|
|
|
2022-03-23 02:20:07 +01:00
|
|
|
void EditorState::reload_preview_audio() {
|
2022-03-31 03:50:15 +02:00
|
|
|
if (not song_path.has_value() or song.metadata.preview_file.empty()) {
|
2022-03-23 02:20:07 +01:00
|
|
|
preview_audio.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-31 03:50:15 +02:00
|
|
|
const auto path = song_path->parent_path() / song.metadata.preview_file;
|
2022-04-01 01:41:47 +02:00
|
|
|
preview_audio.emplace();
|
2022-12-27 18:57:48 +01:00
|
|
|
if (not preview_audio->openFromFile(to_utf8_encoded_string(path))) {
|
2022-03-23 02:20:07 +01:00
|
|
|
preview_audio.reset();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-26 02:08:19 +02:00
|
|
|
void EditorState::reload_sounds_that_depend_on_notes() {
|
|
|
|
std::map<std::string, NewStream> update;
|
|
|
|
const auto pitch = get_pitch();
|
|
|
|
std::shared_ptr<better::Notes> notes = [&](){
|
|
|
|
if (chart_state) {
|
|
|
|
return chart_state->chart.notes;
|
|
|
|
} else {
|
|
|
|
return std::shared_ptr<better::Notes>{};
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
note_claps = (
|
|
|
|
note_claps
|
|
|
|
->with_pitch(pitch)
|
|
|
|
->with_notes_and_timing(notes, applicable_timing)
|
|
|
|
);
|
|
|
|
if (audio.contains_stream(note_clap_stream)) {
|
|
|
|
update[note_clap_stream] = {note_claps, true};
|
|
|
|
}
|
|
|
|
chord_claps = (
|
|
|
|
chord_claps
|
|
|
|
->with_pitch(pitch)
|
|
|
|
->with_notes_and_timing(notes, applicable_timing)
|
|
|
|
);
|
|
|
|
if (audio.contains_stream(chord_clap_stream)) {
|
|
|
|
update[chord_clap_stream] = {chord_claps, true};
|
|
|
|
}
|
|
|
|
audio.update_streams(update, {}, pitch);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::reload_sounds_that_depend_on_timing() {
|
|
|
|
std::map<std::string, NewStream> update;
|
|
|
|
const auto pitch = get_pitch();
|
|
|
|
std::shared_ptr<better::Notes> notes = [&](){
|
|
|
|
if (chart_state) {
|
|
|
|
return chart_state->chart.notes;
|
|
|
|
} else {
|
|
|
|
return std::shared_ptr<better::Notes>{};
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
note_claps = (
|
|
|
|
note_claps
|
|
|
|
->with_pitch(pitch)
|
|
|
|
->with_notes_and_timing(notes, applicable_timing)
|
|
|
|
);
|
|
|
|
if (audio.contains_stream(note_clap_stream)) {
|
|
|
|
update[note_clap_stream] = {note_claps, true};
|
|
|
|
}
|
|
|
|
|
|
|
|
beat_ticks = (
|
|
|
|
beat_ticks
|
|
|
|
->with_pitch(get_pitch())
|
|
|
|
->with_timing(applicable_timing)
|
|
|
|
);
|
|
|
|
if (audio.contains_stream(beat_tick_stream)) {
|
|
|
|
update[beat_tick_stream] = {beat_ticks, true};
|
|
|
|
}
|
|
|
|
chord_claps = (
|
|
|
|
chord_claps
|
|
|
|
->with_pitch(pitch)
|
|
|
|
->with_notes_and_timing(notes, applicable_timing)
|
|
|
|
);
|
|
|
|
if (audio.contains_stream(chord_clap_stream)) {
|
|
|
|
update[chord_clap_stream] = {chord_claps, true};
|
|
|
|
}
|
|
|
|
audio.update_streams(update, {}, pitch);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorState::reload_all_sounds() {
|
|
|
|
reload_sounds_that_depend_on_timing();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-03-31 03:50:15 +02:00
|
|
|
void EditorState::reload_applicable_timing() {
|
2022-04-04 04:30:02 +02:00
|
|
|
if (chart_state and chart_state->chart.timing) {
|
|
|
|
applicable_timing = *chart_state->chart.timing;
|
2022-03-31 03:50:15 +02:00
|
|
|
} else {
|
|
|
|
applicable_timing = song.timing;
|
|
|
|
}
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2022-03-17 02:50:30 +01:00
|
|
|
|
2022-10-26 23:35:00 +02:00
|
|
|
TimingOrigin EditorState::timing_origin() {
|
|
|
|
if (chart_state and chart_state->chart.timing) {
|
|
|
|
return chart_state->difficulty_name;
|
|
|
|
} else {
|
|
|
|
return GlobalTimingObject{};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-01 01:41:47 +02:00
|
|
|
void EditorState::save(const std::filesystem::path& path) {
|
2022-04-02 04:10:09 +02:00
|
|
|
const auto memon = song.dump_to_memon_1_0_0();
|
2022-12-27 14:26:59 +01:00
|
|
|
nowide::ofstream file{path.string()};
|
2022-04-01 01:41:47 +02:00
|
|
|
if (not file) {
|
|
|
|
throw std::runtime_error(
|
|
|
|
fmt::format("Cannot write to file {}", path.string())
|
|
|
|
);
|
|
|
|
}
|
|
|
|
file << memon.dump(4) << std::endl;
|
|
|
|
file.close();
|
|
|
|
if (not file) {
|
|
|
|
throw std::runtime_error(
|
|
|
|
fmt::format("Error while closing file {}", path.string())
|
|
|
|
);
|
|
|
|
}
|
2022-04-09 00:54:06 +02:00
|
|
|
song_path = path;
|
2022-10-13 00:59:40 +02:00
|
|
|
history.mark_as_saved();
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2019-01-13 03:53:42 +01:00
|
|
|
|
2022-11-11 00:06:58 +01:00
|
|
|
void feis::force_save(
|
2022-04-09 00:54:06 +02:00
|
|
|
std::optional<EditorState>& ed,
|
|
|
|
NotificationsQueue& nq
|
|
|
|
) {
|
|
|
|
if (ed) {
|
2022-11-11 00:06:58 +01:00
|
|
|
if (ed->save_asking_for_path() == EditorState::SaveOutcome::UserSaved) {
|
2022-04-09 00:54:06 +02:00
|
|
|
nq.push(std::make_shared<TextNotification>("Saved file"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SAVE if needed and the user asked to, then ASK for a file to opne, then OPEN than file
|
|
|
|
void feis::save_ask_open(
|
|
|
|
std::optional<EditorState>& ed,
|
|
|
|
const std::filesystem::path& assets,
|
2022-11-19 01:43:37 +01:00
|
|
|
const std::filesystem::path& settings,
|
|
|
|
config::Config& config
|
2022-04-09 00:54:06 +02:00
|
|
|
) {
|
|
|
|
if (ed and ed->save_if_needed_and_user_wants_to() == EditorState::SaveOutcome::UserCanceled) {
|
2022-04-07 00:14:01 +02:00
|
|
|
return;
|
2022-04-06 15:47:04 +02:00
|
|
|
}
|
|
|
|
|
2022-04-09 00:54:06 +02:00
|
|
|
if (const auto& filepath = feis::open_file_dialog()) {
|
2022-11-19 01:43:37 +01:00
|
|
|
feis::open_from_file(ed, *filepath, assets, settings, config);
|
2022-04-09 00:54:06 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// SAVE if needed and the user asked to, then OPEN the file passed as argument
|
|
|
|
void feis::save_open(
|
|
|
|
std::optional<EditorState>& ed,
|
|
|
|
const std::filesystem::path& song_path,
|
|
|
|
const std::filesystem::path& assets,
|
2022-11-19 01:43:37 +01:00
|
|
|
const std::filesystem::path& settings,
|
|
|
|
config::Config& config
|
2022-04-09 00:54:06 +02:00
|
|
|
) {
|
|
|
|
if (ed and ed->save_if_needed_and_user_wants_to() == EditorState::SaveOutcome::UserCanceled) {
|
|
|
|
return;
|
2019-01-13 03:53:42 +01:00
|
|
|
}
|
2022-04-09 00:54:06 +02:00
|
|
|
|
2022-11-19 01:43:37 +01:00
|
|
|
feis::open_from_file(ed, song_path, assets, settings, config);
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2019-01-13 03:53:42 +01:00
|
|
|
|
2022-04-09 00:54:06 +02:00
|
|
|
|
2022-04-03 15:59:05 +02:00
|
|
|
void feis::open_from_file(
|
2022-01-04 01:31:17 +01:00
|
|
|
std::optional<EditorState>& ed,
|
2022-04-09 00:54:06 +02:00
|
|
|
const std::filesystem::path& song_path,
|
|
|
|
const std::filesystem::path& assets,
|
2022-11-19 01:43:37 +01:00
|
|
|
const std::filesystem::path& settings,
|
|
|
|
config::Config& config
|
2022-01-04 01:31:17 +01:00
|
|
|
) {
|
2019-01-13 03:53:42 +01:00
|
|
|
try {
|
2022-04-09 00:54:06 +02:00
|
|
|
// force utf-8 song path on windows
|
2022-04-04 04:30:02 +02:00
|
|
|
nowide::ifstream f{song_path.string()};
|
|
|
|
if (not f) {
|
|
|
|
tinyfd_messageBox(
|
|
|
|
"Error",
|
|
|
|
fmt::format("Could not open file {}", song_path.string()).c_str(),
|
|
|
|
"ok",
|
|
|
|
"error",
|
|
|
|
1
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
};
|
2022-04-16 02:26:37 +02:00
|
|
|
const auto json = load_json_preserving_decimals(f);
|
2022-04-04 04:30:02 +02:00
|
|
|
auto song = better::Song::load_from_memon(json);
|
2022-11-19 01:43:37 +01:00
|
|
|
ed.emplace(song, assets, song_path, config);
|
2022-04-04 04:30:02 +02:00
|
|
|
Toolbox::pushNewRecentFile(std::filesystem::canonical(song_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);
|
|
|
|
}
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2019-01-16 01:59:02 +01:00
|
|
|
|
2022-04-09 00:54:06 +02:00
|
|
|
void feis::save_close(std::optional<EditorState>& ed) {
|
|
|
|
if (ed and ed->save_if_needed_and_user_wants_to() == EditorState::SaveOutcome::UserCanceled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ed.reset();
|
|
|
|
}
|
|
|
|
|
2019-03-28 10:54:50 +01:00
|
|
|
/*
|
|
|
|
* Returns the newly created chart if there is one
|
2019-01-16 01:59:02 +01:00
|
|
|
*/
|
2022-04-04 04:30:02 +02:00
|
|
|
std::optional<std::pair<std::string, better::Chart>> feis::NewChartDialog::display(EditorState& editorState) {
|
2019-01-16 01:59:02 +01:00
|
|
|
if (ImGui::Begin(
|
|
|
|
"New Chart",
|
2022-10-11 21:23:29 +02:00
|
|
|
&editorState.show_new_chart_dialog,
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
|
2022-04-04 04:30:02 +02:00
|
|
|
if (show_custom_dif_name) {
|
|
|
|
combo_preview = "Custom";
|
2019-01-16 01:59:02 +01:00
|
|
|
} else {
|
|
|
|
if (difficulty.empty()) {
|
2022-04-04 04:30:02 +02:00
|
|
|
combo_preview = "Choose One";
|
2019-01-16 01:59:02 +01:00
|
|
|
} else {
|
2022-04-04 04:30:02 +02:00
|
|
|
combo_preview = difficulty;
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
|
|
|
}
|
2022-04-04 04:30:02 +02:00
|
|
|
if (ImGui::BeginCombo("Difficulty", combo_preview.c_str())) {
|
2021-12-31 14:59:39 +01:00
|
|
|
for (auto dif_name : {"BSC", "ADV", "EXT"}) {
|
2022-04-04 04:30:02 +02:00
|
|
|
if (not editorState.song.charts.contains(dif_name)) {
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::Selectable(dif_name, dif_name == difficulty)) {
|
2022-04-04 04:30:02 +02:00
|
|
|
show_custom_dif_name = false;
|
2019-01-16 01:59:02 +01:00
|
|
|
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();
|
2022-04-04 04:30:02 +02:00
|
|
|
if (ImGui::Selectable("Custom", &show_custom_dif_name)) {
|
2019-01-16 01:59:02 +01:00
|
|
|
difficulty = "";
|
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
2022-04-04 04:30:02 +02:00
|
|
|
if (show_custom_dif_name) {
|
|
|
|
feis::InputTextColored(
|
2021-12-31 14:59:39 +01:00
|
|
|
"Difficulty Name",
|
2022-04-04 04:30:02 +02:00
|
|
|
&difficulty,
|
|
|
|
not editorState.song.charts.contains(difficulty),
|
|
|
|
"Chart name has to be unique"
|
|
|
|
);
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
2022-04-04 04:30:02 +02:00
|
|
|
feis::InputDecimal("Level", &level);
|
2019-01-16 13:23:36 +01:00
|
|
|
ImGui::Separator();
|
2022-04-04 04:30:02 +02:00
|
|
|
ImGui::BeginDisabled(difficulty.empty() or (editorState.song.charts.contains(difficulty)));
|
|
|
|
if (ImGui::Button("Create Chart##New Chart")) {
|
|
|
|
ImGui::EndDisabled();
|
|
|
|
ImGui::End();
|
|
|
|
try {
|
|
|
|
return {{difficulty, {.level = level}}};
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
tinyfd_messageBox("Error", e.what(), "ok", "error", 1);
|
|
|
|
return {};
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
2022-04-04 04:30:02 +02:00
|
|
|
};
|
|
|
|
ImGui::EndDisabled();
|
2019-01-16 01:59:02 +01:00
|
|
|
}
|
|
|
|
ImGui::End();
|
2022-04-04 04:30:02 +02:00
|
|
|
return {};
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2019-01-16 19:12:01 +01:00
|
|
|
|
2022-04-04 04:30:02 +02:00
|
|
|
void feis::ChartPropertiesDialog::display(EditorState& editor_state) {
|
|
|
|
assert(editor_state.chart_state.has_value());
|
2019-01-16 19:12:01 +01:00
|
|
|
|
2022-04-04 04:30:02 +02:00
|
|
|
if (this->should_refresh_values) {
|
|
|
|
should_refresh_values = false;
|
2019-01-16 19:12:01 +01:00
|
|
|
|
2022-04-04 04:30:02 +02:00
|
|
|
difficulty_names_in_use.clear();
|
|
|
|
this->level = editor_state.chart_state->chart.level.value_or(0);
|
|
|
|
this->difficulty_name = editor_state.chart_state->difficulty_name;
|
|
|
|
this->show_custom_dif_name = (
|
2022-04-19 23:13:18 +02:00
|
|
|
difficulty_name != "BSC"
|
|
|
|
and difficulty_name != "ADV"
|
|
|
|
and difficulty_name != "EXT"
|
2022-04-04 04:30:02 +02:00
|
|
|
);
|
2019-01-16 19:12:01 +01:00
|
|
|
|
2022-04-04 04:30:02 +02:00
|
|
|
for (auto const& [name, _] : editor_state.song.charts) {
|
|
|
|
if (name != editor_state.chart_state->difficulty_name) {
|
|
|
|
difficulty_names_in_use.insert(name);
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImGui::Begin(
|
|
|
|
"Chart Properties",
|
2022-10-11 21:23:29 +02:00
|
|
|
&editor_state.show_chart_properties,
|
2021-12-31 14:59:39 +01:00
|
|
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
|
2022-04-04 04:30:02 +02:00
|
|
|
if (show_custom_dif_name) {
|
|
|
|
combo_preview = "Custom";
|
2019-01-16 19:12:01 +01:00
|
|
|
} else {
|
2019-03-02 13:47:26 +01:00
|
|
|
if (difficulty_name.empty()) {
|
2022-04-04 04:30:02 +02:00
|
|
|
combo_preview = "Choose One";
|
2019-01-16 19:12:01 +01:00
|
|
|
} else {
|
2022-04-04 04:30:02 +02:00
|
|
|
combo_preview = difficulty_name;
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
|
|
|
}
|
2022-04-04 04:30:02 +02:00
|
|
|
if (ImGui::BeginCombo("Difficulty", combo_preview.c_str())) {
|
2021-12-31 14:59:39 +01:00
|
|
|
for (auto dif_name : {"BSC", "ADV", "EXT"}) {
|
2022-04-04 04:30:02 +02:00
|
|
|
if (not difficulty_names_in_use.contains(dif_name)) {
|
2021-12-31 14:59:39 +01:00
|
|
|
if (ImGui::Selectable(dif_name, dif_name == difficulty_name)) {
|
2022-04-04 04:30:02 +02:00
|
|
|
show_custom_dif_name = 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();
|
2022-04-04 04:30:02 +02:00
|
|
|
if (ImGui::Selectable("Custom", &show_custom_dif_name)) {
|
2019-03-02 13:47:26 +01:00
|
|
|
difficulty_name = "";
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
2022-04-04 04:30:02 +02:00
|
|
|
if (show_custom_dif_name) {
|
|
|
|
feis::InputTextColored(
|
2021-12-31 14:59:39 +01:00
|
|
|
"Difficulty Name",
|
2022-04-04 04:30:02 +02:00
|
|
|
&difficulty_name,
|
|
|
|
not difficulty_names_in_use.contains(difficulty_name),
|
|
|
|
"Chart name has to be unique"
|
|
|
|
);
|
2019-01-16 19:12:01 +01:00
|
|
|
}
|
2022-04-04 04:30:02 +02:00
|
|
|
feis::InputDecimal("Level", &level);
|
2019-01-16 19:12:01 +01:00
|
|
|
ImGui::Separator();
|
2021-12-31 14:59:39 +01:00
|
|
|
if (difficulty_name.empty()
|
2022-04-04 04:30:02 +02:00
|
|
|
or (difficulty_names_in_use.find(difficulty_name) != difficulty_names_in_use.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")) {
|
2022-10-13 00:59:40 +02:00
|
|
|
const auto old_dif_name = editor_state.chart_state->difficulty_name;
|
|
|
|
const auto old_level = editor_state.chart_state->chart.level;
|
2019-01-16 19:12:01 +01:00
|
|
|
try {
|
2022-04-04 04:30:02 +02:00
|
|
|
auto modified_chart = editor_state.song.charts.extract(editor_state.chart_state->difficulty_name);
|
|
|
|
modified_chart.key() = this->difficulty_name;
|
|
|
|
modified_chart.mapped().level = this->level;
|
|
|
|
const auto [_1, inserted, _2] = editor_state.song.charts.insert(std::move(modified_chart));
|
|
|
|
if (not inserted) {
|
|
|
|
throw std::runtime_error("Could not insert modified chart in song");
|
2019-01-16 19:12:01 +01:00
|
|
|
} else {
|
2022-04-04 04:30:02 +02:00
|
|
|
editor_state.open_chart(this->difficulty_name);
|
2022-10-13 00:59:40 +02:00
|
|
|
if (old_dif_name != this->difficulty_name) {
|
|
|
|
editor_state.history.push(
|
|
|
|
std::make_shared<RenameChart>(
|
|
|
|
old_dif_name,
|
|
|
|
this->difficulty_name
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (old_level != this->level) {
|
|
|
|
editor_state.history.push(
|
|
|
|
std::make_shared<RerateChart>(
|
|
|
|
this->difficulty_name,
|
|
|
|
old_level,
|
|
|
|
this->level
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2022-04-04 04:30:02 +02:00
|
|
|
should_refresh_values = true;
|
2019-01-16 19:12:01 +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 19:12:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
2022-04-06 15:47:04 +02:00
|
|
|
};
|
2022-11-15 01:14:03 +01:00
|
|
|
|
|
|
|
void feis::display_shortcuts_help(bool& show) {
|
|
|
|
const auto table_shortcut = [](const char* action, const char* keys){
|
|
|
|
ImGui::TableNextRow();
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::TextUnformatted(action);
|
|
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::TextUnformatted(keys);
|
|
|
|
};
|
2022-11-22 00:24:48 +01:00
|
|
|
const auto table_header = [&](){
|
|
|
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 175.f);
|
|
|
|
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
|
|
|
|
};
|
2022-11-15 22:04:29 +01:00
|
|
|
if (ImGui::Begin("Shortcuts", &show, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) {
|
2022-11-15 01:14:03 +01:00
|
|
|
ImGui::Text("Navigation");
|
|
|
|
if (
|
|
|
|
ImGui::BeginTable(
|
|
|
|
"Navigation",
|
|
|
|
2,
|
|
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
|
|
|
)
|
|
|
|
) {
|
2022-11-22 00:24:48 +01:00
|
|
|
table_header();
|
2022-11-15 01:14:03 +01:00
|
|
|
table_shortcut("Play / Pause", "Space");
|
|
|
|
table_shortcut("Move Backwards In Time", "Down");
|
|
|
|
table_shortcut("Move Forwards In Time", "Up");
|
|
|
|
table_shortcut("Decrease Snap", "Left");
|
|
|
|
table_shortcut("Increase Snap", "Right");
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
2022-11-22 00:24:48 +01:00
|
|
|
ImGui::Text("Playfield");
|
|
|
|
if (
|
|
|
|
ImGui::BeginTable(
|
|
|
|
"Playfield",
|
|
|
|
2,
|
|
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
table_header();
|
|
|
|
table_shortcut("Show Free Buttons", "F");
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
2022-11-15 01:14:03 +01:00
|
|
|
|
|
|
|
ImGui::Text("Linear View");
|
|
|
|
if (
|
|
|
|
ImGui::BeginTable(
|
|
|
|
"Linear View",
|
|
|
|
2,
|
|
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
|
|
|
)
|
|
|
|
) {
|
2022-11-22 00:24:48 +01:00
|
|
|
table_header();
|
2022-11-15 01:14:03 +01:00
|
|
|
table_shortcut("Zoom In", "Numpad +");
|
|
|
|
table_shortcut("Zoom Out", "Numpad -");
|
|
|
|
table_shortcut("Set Time Selection Bounds", "Tab");
|
|
|
|
table_shortcut("Discard Selection", "Escape");
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Text("Editing");
|
|
|
|
if (
|
|
|
|
ImGui::BeginTable(
|
|
|
|
"Editing",
|
|
|
|
2,
|
|
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
|
|
|
)
|
|
|
|
) {
|
2022-11-22 00:24:48 +01:00
|
|
|
table_header();
|
2022-11-15 01:14:03 +01:00
|
|
|
table_shortcut("Cut", "Ctrl+X");
|
|
|
|
table_shortcut("Copy", "Ctrl+C");
|
|
|
|
table_shortcut("Paste", "Ctrl+V");
|
|
|
|
table_shortcut("Delete", "Delete");
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::Text("Sound");
|
|
|
|
if (
|
|
|
|
ImGui::BeginTable(
|
|
|
|
"Sound",
|
|
|
|
2,
|
|
|
|
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg
|
|
|
|
)
|
|
|
|
) {
|
2022-11-22 00:24:48 +01:00
|
|
|
table_header();
|
2022-11-15 01:14:03 +01:00
|
|
|
table_shortcut("Increase Music Volume", "Shift+Up");
|
|
|
|
table_shortcut("Decrease Music Volume", "Shift+Down");
|
|
|
|
table_shortcut("Slow Down Playback", "Shift+Left");
|
|
|
|
table_shortcut("Speed Up Playback", "Shift+Right");
|
|
|
|
table_shortcut("Toggle Beat Tick", "F3");
|
|
|
|
table_shortcut("Toggle Note Tick", "F4");
|
|
|
|
table_shortcut("Toggle Chord Tick", "Shift+F4");
|
|
|
|
ImGui::EndTable();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
};
|
2022-11-15 22:04:29 +01:00
|
|
|
|
|
|
|
const std::vector<std::vector<sf::Vector2f>> lines = {
|
|
|
|
{{1, 5}, {1, 1}, {4, 1}}, // F
|
|
|
|
{{1, 3}, {3, 3}},
|
|
|
|
{{4.8f, 4.8f}, {5.2f, 4.8f}, {5.2f, 5.2f}, {4.8f, 5.2f}, {4.8f, 4.8f}}, // .
|
|
|
|
{{9, 5}, {6, 5}, {6, 1}, {9, 1}}, // E
|
|
|
|
{{6, 3}, {8, 3}},
|
|
|
|
{{9.8f, 4.8f}, {10.2f, 4.8f}, {10.2f, 5.2f}, {9.8f, 5.2f}, {9.8f, 4.8f}}, // .
|
|
|
|
{{11, 1}, {14, 1}}, // I
|
|
|
|
{{12.5f, 1}, {12.5f, 5}},
|
|
|
|
{{11, 5}, {14, 5}},
|
|
|
|
{{14.8f, 4.8f}, {15.2f, 4.8f}, {15.2f, 5.2f}, {14.8f, 5.2f}, {14.8f, 4.8f}}, // .
|
|
|
|
{{16, 4.5f}, {16, 5}, {19, 5}, {19, 3}, {16, 3}, {16, 1}, {19, 1}, {19, 1.5f}} // S
|
|
|
|
};
|
|
|
|
|
|
|
|
const std::vector<float> random_offsets = {
|
|
|
|
+0.34f, -0.11f, +0.28f, -0.13f, -0.09f,
|
|
|
|
+0.03f, +0.22f, +0.01f, +0.13f, +0.15f,
|
|
|
|
+0.23f, -0.16f, -0.28f, +0.08f, -0.23f,
|
|
|
|
+0.05f, +0.22f, -0.03f, +0.22f, +0.46f
|
|
|
|
};
|
|
|
|
|
|
|
|
void feis::display_about_menu(bool &show) {
|
|
|
|
static float scale = 10.0f;
|
|
|
|
static float noise_scale = 0.65f;
|
|
|
|
static sf::Vector2f offset = {19, 60};
|
|
|
|
static sf::Vector2f dummy_size = {225, 125};
|
|
|
|
static std::size_t index = 0;
|
|
|
|
/*
|
|
|
|
if (ImGui::Begin("About Debug")) {
|
|
|
|
ImGui::SliderFloat("Scale", &scale, 0, 10);
|
|
|
|
ImGui::SliderFloat("Noise scale", &noise_scale, 0, 1, "%.3f", ImGuiSliderFlags_Logarithmic);
|
|
|
|
ImGui::SliderFloat2("Offset", &offset.x, 0, 200);
|
|
|
|
ImGui::SliderFloat2("Dummy", &dummy_size.x, 0, 300);
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
*/
|
|
|
|
if (ImGui::Begin("About", &show, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) {
|
|
|
|
ImGui::Dummy(dummy_size);
|
|
|
|
auto drawlist = ImGui::GetWindowDrawList();
|
|
|
|
auto origin = sf::Vector2f{ImGui::GetWindowPos()};
|
|
|
|
for (const auto& points : lines) {
|
|
|
|
for (auto point = points.begin(); point != points.end(); ++point) {
|
|
|
|
auto next_point = std::next(point);
|
|
|
|
if (next_point != points.end()) {
|
|
|
|
const auto random_offset = sf::Vector2f{
|
|
|
|
random_offsets.at(index % random_offsets.size()),
|
|
|
|
random_offsets.at((index + 1) % random_offsets.size())
|
|
|
|
};
|
|
|
|
const auto next_random_offset = sf::Vector2f{
|
|
|
|
random_offsets.at((index + 2) % random_offsets.size()),
|
|
|
|
random_offsets.at((index + 3) % random_offsets.size()),
|
|
|
|
};
|
|
|
|
drawlist->AddLine(
|
|
|
|
origin +((*point + (random_offset * noise_scale)) * scale) + offset,
|
|
|
|
origin +((*next_point + (next_random_offset * noise_scale)) * scale) + offset,
|
|
|
|
ImColor(sf::Color::White)
|
|
|
|
);
|
|
|
|
index += 2;
|
|
|
|
index %= random_offsets.size();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
feis::CenteredText(fmt::format("{} {}", FEIS_NAME, FEIS_VERSION));
|
|
|
|
feis::CenteredText("Made with <3 by Stepland");
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|