Files
obs-studio/UI/undo-stack-obs.cpp
jp9000 ae5ac8fa82 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.
2021-04-27 20:45:43 -07:00

135 lines
3.0 KiB
C++

#include "undo-stack-obs.hpp"
#include <util/util.hpp>
#define MAX_STACK_SIZE 5000
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"));
ui->actionMainUndo->setDisabled(true);
ui->actionMainRedo->setDisabled(true);
}
void undo_stack::add_action(const QString &name, undo_redo_cb undo,
undo_redo_cb redo, std::string undo_data,
std::string redo_data, bool repeatable)
{
while (undo_items.size() >= MAX_STACK_SIZE) {
undo_redo_t item = undo_items.back();
undo_items.pop_back();
}
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();
ui->actionMainUndo->setText(QTStr("Undo.Item.Undo").arg(name));
ui->actionMainUndo->setEnabled(true);
ui->actionMainRedo->setText(QTStr("Undo.Redo"));
ui->actionMainRedo->setDisabled(true);
}
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);
undo_items.pop_front();
ui->actionMainRedo->setText(QTStr("Undo.Item.Redo").arg(temp.name));
ui->actionMainRedo->setEnabled(true);
if (undo_items.size() == 0) {
ui->actionMainUndo->setDisabled(true);
ui->actionMainUndo->setText(QTStr("Undo.Undo"));
} else {
ui->actionMainUndo->setText(
QTStr("Undo.Item.Undo").arg(undo_items.front().name));
}
}
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);
redo_items.pop_front();
ui->actionMainUndo->setText(QTStr("Undo.Item.Undo").arg(temp.name));
ui->actionMainUndo->setEnabled(true);
if (redo_items.size() == 0) {
ui->actionMainRedo->setDisabled(true);
ui->actionMainRedo->setText(QTStr("Undo.Redo"));
} else {
ui->actionMainRedo->setText(
QTStr("Undo.Item.Redo").arg(redo_items.front().name));
}
}
void undo_stack::enable_undo_redo()
{
disabled = false;
last_is_repeatable = false;
ui->actionMainUndo->setDisabled(false);
ui->actionMainRedo->setDisabled(false);
}
void undo_stack::disable_undo_redo()
{
disabled = true;
last_is_repeatable = false;
ui->actionMainUndo->setDisabled(true);
ui->actionMainRedo->setDisabled(true);
}
void undo_stack::clear_redo()
{
redo_items.clear();
}