Undo/Redo feature done !

- History cannot give back its last previous state
- HistoryAction.cpp has everything you need to define actions stored in history
- Added a History dialog showing the contents of the history
This commit is contained in:
Stepland 2019-03-02 13:47:26 +01:00
parent 07def1295f
commit d623ff74ae
9 changed files with 404 additions and 81 deletions

View File

@ -3,21 +3,27 @@ project(FEIS)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_EXTENSIONS ON)
set(OpenGL_GL_PREFERENCE GLVND)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g3")
SET(GCC_COVERAGE_COMPILE_FLAGS "-mwindows")
SET(GCC_COVERAGE_LINK_FLAGS "-mwindows")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS} -static-libgcc -static-libstdc++")
if (WIN32)
message("Compiling for windows !")
SET(GCC_COVERAGE_COMPILE_FLAGS "${GCC_COVERAGE_COMPILE_FLAGS} -mwindows")
SET(GCC_COVERAGE_LINK_FLAGS "${GCC_COVERAGE_LINK_FLAGS} -mwindows")
endif(WIN32)
set(imgui imgui/imgui.cpp
imgui/imgui_draw.cpp
imgui/imgui_widgets.cpp
imgui/imgui-SFML.cpp
imgui/imgui_stdlib.cpp
imgui/imgui_demo.cpp)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
set(imgui include/imgui/imgui.cpp
include/imgui/imgui_draw.cpp
include/imgui/imgui_widgets.cpp
include/imgui/imgui-SFML.cpp
include/imgui/imgui_stdlib.cpp
include/imgui/imgui_demo.cpp)
set(SOURCE_FILES
main.cpp
@ -28,7 +34,7 @@ set(SOURCE_FILES
Chart.cpp
EditorState.cpp
${imgui}
tinyfiledialogs.c Toolbox.cpp Toolbox.h LNMarker.cpp LNMarker.h History.h)
tinyfiledialogs.c Toolbox.cpp Toolbox.h LNMarker.cpp LNMarker.h History.h HistoryActions.cpp HistoryActions.h)
#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${FEIS_SOURCE_DIR}/cmake")
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH})
@ -37,12 +43,15 @@ find_library (WinMM libwinmm.a)
find_package (SFML 2.5.1 COMPONENTS system window graphics network audio REQUIRED)
find_package (OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIR} imgui/)
include_directories(${OPENGL_INCLUDE_DIR} include/imgui/)
add_executable(FEIS ${SOURCE_FILES})
target_link_libraries(FEIS stdc++fs ${OPENGL_LIBRARIES} sfml-system sfml-window sfml-graphics sfml-network sfml-audio WinMM)
add_executable(ImGuiDemo ImGuiDemoMain.cpp ${imgui})
set(TARGET_LINK_LIBS ${OPENGL_LIBRARIES} stdc++ stdc++fs sfml-system sfml-window sfml-graphics sfml-network sfml-audio)
target_link_libraries(ImGuiDemo ${OPENGL_LIBRARIES} sfml-system sfml-window sfml-graphics sfml-network sfml-audio WinMM)
if(WIN32)
set(TARGET_LINK_LIBS ${TARGET_LINK_LIBS} WinMM)
endif(WIN32)
target_link_libraries(FEIS ${TARGET_LINK_LIBS})

View File

@ -18,9 +18,9 @@ EditorState::EditorState(Fumen &fumen) : fumen(fumen) {
void EditorState::reloadFromFumen() {
if (not this->fumen.Charts.empty()) {
this->selectedChart = this->fumen.Charts.begin()->second;
this->chart.emplace(this->fumen.Charts.begin()->second);
} else {
this->selectedChart.reset();
this->chart.reset();
}
reloadMusic();
reloadJacket();
@ -50,14 +50,14 @@ void EditorState::reloadPlaybackPositionAndChartRuntime() {
playbackPosition = sf::seconds(-(fumen.offset));
previousPos = playbackPosition;
if (music) {
if (selectedChart) {
chartRuntime = sf::seconds(std::max(music->getDuration().asSeconds(),fumen.getChartRuntime(*selectedChart)-fumen.offset)+2.f);
if (chart) {
chartRuntime = sf::seconds(std::max(music->getDuration().asSeconds(),fumen.getChartRuntime(chart->ref)-fumen.offset)+2.f);
} else {
chartRuntime = sf::seconds(std::max(-fumen.offset,music->getDuration().asSeconds()));
}
} else {
if (selectedChart) {
chartRuntime = sf::seconds(std::max(fumen.getChartRuntime(*selectedChart)-fumen.offset,2.f));
if (chart) {
chartRuntime = sf::seconds(std::max(fumen.getChartRuntime(chart->ref)-fumen.offset,2.f));
} else {
chartRuntime = sf::seconds(std::max(-fumen.offset,2.f));
}
@ -106,7 +106,7 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
float squareSize = ImGui::GetWindowSize().x / 4.f;
float TitlebarHeight = ImGui::GetWindowSize().y - ImGui::GetWindowSize().x;
if (selectedChart) {
if (chart) {
int ImGuiIndex = 0;
@ -403,8 +403,8 @@ void EditorState::displayPlaybackStatus() {
|ImGuiWindowFlags_AlwaysAutoResize
);
{
if (selectedChart) {
ImGui::Text("%s %d",selectedChart->get().dif_name.c_str(),selectedChart->get().level); ImGui::SameLine();
if (chart) {
ImGui::Text("%s %d",chart->ref.dif_name.c_str(),chart->ref.level); ImGui::SameLine();
} else {
ImGui::TextDisabled("No chart selected"); ImGui::SameLine();
}
@ -469,8 +469,8 @@ void EditorState::displayChartList() {
ImGui::TextDisabled("Note Count"); ImGui::NextColumn();
ImGui::Separator();
for (auto& tuple : fumen.Charts) {
if (ImGui::Selectable(tuple.first.c_str(), selectedChart ? selectedChart->get()==tuple.second : false , ImGuiSelectableFlags_SpanAllColumns)) {
selectedChart = tuple.second;
if (ImGui::Selectable(tuple.first.c_str(), chart ? chart->ref==tuple.second : false , ImGuiSelectableFlags_SpanAllColumns)) {
chart.emplace(tuple.second);
}
ImGui::NextColumn();
ImGui::Text("%d",tuple.second.level); ImGui::NextColumn();
@ -490,11 +490,11 @@ void EditorState::updateVisibleNotes() {
visibleNotes.clear();
if (selectedChart) {
if (chart) {
float position = playbackPosition.asSeconds();
for (auto const& note : selectedChart->get().Notes) {
for (auto const& note : chart->ref.Notes) {
float note_timing_in_seconds = getSecondsAt(note.getTiming());
@ -520,18 +520,27 @@ void EditorState::updateVisibleNotes() {
* Otherwise create note at nearest tick
*/
void EditorState::toggleNoteAtCurrentTime(int pos) {
if (selectedChart) {
if (chart) {
std::set<Note> toggledNotes = {};
bool deleted_something = false;
for (auto note : visibleNotes) {
if (note.getPos() == pos) {
selectedChart->get().Notes.erase(note);
toggledNotes.insert(note);
chart->ref.Notes.erase(note);
deleted_something = true;
}
}
if (not deleted_something) {
selectedChart->get().Notes.emplace(pos,static_cast<int>(roundf(getTicks())));
toggledNotes.emplace(pos,static_cast<int>(roundf(getTicks())));
chart->ref.Notes.emplace(pos,static_cast<int>(roundf(getTicks())));
}
chart->history.push(std::make_shared<ToggledNotes>(toggledNotes, not deleted_something));
}
}
void ESHelper::save(EditorState& ed) {
@ -651,20 +660,20 @@ std::optional<Chart> ESHelper::NewChartDialog::display(EditorState &editorState)
void ESHelper::ChartPropertiesDialog::display(EditorState &editorState) {
assert(editorState.selectedChart.has_value());
assert(editorState.chart.has_value());
if (this->shouldRefreshValues) {
shouldRefreshValues = false;
difNamesInUse.clear();
this->level = editorState.selectedChart->get().level;
this->difficulty = editorState.selectedChart->get().dif_name;
this->level = editorState.chart->ref.level;
this->difficulty_name = editorState.chart->ref.dif_name;
std::set<std::string> difNames{"BSC","ADV","EXT"};
showCustomDifName = (difNames.find(difficulty) == difNames.end());
showCustomDifName = (difNames.find(difficulty_name) == difNames.end());
for (auto const& tuple : editorState.fumen.Charts) {
if (tuple.second != editorState.selectedChart) {
if (tuple.second != editorState.chart->ref) {
difNamesInUse.insert(tuple.first);
}
}
@ -680,18 +689,18 @@ void ESHelper::ChartPropertiesDialog::display(EditorState &editorState) {
if (showCustomDifName) {
comboPreview = "Custom";
} else {
if (difficulty.empty()) {
if (difficulty_name.empty()) {
comboPreview = "Choose One";
} else {
comboPreview = difficulty;
comboPreview = difficulty_name;
}
}
if(ImGui::BeginCombo("Difficulty",comboPreview.c_str())) {
for (auto dif_name : {"BSC","ADV","EXT"}) {
if (difNamesInUse.find(dif_name) == difNamesInUse.end()) {
if(ImGui::Selectable(dif_name,dif_name == difficulty)) {
if(ImGui::Selectable(dif_name,dif_name == difficulty_name)) {
showCustomDifName = false;
difficulty = dif_name;
difficulty_name = dif_name;
}
} else {
ImGui::TextDisabled(dif_name);
@ -699,21 +708,21 @@ void ESHelper::ChartPropertiesDialog::display(EditorState &editorState) {
}
ImGui::Separator();
if (ImGui::Selectable("Custom",&showCustomDifName)) {
difficulty = "";
difficulty_name = "";
}
ImGui::EndCombo();
}
if (showCustomDifName) {
Toolbox::InputTextColored(
difNamesInUse.find(difficulty) == difNamesInUse.end(),
difNamesInUse.find(difficulty_name) == difNamesInUse.end(),
"Chart name has to be unique",
"Difficulty Name",
&difficulty
&difficulty_name
);
}
ImGui::InputInt("Level",&level);
ImGui::Separator();
if (difficulty.empty() or (difNamesInUse.find(difficulty) != difNamesInUse.end())) {
if (difficulty_name.empty() or (difNamesInUse.find(difficulty_name) != difNamesInUse.end())) {
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
ImGui::Button("Apply##New Chart");
@ -722,12 +731,14 @@ void ESHelper::ChartPropertiesDialog::display(EditorState &editorState) {
} else {
if (ImGui::Button("Apply##New Chart")) {
try {
editorState.fumen.Charts.erase(editorState.selectedChart->get().dif_name);
editorState.selectedChart->get().dif_name = this->difficulty;
editorState.selectedChart->get().level = this->level;
if (not (editorState.fumen.Charts.emplace(this->difficulty,editorState.selectedChart.value())).second) {
Chart modified_chart = editorState.fumen.Charts.at(editorState.chart->ref.dif_name);
editorState.fumen.Charts.erase(editorState.chart->ref.dif_name);
modified_chart.dif_name = this->difficulty_name;
modified_chart.level = this->level;
if (not (editorState.fumen.Charts.emplace(modified_chart.dif_name,modified_chart)).second) {
throw std::runtime_error("Could not insert modified chart in fumen");
} else {
editorState.chart.emplace(editorState.fumen.Charts.at(modified_chart.dif_name));
shouldRefreshValues = true;
}
} catch (const std::exception& e) {
@ -739,3 +750,7 @@ void ESHelper::ChartPropertiesDialog::display(EditorState &editorState) {
ImGui::End();
}
EditorState::Chart_with_History::Chart_with_History(Chart &c) : ref(c) {
history.push(std::make_shared<OpenChart>(c));
}

View File

@ -11,15 +11,25 @@
#include "Fumen.h"
#include "Marker.h"
#include "Widgets.h"
#include "History.h"
#include "HistoryActions.h"
class ActionWithMessage;
class OpenChart;
class EditorState {
public:
struct Chart_with_History {
explicit Chart_with_History(Chart& c);
Chart& ref;
History<std::shared_ptr<ActionWithMessage>> history;
};
explicit EditorState(Fumen& fumen);
Fumen fumen;
std::optional<std::reference_wrapper<Chart>> selectedChart;
std::optional<Chart_with_History> chart;
Widgets::Playfield playfield;
int snap = 1;
@ -43,7 +53,7 @@ public:
float getTicks () {return getTicksAt(playbackPosition.asSeconds());};
float getTicksAt (float seconds) {return getBeatsAt(seconds) * getResolution();}
float getSecondsAt (int tick) {return (60.f * tick)/(fumen.BPM * getResolution()) - fumen.offset;};
int getResolution () {return selectedChart ? selectedChart->get().getResolution() : 240;};
int getResolution () {return chart ? chart->ref.getResolution() : 240;};
int getSnapStep () {return getResolution() / snap;};
float ticksToSeconds (int ticks) {return (60.f * ticks)/(fumen.BPM * getResolution());};
@ -61,6 +71,7 @@ public:
bool showChartList;
bool showNewChartDialog;
bool showChartProperties;
bool showHistory;
void displayPlayfield(Marker& marker, MarkerEndingState markerEndingState);
void displayProperties();
@ -107,7 +118,7 @@ namespace ESHelper {
private:
int level;
std::string difficulty;
std::string difficulty_name;
std::string comboPreview;
std::set<std::string> difNamesInUse;
bool showCustomDifName = false;

View File

@ -8,43 +8,87 @@
#include <stack>
#include <optional>
/*
* History implemented this way :
*
* last action -> * <- back of next_actions
* *
* *
* * <- front of next_actions
*
* given state of stuff -> x
*
* cause of current state -> * <- front of previous_actions
* *
* *
* first action ever done -> * <- back of previous_actions
*
*
*/
template<typename T>
class History {
public:
/*
* we cannot undo the very first action, which in my case corresponds to opening a chart
*/
std::optional<T> get_previous() {
if (previous_states.empty()) {
if (previous_actions.size() == 1) {
return {};
} else {
T value = previous_states.back();
previous_states.pop_back();
next_states.push_back(value);
return value;
T elt = previous_actions.front();
next_actions.push_front(elt);
previous_actions.pop_front();
return elt;
}
}
std::optional<T> get_next() {
if (next_states.empty()) {
if (next_actions.empty()) {
return {};
} else {
T value = next_states.back();
next_states.pop_back();
previous_states.push_back(value);
return value;
T elt = next_actions.front();
previous_actions.push_front(elt);
next_actions.pop_front();
return elt;
}
}
void push(const T& elt) {
previous_states.push_back(elt);
if (not next_states.empty()) {
next_states.clear();
previous_actions.push_front(elt);
if (not next_actions.empty()) {
next_actions.clear();
}
}
void display(std::function<std::string(T)> printer) {
if (ImGui::Begin("History")) {
ImGui::Indent();
for (auto it = next_actions.crbegin(); it != next_actions.crend(); ++it) {
ImGui::TextUnformatted(printer(*it).c_str());
}
ImGui::Unindent();
if (previous_actions.empty()) {
ImGui::Bullet(); ImGui::TextDisabled("(empty)");
} else {
auto it = previous_actions.cbegin();
ImGui::Bullet(); ImGui::TextUnformatted(printer(*it).c_str());
ImGui::Indent();
++it;
while (it != previous_actions.cend()) {
ImGui::TextUnformatted(printer(*it).c_str());
++it;
}
ImGui::Unindent();
}
}
ImGui::End();
}
private:
std::deque<T> previous_states;
std::deque<T> next_states;
std::deque<T> previous_actions;
std::deque<T> next_actions;
};

66
HistoryActions.cpp Normal file
View File

@ -0,0 +1,66 @@
//
// Created by Syméon on 02/03/2019.
//
#include <sstream>
#include "HistoryActions.h"
#include "EditorState.h"
const std::string &ActionWithMessage::getMessage() const {
return message;
}
OpenChart::OpenChart(Chart c) : notes(c.Notes) {
std::stringstream ss;
ss << "Opened Chart " << c.dif_name << " (Level " << c.level << ")";
message = ss.str();
}
void OpenChart::doAction(EditorState &ed) {
ed.chart->ref.Notes = notes;
}
ToggledNotes::ToggledNotes(std::set<Note> n, bool have_been_added) : notes(n), have_been_added(have_been_added) {
std::stringstream ss;
if (have_been_added) {
ss << "Added Note";
} else {
ss << "Removed Note";
}
if (n.size() > 1) {
ss << "s";
}
message = ss.str();
}
void ToggledNotes::doAction(EditorState &ed) {
if (have_been_added) {
for (auto note : notes) {
if (ed.chart->ref.Notes.find(note) == ed.chart->ref.Notes.end()) {
ed.chart->ref.Notes.insert(note);
}
}
} else {
for (auto note : notes) {
if (ed.chart->ref.Notes.find(note) != ed.chart->ref.Notes.end()) {
ed.chart->ref.Notes.erase(note);
}
}
}
}
void ToggledNotes::undoAction(EditorState &ed) {
if (not have_been_added) {
for (auto note : notes) {
if (ed.chart->ref.Notes.find(note) == ed.chart->ref.Notes.end()) {
ed.chart->ref.Notes.insert(note);
}
}
} else {
for (auto note : notes) {
if (ed.chart->ref.Notes.find(note) != ed.chart->ref.Notes.end()) {
ed.chart->ref.Notes.erase(note);
}
}
}
}

65
HistoryActions.h Normal file
View File

@ -0,0 +1,65 @@
#include <utility>
//
// Created by Syméon on 02/03/2019.
//
#ifndef FEIS_HISTORYSTATE_H
#define FEIS_HISTORYSTATE_H
#include <string>
#include <variant>
#include <memory>
#include "Chart.h"
class EditorState;
class ActionWithMessage {
public:
explicit ActionWithMessage(std::string message = "") : message(std::move(message)) {};
const std::string &getMessage() const;
virtual void doAction(EditorState &ed) {};
virtual void undoAction(EditorState &ed) {};
virtual ~ActionWithMessage() = default;
protected:
std::string message;
};
/*
* A history action replacing every note, typically the first state in history
*/
class OpenChart : public ActionWithMessage {
public:
explicit OpenChart(Chart c);
void doAction(EditorState &ed) override;
protected:
std::set<Note> notes;
};
/*
* Some notes have been toggled, either on or off depending on have_been_added
*/
class ToggledNotes : public ActionWithMessage {
public:
ToggledNotes(std::set<Note> notes, bool have_been_added);
void doAction(EditorState &ed) override;
void undoAction(EditorState &ed) override;
protected:
bool have_been_added;
std::set<Note> notes;
};
auto print_history_message = [](std::shared_ptr<ActionWithMessage> hs) -> std::string {
return (*hs).getMessage();
};
#endif //FEIS_HISTORYSTATE_H

View File

@ -0,0 +1,65 @@
// imgui_stdlib.cpp
// Wrappers for C++ standard library (STL) types (std::string, etc.)
// This is also an example of how you may wrap your own similar types.
// Compatibility:
// - std::string support is only guaranteed to work from C++11.
// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture)
// Changelog:
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
#include "imgui.h"
#include "imgui_stdlib.h"
struct InputTextCallback_UserData
{
std::string* Str;
ImGuiInputTextCallback ChainCallback;
void* ChainCallbackUserData;
};
static int InputTextCallback(ImGuiInputTextCallbackData* data)
{
InputTextCallback_UserData* user_data = (InputTextCallback_UserData*)data->UserData;
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
{
// Resize string callback
// If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
std::string* str = user_data->Str;
IM_ASSERT(data->Buf == str->c_str());
str->resize(data->BufTextLen);
data->Buf = (char*)str->c_str();
}
else if (user_data->ChainCallback)
{
// Forward to user callback, if any
data->UserData = user_data->ChainCallbackUserData;
return user_data->ChainCallback(data);
}
return 0;
}
bool ImGui::InputText(const char* label, std::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
{
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
flags |= ImGuiInputTextFlags_CallbackResize;
InputTextCallback_UserData cb_user_data;
cb_user_data.Str = str;
cb_user_data.ChainCallback = callback;
cb_user_data.ChainCallbackUserData = user_data;
return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
}
bool ImGui::InputTextMultiline(const char* label, std::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
{
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
flags |= ImGuiInputTextFlags_CallbackResize;
InputTextCallback_UserData cb_user_data;
cb_user_data.Str = str;
cb_user_data.ChainCallback = callback;
cb_user_data.ChainCallbackUserData = user_data;
return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data);
}

View File

@ -0,0 +1,22 @@
// imgui_stdlib.h
// Wrappers for C++ standard library (STL) types (std::string, etc.)
// This is also an example of how you may wrap your own similar types.
// Compatibility:
// - std::string support is only guaranteed to work from C++11.
// If you try to use it pre-C++11, please share your findings (w/ info about compiler/architecture)
// Changelog:
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
#pragma once
#include <string>
namespace ImGui
{
// ImGui::InputText() with std::string
// Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
}

View File

@ -10,7 +10,7 @@
int main(int argc, char** argv) {
// TODO : Highlight des notes qui s'entrechoquent
// TODO : Undo / Redo
// TODO : scroll to modifed note when undoing/redoing
// TODO : Debug Log
// TODO : Bruit différent si clap simple ou chord
// TODO : Density graph sur la timeline
@ -98,7 +98,7 @@ int main(int argc, char** argv) {
}
}
} else {
if (editorState and editorState->selectedChart) {
if (editorState and editorState->chart) {
float floatTicks = editorState->getTicks();
int prevTick = static_cast<int>(floorf(floatTicks));
int step = editorState->getSnapStep();
@ -121,7 +121,7 @@ int main(int argc, char** argv) {
}
}
} else {
if (editorState and editorState->selectedChart) {
if (editorState and editorState->chart) {
float floatTicks = editorState->getTicks();
int nextTick = static_cast<int>(ceilf(floatTicks));
int step = editorState->getSnapStep();
@ -131,13 +131,13 @@ int main(int argc, char** argv) {
}
break;
case sf::Keyboard::Left:
if (editorState and editorState->selectedChart) {
editorState->snap = Toolbox::getPreviousDivisor(editorState->selectedChart->get().getResolution(),editorState->snap);
if (editorState and editorState->chart) {
editorState->snap = Toolbox::getPreviousDivisor(editorState->chart->ref.getResolution(),editorState->snap);
}
break;
case sf::Keyboard::Right:
if (editorState and editorState->selectedChart) {
editorState->snap = Toolbox::getNextDivisor(editorState->selectedChart->get().getResolution(),editorState->snap);
if (editorState and editorState->chart) {
editorState->snap = Toolbox::getNextDivisor(editorState->chart->ref.getResolution(),editorState->snap);
}
break;
case sf::Keyboard::F3:
@ -168,6 +168,26 @@ int main(int argc, char** argv) {
ESHelper::save(*editorState);
}
break;
case sf::Keyboard::Y:
if (event.key.control) {
if (editorState and editorState->chart) {
auto next = editorState->chart->history.get_next();
if (next) {
(*next)->doAction(*editorState);
}
}
}
break;
case sf::Keyboard::Z:
if (event.key.control) {
if (editorState and editorState->chart) {
auto previous = editorState->chart->history.get_previous();
if (previous) {
(*previous)->undoAction(*editorState);
}
}
}
break;
default:
break;
}
@ -238,11 +258,14 @@ int main(int argc, char** argv) {
}
}
// Dessin du fond
// Drawing
if (editorState) {
window.clear(sf::Color(0, 0, 0));
if (editorState->showHistory) {
editorState->chart->history.display(print_history_message);
}
if (editorState->showPlayfield) {
editorState->displayPlayfield(marker,markerEndingState);
}
@ -278,7 +301,7 @@ int main(int argc, char** argv) {
if (c) {
editorState->showNewChartDialog = false;
if(editorState->fumen.Charts.try_emplace(c->dif_name,*c).second) {
editorState->selectedChart = editorState->fumen.Charts[c->dif_name];
editorState->chart->ref = editorState->fumen.Charts[c->dif_name];
}
}
} else {
@ -359,7 +382,7 @@ int main(int argc, char** argv) {
if (ImGui::MenuItem("Chart List")) {
editorState->showChartList = true;
}
if (ImGui::MenuItem("Properties##Chart",nullptr,false,editorState->selectedChart.has_value())) {
if (ImGui::MenuItem("Properties##Chart",nullptr,false,editorState->chart.has_value())) {
editorState->showChartProperties = true;
}
ImGui::Separator();
@ -367,9 +390,9 @@ int main(int argc, char** argv) {
editorState->showNewChartDialog = true;
}
ImGui::Separator();
if (ImGui::MenuItem("Delete Chart",nullptr,false,editorState->selectedChart.has_value())) {
editorState->fumen.Charts.erase(editorState->selectedChart->get().dif_name);
editorState->selectedChart.reset();
if (ImGui::MenuItem("Delete Chart",nullptr,false,editorState->chart.has_value())) {
editorState->fumen.Charts.erase(editorState->chart->ref.dif_name);
editorState->chart.reset();
}
ImGui::EndMenu();
}
@ -386,6 +409,9 @@ int main(int argc, char** argv) {
if (ImGui::MenuItem("Editor Status",nullptr,editorState->showStatus)) {
editorState->showStatus = not editorState->showStatus;
}
if (ImGui::MenuItem("History",nullptr,editorState->showHistory)) {
editorState->showHistory = not editorState->showHistory;
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Settings",editorState.has_value())) {