add cursor undo/redo actions
This commit is contained in:
parent
8d81ef87b8
commit
572eb10e91
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue