add cursor undo/redo actions

This commit is contained in:
Adam Lederer 2024-08-24 18:00:11 -07:00 committed by tildearrow
parent 8d81ef87b8
commit 572eb10e91
10 changed files with 122 additions and 2 deletions

View file

@ -69,6 +69,9 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) {
selStart.y=y;
selEnd.y=y;
} else {
if (xCoarse!=cursor.xCoarse || y!=cursor.y) {
makeCursorUndo();
}
cursor.xCoarse=xCoarse;
cursor.xFine=xFine;
cursor.y=y;
@ -208,6 +211,9 @@ void FurnaceGUI::finishSelection() {
}
void FurnaceGUI::moveCursor(int x, int y, bool select) {
if (y>=editStepCoarse || y<=-editStepCoarse || x<=-5 || x>=5 ) {
makeCursorUndo();
}
if (!select) {
finishSelection();
}
@ -326,6 +332,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) {
}
void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
makeCursorUndo();
finishSelection();
curNibble=false;
@ -354,6 +361,7 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) {
}
void FurnaceGUI::moveCursorNextChannel(bool overflow) {
makeCursorUndo();
finishSelection();
curNibble=false;
@ -382,6 +390,7 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) {
}
void FurnaceGUI::moveCursorTop(bool select) {
makeCursorUndo();
if (!select) {
finishSelection();
}
@ -403,6 +412,7 @@ void FurnaceGUI::moveCursorTop(bool select) {
}
void FurnaceGUI::moveCursorBottom(bool select) {
makeCursorUndo();
if (!select) {
finishSelection();
}

View file

@ -731,6 +731,25 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("result: %.0f%%",realVol*100.0f);
ImGui::TreePop();
}
if (ImGui::TreeNode("Cursor Undo Debug")) {
auto DrawSpot=[&](const CursorJumpPoint& spot) {
ImGui::Text("[%d:%d] <%d:%d, %d>", spot.subSong, spot.order, spot.point.xCoarse, spot.point.xFine, spot.point.y);
};
if (ImGui::BeginChild("##CursorUndoDebugChild", ImVec2(0, 300), true)) {
if (ImGui::BeginTable("##CursorUndoDebug", 2, ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
for (size_t row=0; row<MAX(cursorUndoHist.size(),cursorRedoHist.size()); ++row) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (row<cursorUndoHist.size()) DrawSpot(cursorUndoHist[cursorUndoHist.size()-row-1]);
ImGui::TableNextColumn();
if (row<cursorRedoHist.size()) DrawSpot(cursorRedoHist[cursorRedoHist.size()-row-1]);
}
ImGui::EndTable();
}
}
ImGui::EndChild();
ImGui::TreePop();
}
if (ImGui::TreeNode("User Interface")) {
if (ImGui::Button("Inspect")) {
inspectorOpen=!inspectorOpen;

View file

@ -680,10 +680,15 @@ void FurnaceGUI::doAction(int what) {
latchTarget=0;
latchNibble=false;
break;
case GUI_ACTION_PAT_ABSORB_INSTRUMENT: {
case GUI_ACTION_PAT_ABSORB_INSTRUMENT:
doAbsorbInstrument();
break;
}
case GUI_ACTION_PAT_CURSOR_UNDO:
doCursorUndo();
break;
case GUI_ACTION_PAT_CURSOR_REDO:
doCursorRedo();
break;
case GUI_ACTION_INS_LIST_ADD:
if (settings.insTypeMenu) {

View file

@ -678,6 +678,7 @@ void FurnaceGUI::doPasteFurnace(PasteMode mode, int arg, bool readClipboard, Str
if (readClipboard) {
if (settings.cursorPastePos) {
makeCursorUndo();
cursor.y=j;
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
selStart=cursor;
@ -1220,6 +1221,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String
if (readClipboard) {
if (settings.cursorPastePos) {
makeCursorUndo();
cursor.y=j;
if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1;
selStart=cursor;
@ -2061,3 +2063,52 @@ void FurnaceGUI::doRedo() {
redoHist.pop_back();
}
CursorJumpPoint FurnaceGUI::getCurrentCursorJumpPoint() {
return CursorJumpPoint(cursor, curOrder, e->getCurrentSubSong());
}
void FurnaceGUI::applyCursorJumpPoint(const CursorJumpPoint& spot) {
cursor=spot.point;
curOrder=MIN(e->curSubSong->ordersLen-1, spot.order);
e->setOrder(curOrder);
e->changeSongP(spot.subSong);
if (!settings.cursorMoveNoScroll) {
updateScroll(cursor.y);
}
}
void FurnaceGUI::makeCursorUndo() {
CursorJumpPoint spot = getCurrentCursorJumpPoint();
if (!cursorUndoHist.empty() && spot == cursorUndoHist.back()) return;
if (cursorUndoHist.size()>=settings.maxUndoSteps) cursorUndoHist.pop_front();
cursorUndoHist.push_back(spot);
// redo history no longer relevant, we've changed timeline
cursorRedoHist.clear();
}
void FurnaceGUI::doCursorUndo() {
if (cursorUndoHist.empty()) return;
// allow returning to current spot
if (cursorRedoHist.size()>=settings.maxUndoSteps) cursorRedoHist.pop_front();
cursorRedoHist.push_back(getCurrentCursorJumpPoint());
// apply spot
applyCursorJumpPoint(cursorUndoHist.back());
cursorUndoHist.pop_back();
}
void FurnaceGUI::doCursorRedo() {
if (cursorRedoHist.empty()) return;
// allow returning to current spot
if (cursorUndoHist.size()>=settings.maxUndoSteps) cursorUndoHist.pop_front();
cursorUndoHist.push_back(getCurrentCursorJumpPoint());
// apply spot
applyCursorJumpPoint(cursorRedoHist.back());
cursorRedoHist.pop_back();
}

View file

@ -560,6 +560,7 @@ void FurnaceGUI::drawFindReplace() {
if (ImGui::TableNextColumn()) {
snprintf(tempID,1024,ICON_FA_CHEVRON_RIGHT "##_FR%d",index);
if (ImGui::Selectable(tempID)) {
makeCursorUndo();
e->changeSongP(i.subsong);
if (e->isPlaying()) {
followPattern=false;

View file

@ -1211,6 +1211,7 @@ void FurnaceGUI::play(int row) {
memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float));
e->walkSong(loopOrder,loopRow,loopEnd);
memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS);
if (followPattern) makeCursorUndo();
if (!followPattern) e->setOrder(curOrder);
if (row>0) {
if (!e->playToRow(row)) {

View file

@ -818,6 +818,8 @@ enum FurnaceGUIActions {
GUI_ACTION_PAT_SCROLL_MODE,
GUI_ACTION_PAT_CLEAR_LATCH,
GUI_ACTION_PAT_ABSORB_INSTRUMENT,
GUI_ACTION_PAT_CURSOR_UNDO,
GUI_ACTION_PAT_CURSOR_REDO,
GUI_ACTION_PAT_MAX,
GUI_ACTION_INS_LIST_MIN,
@ -1103,6 +1105,22 @@ struct UndoStep {
newPatLen(0) {}
};
struct CursorJumpPoint {
SelectionPoint point;
int order;
int subSong;
CursorJumpPoint(const SelectionPoint& p, int o, int ss):
point(p), order(o), subSong(ss) {}
CursorJumpPoint():
point(), order(0), subSong(0) {}
bool operator== (const CursorJumpPoint& spot) {
return point.xCoarse==spot.point.xCoarse && point.xFine==spot.point.xFine && point.y==spot.point.y && order==spot.order && subSong==spot.subSong;
}
bool operator!= (const CursorJumpPoint& spot) {
return !(*this == spot);
}
};
// -1 = any
struct MIDIBind {
int type, channel, data1, data2;
@ -2500,6 +2518,8 @@ class FurnaceGUI {
std::map<unsigned short,DivPattern*> oldPatMap;
FixedQueue<UndoStep,256> undoHist;
FixedQueue<UndoStep,256> redoHist;
FixedQueue<CursorJumpPoint,256> cursorUndoHist;
FixedQueue<CursorJumpPoint,256> cursorRedoHist;
// sample editor specific
double sampleZoom;
@ -2936,6 +2956,12 @@ class FurnaceGUI {
void doGenerateWave();
CursorJumpPoint getCurrentCursorJumpPoint();
void applyCursorJumpPoint(const CursorJumpPoint& spot);
void makeCursorUndo();
void doCursorUndo();
void doCursorRedo();
void doUndoSample();
void doRedoSample();

View file

@ -688,6 +688,8 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
D("PAT_SCROLL_MODE", _N("Change mobile scroll mode"), 0),
D("PAT_CLEAR_LATCH", _N("Clear note input latch"), 0),
D("PAT_ABSORB_INSTRUMENT", _N("Absorb instrument/octave from status at cursor"), 0),
D("PAT_CURSOR_UNDO", _N("Return cursor to previous jump point"), 0),
D("PAT_CURSOR_REDO", _N("Reverse recent cursor undo"), 0),
D("PAT_MAX", "", NOT_AN_ACTION),
D("INS_LIST_MIN", _N("---Instrument list"), NOT_AN_ACTION),

View file

@ -2428,6 +2428,8 @@ void FurnaceGUI::drawSettings() {
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_LATCH);
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CLEAR_LATCH);
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_ABSORB_INSTRUMENT);
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CURSOR_UNDO);
UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CURSOR_REDO);
KEYBIND_CONFIG_END;
ImGui::TreePop();

View file

@ -36,6 +36,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(id,i==e->getCurrentSubSong())) {
makeCursorUndo();
e->changeSongP(i);
updateScroll(0);
oldRow=0;
@ -72,6 +73,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
if (!e->addSubSong()) {
showError(_("too many subsongs!"));
} else {
makeCursorUndo();
e->changeSongP(e->song.subsong.size()-1);
updateScroll(0);
oldRow=0;
@ -92,6 +94,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) {
if (!e->duplicateSubSong(e->getCurrentSubSong())) {
showError(_("too many subsongs!"));
} else {
makeCursorUndo();
e->changeSongP(e->song.subsong.size()-1);
updateScroll(0);
oldRow=0;