UI: Add repeat protection for Undo/Redo

This allows the ability to mark an action as repeatable, which when set,
makes it so that it will change only the redo value for the last undo
item in the undo stack within the last three seconds. This allows the
ability to, for example, scroll a spinbox or change a slider value
without creating many unnecessarily duplicated undo/redo actions.
This commit is contained in:
jp9000 2021-04-27 20:37:37 -07:00
parent 78f1983f7d
commit ae5ac8fa82
2 changed files with 43 additions and 4 deletions

View File

@ -4,12 +4,24 @@
#define MAX_STACK_SIZE 5000
undo_stack::undo_stack(ui_ptr ui) : ui(ui) {}
undo_stack::undo_stack(ui_ptr ui) : ui(ui)
{
QObject::connect(&repeat_reset_timer, &QTimer::timeout, this,
&undo_stack::reset_repeatable_state);
repeat_reset_timer.setSingleShot(true);
repeat_reset_timer.setInterval(3000);
}
void undo_stack::reset_repeatable_state()
{
last_is_repeatable = false;
}
void undo_stack::clear()
{
undo_items.clear();
redo_items.clear();
last_is_repeatable = false;
ui->actionMainUndo->setText(QTStr("Undo.Undo"));
ui->actionMainRedo->setText(QTStr("Undo.Redo"));
@ -20,7 +32,7 @@ void undo_stack::clear()
void undo_stack::add_action(const QString &name, undo_redo_cb undo,
undo_redo_cb redo, std::string undo_data,
std::string redo_data)
std::string redo_data, bool repeatable)
{
while (undo_items.size() >= MAX_STACK_SIZE) {
undo_redo_t item = undo_items.back();
@ -29,6 +41,17 @@ void undo_stack::add_action(const QString &name, undo_redo_cb undo,
undo_redo_t n = {name, undo_data, redo_data, undo, redo};
if (repeatable) {
repeat_reset_timer.start();
}
if (last_is_repeatable && repeatable && name == undo_items[0].name) {
undo_items[0].redo = redo;
undo_items[0].redo_data = redo_data;
return;
}
last_is_repeatable = repeatable;
undo_items.push_front(n);
clear_redo();
@ -44,6 +67,8 @@ void undo_stack::undo()
if (undo_items.size() == 0 || disabled)
return;
last_is_repeatable = false;
undo_redo_t temp = undo_items.front();
temp.undo(temp.undo_data);
redo_items.push_front(temp);
@ -66,6 +91,8 @@ void undo_stack::redo()
if (redo_items.size() == 0 || disabled)
return;
last_is_repeatable = false;
undo_redo_t temp = redo_items.front();
temp.redo(temp.redo_data);
undo_items.push_front(temp);
@ -86,6 +113,7 @@ void undo_stack::redo()
void undo_stack::enable_undo_redo()
{
disabled = false;
last_is_repeatable = false;
ui->actionMainUndo->setDisabled(false);
ui->actionMainRedo->setDisabled(false);
@ -94,6 +122,7 @@ void undo_stack::enable_undo_redo()
void undo_stack::disable_undo_redo()
{
disabled = true;
last_is_repeatable = false;
ui->actionMainUndo->setDisabled(true);
ui->actionMainRedo->setDisabled(true);

View File

@ -1,6 +1,8 @@
#pragma once
#include <QObject>
#include <QString>
#include <QTimer>
#include <deque>
#include <functional>
@ -9,7 +11,9 @@
#include "ui_OBSBasic.h"
class undo_stack {
class undo_stack : public QObject {
Q_OBJECT
typedef std::function<void(const std::string &data)> undo_redo_cb;
typedef std::function<void(bool is_undo)> func;
typedef std::unique_ptr<Ui::OBSBasic> &ui_ptr;
@ -26,9 +30,15 @@ class undo_stack {
std::deque<undo_redo_t> undo_items;
std::deque<undo_redo_t> redo_items;
bool disabled = false;
bool last_is_repeatable = false;
QTimer repeat_reset_timer;
void clear_redo();
private slots:
void reset_repeatable_state();
public:
undo_stack(ui_ptr ui);
@ -38,7 +48,7 @@ public:
void clear();
void add_action(const QString &name, undo_redo_cb undo,
undo_redo_cb redo, std::string undo_data,
std::string redo_data);
std::string redo_data, bool repeatable = false);
void undo();
void redo();
};