Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch undo #6410

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open

Batch undo #6410

wants to merge 20 commits into from

Conversation

allejok96
Copy link
Contributor

@allejok96 allejok96 commented May 21, 2022

Allow multiple journal checkpoint to be grouped together, so they can all be restored with one click.

if (someKindOfContext)
{
    // this will behave like one single undo checkpoint
    auto batchAction = Engine::projectJournal()->beginBatchAction();
    for (auto thing: allThings) { thing->addJournalCheckPoint(); }
}
// this is a separate checkpoint (batchAction ends when it goes out of scope)
otherThing->addJournalCheckPoint();

Batch undo TODO:

  • Clip colors
  • Clip delete
  • Clip move
  • Clip mute
  • Clip cut
  • Clip paste
  • Track solo/mute?
  • Other actions that span multiple tracks?

Fixes #6251 fixes #5885 fixes #4898 plus some more.

@LmmsBot
Copy link

LmmsBot commented May 21, 2022

🤖 Hey, I'm @LmmsBot from github.com/lmms/bot and I made downloads for this pull request, click me to make them magically appear! 🎩

Linux

Windows

macOS

🤖
{"platform_name_to_artifacts": {"Linux": [{"artifact": {"title": {"title": "(AppImage)", "platform_name": "Linux"}, "link": {"link": "https://output.circle-artifacts.com/output/job/22c65dab-f748-41bb-9c65-627f89591114/artifacts/0/lmms-1.3.0-alpha.1.235+g23a8ecfd2-linux-x86_64.AppImage"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/17781?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}], "Windows": [{"artifact": {"title": {"title": "32-bit", "platform_name": "Windows"}, "link": {"link": "https://output.circle-artifacts.com/output/job/dd818bd2-8a2e-4437-b8de-f3610f1a7cf2/artifacts/0/lmms-1.3.0-alpha.1.235+g23a8ecfd2-mingw-win32.exe"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/17783?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}, {"artifact": {"title": {"title": "64-bit", "platform_name": "Windows"}, "link": {"link": "https://output.circle-artifacts.com/output/job/e00d9cd1-e8f6-49f3-a7a4-070e74d521e1/artifacts/0/lmms-1.3.0-alpha.1.235+g23a8ecfd2-mingw-win64.exe"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/17784?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}, {"artifact": {"title": {"title": "32-bit", "platform_name": "Windows"}, "link": {"link": "https://ci.appveyor.com/api/buildjobs/xh0a4mvot3imnylq/artifacts/build/lmms-1.3.0-alpha-msvc2017-win32.exe"}}, "build_link": "https://ci.appveyor.com/project/Lukas-W/lmms/builds/44153820"}, {"artifact": {"title": {"title": "64-bit", "platform_name": "Windows"}, "link": {"link": "https://ci.appveyor.com/api/buildjobs/y69x6dxju5tbk0re/artifacts/build/lmms-1.3.0-alpha-msvc2017-win64.exe"}}, "build_link": "https://ci.appveyor.com/project/Lukas-W/lmms/builds/44153820"}], "macOS": [{"artifact": {"title": {"title": "", "platform_name": "macOS"}, "link": {"link": "https://output.circle-artifacts.com/output/job/fdb0e396-0e10-4d27-84fb-8d94eba1fbb9/artifacts/0/lmms-1.3.0-alpha.1.235+g23a8ecfd2-mac10.14.dmg"}}, "build_link": "https://circleci.com/gh/LMMS/lmms/17785?utm_campaign=vcs-integration-link&utm_medium=referral&utm_source=github-build-link"}]}, "commit_sha": "ea6674c81c9e3857fe0ef6b0963fbeed1040a6f4"}

@Spekular
Copy link
Member

Isn't this exactly what we use setJournalling for?

setJournalling(false)
//batch operation
setJournalling(true)
addJournalCheckpoint()

@allejok96
Copy link
Contributor Author

Nope. setJournalling is when you don't want to journal the same thing many times. This is when you have to journal many unrelated things (no common parent) and want them all to undo at once. Like moving a selection of clips that span multiple tracks.

@allejok96 allejok96 marked this pull request as ready for review May 26, 2022 20:28
@allejok96
Copy link
Contributor Author

Fixes #6251 #5885 and #4898

@PhysSong
Copy link
Member

PhysSong commented Jul 2, 2022

Fixes #6251 #5885 and #4898

You should add that in the PR body with "fixes" before every issues to auto-close issues on merge.

Also, could you handle the merge conflicts? I want to review this PR after resolving conflicts.

@allejok96
Copy link
Contributor Author

@PhysSong conflicts fixed. I can add the "fixed" tags in the commit message when I squash the commits later, right?

@zonkmachine
Copy link
Member

I can add the "fixed" tags in the commit message when I squash the commits later, right?

Technically I believe that would auto-close those issues, but it's good to have a reference to them in the PR description.

Copy link
Member

@PhysSong PhysSong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some suggestions and questions. Haven't reviewed ClipView changes yet.

src/core/ProjectJournal.cpp Show resolved Hide resolved
src/core/ProjectJournal.cpp Outdated Show resolved Hide resolved
src/core/ProjectJournal.cpp Outdated Show resolved Hide resolved
src/core/ProjectJournal.cpp Outdated Show resolved Hide resolved
@JohannesLorenz
Copy link
Contributor

JohannesLorenz commented Jul 8, 2022

For information, I'm still about to review this, should be finished this weekend. Would be cool if you can wait with merging. Already finished.

Copy link
Contributor

@JohannesLorenz JohannesLorenz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review submitted.

Testing Review:

  • Solo buttons: Have 3 tracks (like in the default song), "solo" track 1, then "solo" track 3. Press Ctrl+z twice -> works. Press Ctrl+shift+z twice -> fails twice.
  • Clip colors: Create clip, change color (I used pick random), create second clip (e.g. same pattern), change color. Ctrl+z removes color -> OK. Ctrl+z removes 2nd clip -> OK. Ctrl+z removes 1st clip -> fail.
  • Delete clips: Create 3 clips, mark all, press del. Press Ctrl+z -> they reappear -> OK. you need to press 3 times Ctrl+z so something new happens -> intended?

src/gui/clips/ClipView.cpp Outdated Show resolved Hide resolved
}
}
}
else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was the else branch removed (I'm unsure both about ...selectAllClips and clipViews.push_back(this)).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getClickedClips() does the equivalent of this whole if-else statement, with the exception of the mentioned selectAllClips(false).

The only result of removing that line is that when you have some clips selected and you ctrl+drag an unselected clip, the old selection will remain until the clip is dropped.

src/gui/clips/ClipView.cpp Show resolved Hide resolved
src/gui/clips/ClipView.cpp Show resolved Hide resolved
src/core/ProjectJournal.cpp Outdated Show resolved Hide resolved
src/core/Track.cpp Outdated Show resolved Hide resolved
if (!isJournalling()) { return; }
// Begin on a new batch if we are not already batching, or if there is nothing to append to
if (append ? m_undoCheckPoints.empty() : m_batchingCount == 0) { m_undoCheckPoints.emplace_back(); }
++m_batchingCount;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, I found the variable name confusing. batchingCount sounds like "This is the number of CheckPoints that I am batching". Maybe subjective, though 😁

Secondly, why is this variable changed if we add/remove checkpoints here, but not e.g. in ProjectJournal::addJournalCheckPoint? Intended?

Thirdly, I guess this variable would (if stuff like "secondly" would be changed) be equivalent to something like m_undoCheckPoints.size() or m_undoCheckPoints.size() + m_redoCheckPoints.size()?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes confusing name. Perhaps batchesInProgress would be more descriptive?

The count is not of the number of checkpoints in the current batch, instead it counts the number of times beginBatchCheckPoint have been called (like a recursion level, but it's not recursive). Calling the begin function multiple time has no real effect, but we must count the calls, because for every call to begin you expect a call to endBatchCheckPoint.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! So the only reason for that variable is to make sure that begin... and end... match? Then it would all make sense.

An RAII version, like BashPointGuard would be cool (though, probably, less descriptive...).

Copy link
Contributor Author

@allejok96 allejok96 Jul 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not so good at RAII so I don't understand how that would be implemented in this case. I just get a feeling it would make things a lot more complex than they had to be, and you'd still have to use some form of counter or vector so you can detect when the last guard is deleted.

What would be nice if there was something like a context manager:

def doThing():
    with journal.batchCheckPoints():
        for obj in objects:
            obj.addJournalCheckPoint()

But the closest I can think of is

void doThing()
{
    BatchHandle handle = journal->getBatchHandle(); // error: variable never used
    for (Object ob: objects)
    {
        obj->addJournalCheckPoint();
    }
}

(It would not remove the need for the counter, but it would remove the need to call endBatchCheckPoint() which could mess things up if forgotten)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this was what I had in mind, too. It should not cause unused variable warnings, since std::lock_guard is used the same way.

I'm OK with either begin/end or RAII, as long as it's clear what the counter is used for 😁

- Use RAII to start/end batching
- Use emplace_back and std::move
- Fix batch undo when clips have been deleted with keyboard
- Fix broken undo for objects that have been restored (9 yr old bug)
- Fix undo when switching from one solo track to another
- Rename BatchCheckPoint to CheckPointBatch
@allejok96
Copy link
Contributor Author

Much stuff going on in last commit.

@JohannesLorenz fixed solo buttons, delete using keyboard and clip colors.

The last one was a 9 year old bug that breaks undo for deleted/restored objects. Try to write some notes and copy the clip and then undo back to before you wrote notes -> fails. I fixed the bug but that introduces an error message JO-ID ... already in use by ...! when copying clips etc. This is not harmful, so let's fix the messages in another PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
6 participants