GUI: new pattern cursor logic, part 20

oh yes
This commit is contained in:
tildearrow 2025-07-18 05:05:56 -05:00
parent c5231eb569
commit e5ad462cd2
7 changed files with 60 additions and 14 deletions

View file

@ -196,6 +196,10 @@ void FurnaceGUI::drawDebug() {
ImGui::TextWrapped("%s",pdi.c_str()); ImGui::TextWrapped("%s",pdi.c_str());
ImGui::TreePop(); ImGui::TreePop();
} }
if (ImGui::TreeNode("GUI Status")) {
ImGui::Text("patScroll: %f",patScroll);
ImGui::TreePop();
}
if (ImGui::TreeNode("Sample Debug")) { if (ImGui::TreeNode("Sample Debug")) {
for (int i=0; i<e->song.sampleLen; i++) { for (int i=0; i<e->song.sampleLen; i++) {
DivSample* sample=e->getSample(i); DivSample* sample=e->getSample(i);

View file

@ -172,14 +172,30 @@ void FurnaceGUI::doAction(int what) {
break; break;
case GUI_ACTION_ORDER_LOCK: case GUI_ACTION_ORDER_LOCK:
orderLock=!orderLock; orderLock=!orderLock;
// move selection within bounds of current order if necessary
if (selStart.order!=curOrder || selEnd.order!=curOrder) { if (selStart.order!=curOrder || selEnd.order!=curOrder) {
finishSelection();
// selection confinement logic // selection confinement logic
if (selStart.order<curOrder && selEnd.order>curOrder) { if (selStart.order==selEnd.order) {
// selection within one order - move it to the current one
} else if (selStart.order<curOrder && selEnd.order<=curOrder) { } else if (selStart.order<curOrder && selEnd.order>curOrder) {
} else if (selStart.order>=curOrder && selEnd.order>curOrder) { // current order is inside selection - confine it to this order
selStart.y=0;
selEnd.y=e->curSubSong->patLen;
} else if (selStart.order<curOrder && selEnd.order==curOrder) {
// current order intersects selection - clamp the top
selStart.y=0;
} else if (selStart.order==curOrder && selEnd.order>curOrder) {
// current order intersects selection - clamp the bottom
selEnd.y=e->curSubSong->patLen;
} else {
// something else - reset selection...
selStart=cursor;
selEnd=cursor;
} }
selStart.order=curOrder;
selEnd.order=curOrder;
cursor.order=curOrder;
finishSelection();
} }
break; break;
case GUI_ACTION_REPEAT_PATTERN: case GUI_ACTION_REPEAT_PATTERN:

View file

@ -723,7 +723,10 @@ void FurnaceGUI::drawEditControls() {
ImGui::SameLine(); ImGui::SameLine();
ImGui::Checkbox(_("Edit"),&edit); ImGui::Checkbox(_("Edit"),&edit);
ImGui::SameLine(); ImGui::SameLine();
ImGui::Checkbox(_("Lock"),&orderLock); bool ol=orderLock;
if (ImGui::Checkbox(_("Lock"),&ol)) {
doAction(GUI_ACTION_ORDER_LOCK);
}
ImGui::SameLine(); ImGui::SameLine();
bool metro=e->getMetronome(); bool metro=e->getMetronome();
if (ImGui::Checkbox(_("Metronome"),&metro)) { if (ImGui::Checkbox(_("Metronome"),&metro)) {
@ -813,7 +816,7 @@ void FurnaceGUI::drawEditControls() {
pushToggleColors(orderLock); pushToggleColors(orderLock);
if (ImGui::Button(ICON_FA_LOCK "##OrderLock")) { if (ImGui::Button(ICON_FA_LOCK "##OrderLock")) {
orderLock=!orderLock; doAction(GUI_ACTION_ORDER_LOCK);
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(_("Lock cursor/selection to this order")); ImGui::SetTooltip(_("Lock cursor/selection to this order"));
@ -939,7 +942,7 @@ void FurnaceGUI::drawEditControls() {
pushToggleColors(orderLock); pushToggleColors(orderLock);
if (ImGui::Button(ICON_FA_LOCK "##OrderLock",buttonSize)) { if (ImGui::Button(ICON_FA_LOCK "##OrderLock",buttonSize)) {
orderLock=!orderLock; doAction(GUI_ACTION_ORDER_LOCK);
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(_("Lock cursor/selection to this order")); ImGui::SetTooltip(_("Lock cursor/selection to this order"));
@ -1088,7 +1091,7 @@ void FurnaceGUI::drawEditControls() {
ImGui::SameLine(); ImGui::SameLine();
pushToggleColors(orderLock); pushToggleColors(orderLock);
if (ImGui::Button(ICON_FA_LOCK "##OrderLock")) { if (ImGui::Button(ICON_FA_LOCK "##OrderLock")) {
orderLock=!orderLock; doAction(GUI_ACTION_ORDER_LOCK);
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(_("Lock cursor/selection to this order")); ImGui::SetTooltip(_("Lock cursor/selection to this order"));

View file

@ -53,6 +53,8 @@ const char* FurnaceGUI::noteNameNormal(short note, short octave) {
} }
void FurnaceGUI::prepareUndo(ActionType action, UndoRegion region) { void FurnaceGUI::prepareUndo(ActionType action, UndoRegion region) {
prevCursor=cursor;
if (region.begin.ord==-1) { if (region.begin.ord==-1) {
region.begin.ord=selStart.order; region.begin.ord=selStart.order;
region.end.ord=selEnd.order; region.end.ord=selEnd.order;
@ -126,9 +128,10 @@ void FurnaceGUI::makeUndo(ActionType action, UndoRegion region) {
bool shallWalk=false; bool shallWalk=false;
UndoStep s; UndoStep s;
s.type=action; s.type=action;
s.cursor=cursor; s.cursor=prevCursor;
s.selStart=selStart; s.selStart=selStart;
s.selEnd=selEnd; s.selEnd=selEnd;
s.scroll=patScroll;
s.order=curOrder; s.order=curOrder;
s.oldOrdersLen=oldOrdersLen; s.oldOrdersLen=oldOrdersLen;
s.newOrdersLen=e->curSubSong->ordersLen; s.newOrdersLen=e->curSubSong->ordersLen;
@ -2131,7 +2134,6 @@ void FurnaceGUI::moveSelected(int x, int y) {
void FurnaceGUI::doUndo() { void FurnaceGUI::doUndo() {
if (undoHist.empty()) return; if (undoHist.empty()) return;
UndoStep& us=undoHist.back(); UndoStep& us=undoHist.back();
redoHist.push_back(us);
MARK_MODIFIED; MARK_MODIFIED;
switch (us.type) { switch (us.type) {
@ -2172,8 +2174,10 @@ void FurnaceGUI::doUndo() {
selStart=us.selStart; selStart=us.selStart;
selEnd=us.selEnd; selEnd=us.selEnd;
curNibble=us.nibble; curNibble=us.nibble;
updateScroll(cursor.y);
setOrder(us.order); setOrder(us.order);
if (us.scroll>=0.0f) {
updateScrollRaw(us.scroll);
}
} }
} }
e->walkSong(loopOrder,loopRow,loopEnd); e->walkSong(loopOrder,loopRow,loopEnd);
@ -2205,6 +2209,11 @@ void FurnaceGUI::doUndo() {
e->setOrder(curOrder); e->setOrder(curOrder);
} }
// reverse state for redo
us.cursor=cursor;
us.scroll=patScroll;
redoHist.push_back(us);
undoHist.pop_back(); undoHist.pop_back();
} }
@ -2252,8 +2261,10 @@ void FurnaceGUI::doRedo() {
selStart=us.selStart; selStart=us.selStart;
selEnd=us.selEnd; selEnd=us.selEnd;
curNibble=us.nibble; curNibble=us.nibble;
updateScroll(cursor.y);
setOrder(us.order); setOrder(us.order);
if (us.scroll>=0.0f) {
updateScrollRaw(us.scroll);
}
} }
} }
e->walkSong(loopOrder,loopRow,loopEnd); e->walkSong(loopOrder,loopRow,loopEnd);

View file

@ -563,6 +563,11 @@ void FurnaceGUI::updateScroll(int amount) {
haveHitBounds=false; haveHitBounds=false;
} }
void FurnaceGUI::updateScrollRaw(float amount) {
nextScroll=amount;
haveHitBounds=false;
}
void FurnaceGUI::addScroll(int amount) { void FurnaceGUI::addScroll(int amount) {
float lineHeight=(patFont->FontSize+2*dpiScale); float lineHeight=(patFont->FontSize+2*dpiScale);
nextAddScroll=lineHeight*amount; nextAddScroll=lineHeight*amount;
@ -8836,6 +8841,7 @@ FurnaceGUI::FurnaceGUI():
nextAddScroll(0.0f), nextAddScroll(0.0f),
orderScroll(0.0f), orderScroll(0.0f),
orderScrollSlideOrigin(0.0f), orderScrollSlideOrigin(0.0f),
patScroll(-1.0f),
orderScrollRealOrigin(0.0f,0.0f), orderScrollRealOrigin(0.0f,0.0f),
dragMobileMenuOrigin(0.0f,0.0f), dragMobileMenuOrigin(0.0f,0.0f),
layoutTimeBegin(0), layoutTimeBegin(0),

View file

@ -1098,6 +1098,7 @@ struct UndoOtherData {
struct UndoStep { struct UndoStep {
ActionType type; ActionType type;
SelectionPoint cursor, selStart, selEnd; SelectionPoint cursor, selStart, selEnd;
float scroll;
int order; int order;
bool nibble; bool nibble;
int oldOrdersLen, newOrdersLen; int oldOrdersLen, newOrdersLen;
@ -1111,6 +1112,7 @@ struct UndoStep {
cursor(), cursor(),
selStart(), selStart(),
selEnd(), selEnd(),
scroll(-1.0f),
order(0), order(0),
nibble(false), nibble(false),
oldOrdersLen(0), oldOrdersLen(0),
@ -2371,7 +2373,7 @@ class FurnaceGUI {
bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime; bool clockShowReal, clockShowRow, clockShowBeat, clockShowMetro, clockShowTime;
float clockMetroTick[16]; float clockMetroTick[16];
SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd; SelectionPoint selStart, selEnd, cursor, cursorDrag, dragStart, dragEnd, prevCursor;
bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, wasFollowing, changeAllOrders, mobileUI; bool selecting, selectingFull, dragging, curNibble, orderNibble, followOrders, followPattern, wasFollowing, changeAllOrders, mobileUI;
bool collapseWindow, demandScrollX, fancyPattern, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble; bool collapseWindow, demandScrollX, fancyPattern, firstFrame, tempoView, waveHex, waveSigned, waveGenVisible, lockLayout, editOptsVisible, latchNibble, nonLatchNibble;
bool keepLoopAlive, keepGrooveAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton, wantGrooveListFocus; bool keepLoopAlive, keepGrooveAlive, orderScrollLocked, orderScrollTolerance, dragMobileMenu, dragMobileEditButton, wantGrooveListFocus;
@ -2531,6 +2533,7 @@ class FurnaceGUI {
bool bindSetActive, bindSetPending; bool bindSetActive, bindSetPending;
float nextScroll, nextAddScroll, nextAddScrollX, orderScroll, orderScrollSlideOrigin; float nextScroll, nextAddScroll, nextAddScrollX, orderScroll, orderScrollSlideOrigin;
float patScroll;
ImVec2 orderScrollRealOrigin; ImVec2 orderScrollRealOrigin;
ImVec2 dragMobileMenuOrigin; ImVec2 dragMobileMenuOrigin;
@ -3114,6 +3117,7 @@ class FurnaceGUI {
void bindEngine(DivEngine* eng); void bindEngine(DivEngine* eng);
void enableSafeMode(); void enableSafeMode();
void updateScroll(int amount); void updateScroll(int amount);
void updateScrollRaw(float amount);
void addScroll(int amount); void addScroll(int amount);
void addScrollX(int amount); void addScrollX(int amount);
void setFileName(String name); void setFileName(String name);

View file

@ -1401,6 +1401,8 @@ void FurnaceGUI::drawPattern() {
} }
} }
} }
// HACK: we need to capture the last scroll position in order to restore it during undo/redo
patScroll=ImGui::GetScrollY();
// HACK: rendering here would cause the pairs to be drawn behind the pattern for some reason... // HACK: rendering here would cause the pairs to be drawn behind the pattern for some reason...
// ...so we capture the table's window draw list... // ...so we capture the table's window draw list...
tdl=ImGui::GetWindowDrawList(); tdl=ImGui::GetWindowDrawList();