mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2025-02-20 12:31:01 +01:00
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:
parent
07def1295f
commit
d623ff74ae
@ -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})
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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;
|
||||
|
74
History.h
74
History.h
@ -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
66
HistoryActions.cpp
Normal 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
65
HistoryActions.h
Normal 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
|
65
include/imgui/imgui_stdlib.cpp
Normal file
65
include/imgui/imgui_stdlib.cpp
Normal 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);
|
||||
}
|
22
include/imgui/imgui_stdlib.h
Normal file
22
include/imgui/imgui_stdlib.h
Normal 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);
|
||||
}
|
52
main.cpp
52
main.cpp
@ -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())) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user