mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-16 05:42:32 +00:00
Add undo2 library
This commit is contained in:
parent
e40d8e8cfe
commit
a9fa9f5fdc
@ -31,6 +31,7 @@ set(aseprite_libraries
|
|||||||
render-lib
|
render-lib
|
||||||
scripting-lib
|
scripting-lib
|
||||||
undo-lib
|
undo-lib
|
||||||
|
undo2-lib
|
||||||
filters-lib
|
filters-lib
|
||||||
ui-lib
|
ui-lib
|
||||||
she
|
she
|
||||||
@ -220,6 +221,7 @@ add_subdirectory(scripting)
|
|||||||
add_subdirectory(she)
|
add_subdirectory(she)
|
||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
add_subdirectory(undo)
|
add_subdirectory(undo)
|
||||||
|
add_subdirectory(undo2)
|
||||||
|
|
||||||
add_subdirectory(app)
|
add_subdirectory(app)
|
||||||
|
|
||||||
@ -328,6 +330,7 @@ function(find_tests dir dependencies)
|
|||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
find_tests(base base-lib ${sys_libs})
|
find_tests(base base-lib ${sys_libs})
|
||||||
|
find_tests(undo2 undo2-lib ${sys_libs})
|
||||||
find_tests(gfx gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
find_tests(gfx gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
||||||
find_tests(doc doc-lib gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
find_tests(doc doc-lib gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
||||||
find_tests(render render-lib doc-lib gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
find_tests(render render-lib doc-lib gfx-lib base-lib ${libs3rdparty} ${sys_libs})
|
||||||
|
@ -19,6 +19,7 @@ because they don't depend on any other component.
|
|||||||
* [gfx](gfx/): Abstract graphics structures like point, size, rectangle, region, color, etc.
|
* [gfx](gfx/): Abstract graphics structures like point, size, rectangle, region, color, etc.
|
||||||
* [scripting](scripting/): JavaScript engine ([V8](https://code.google.com/p/v8/)).
|
* [scripting](scripting/): JavaScript engine ([V8](https://code.google.com/p/v8/)).
|
||||||
* [undo](undo/): Generic library to manage undo history of undoable actions.
|
* [undo](undo/): Generic library to manage undo history of undoable actions.
|
||||||
|
* [undo2](undo2/): New library to replace the old undo system.
|
||||||
|
|
||||||
## Level 1
|
## Level 1
|
||||||
|
|
||||||
|
5
src/undo2/CMakeLists.txt
Normal file
5
src/undo2/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Aseprite Undo2 Library
|
||||||
|
# Copyright (C) 2015 David Capello
|
||||||
|
|
||||||
|
add_library(undo2-lib
|
||||||
|
undo_history.cpp)
|
20
src/undo2/LICENSE.txt
Normal file
20
src/undo2/LICENSE.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2015 David Capello
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
4
src/undo2/README.md
Normal file
4
src/undo2/README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Aseprite Undo2 Library
|
||||||
|
*Copyright (C) 2015 David Capello*
|
||||||
|
|
||||||
|
> Distributed under [MIT license](LICENSE.txt)
|
22
src/undo2/undo_command.h
Normal file
22
src/undo2/undo_command.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Aseprite Undo2 Library
|
||||||
|
// Copyright (C) 2015 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifndef UNDO2_UNDO_COMMAND_H_INCLUDED
|
||||||
|
#define UNDO2_UNDO_COMMAND_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace undo2 {
|
||||||
|
|
||||||
|
class UndoCommand {
|
||||||
|
public:
|
||||||
|
virtual ~UndoCommand() { }
|
||||||
|
virtual void undo() = 0;
|
||||||
|
virtual void redo() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace undo2
|
||||||
|
|
||||||
|
#endif // UNDO2_UNDO_COMMAND_H_INCLUDED
|
162
src/undo2/undo_history.cpp
Normal file
162
src/undo2/undo_history.cpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// Aseprite Undo2 Library
|
||||||
|
// Copyright (C) 2015 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "undo2/undo_history.h"
|
||||||
|
|
||||||
|
#include "undo2/undo_command.h"
|
||||||
|
#include "undo2/undo_state.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
namespace undo2 {
|
||||||
|
|
||||||
|
UndoHistory::UndoHistory()
|
||||||
|
: m_first(nullptr)
|
||||||
|
, m_last(nullptr)
|
||||||
|
, m_cur(nullptr)
|
||||||
|
, m_createBranches(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UndoHistory::~UndoHistory()
|
||||||
|
{
|
||||||
|
m_cur = nullptr;
|
||||||
|
clearRedo();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UndoHistory::canUndo() const
|
||||||
|
{
|
||||||
|
return m_cur != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UndoHistory::canRedo() const
|
||||||
|
{
|
||||||
|
return m_cur != m_last;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoHistory::undo()
|
||||||
|
{
|
||||||
|
assert(m_cur);
|
||||||
|
if (!m_cur)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(
|
||||||
|
(m_cur != m_first && m_cur->m_prev) ||
|
||||||
|
(m_cur == m_first && !m_cur->m_prev));
|
||||||
|
|
||||||
|
moveTo(m_cur->m_prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoHistory::redo()
|
||||||
|
{
|
||||||
|
if (!m_cur)
|
||||||
|
moveTo(m_first);
|
||||||
|
else
|
||||||
|
moveTo(m_cur->m_next);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoHistory::setCreateBranches(bool state)
|
||||||
|
{
|
||||||
|
m_createBranches = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoHistory::clearRedo()
|
||||||
|
{
|
||||||
|
for (UndoState* state = m_last, *prev;
|
||||||
|
state && state != m_cur;
|
||||||
|
state = prev) {
|
||||||
|
prev = state->m_prev;
|
||||||
|
delete state;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_cur) {
|
||||||
|
m_cur->m_next = nullptr;
|
||||||
|
m_last = m_cur;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_first = m_last = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoHistory::add(UndoCommand* cmd)
|
||||||
|
{
|
||||||
|
if (!m_createBranches)
|
||||||
|
clearRedo();
|
||||||
|
|
||||||
|
UndoState* state = new UndoState;
|
||||||
|
state->m_prev = m_last;
|
||||||
|
state->m_next = nullptr;
|
||||||
|
state->m_parent = m_cur;
|
||||||
|
state->m_cmd = cmd;
|
||||||
|
|
||||||
|
if (!m_first)
|
||||||
|
m_first = state;
|
||||||
|
|
||||||
|
m_cur = m_last = state;
|
||||||
|
|
||||||
|
if (state->m_prev) {
|
||||||
|
assert(!state->m_prev->m_next);
|
||||||
|
state->m_prev->m_next = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UndoState* UndoHistory::findCommonParent(UndoState* a, UndoState* b)
|
||||||
|
{
|
||||||
|
UndoState* pA = a;
|
||||||
|
UndoState* pB = b;
|
||||||
|
|
||||||
|
if (pA == nullptr || pB == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
while (pA != pB) {
|
||||||
|
pA = pA->m_parent;
|
||||||
|
if (!pA) {
|
||||||
|
pA = a;
|
||||||
|
pB = pB->m_parent;
|
||||||
|
if (!pB)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pA;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UndoHistory::moveTo(UndoState* new_state)
|
||||||
|
{
|
||||||
|
UndoState* common = findCommonParent(m_cur, new_state);
|
||||||
|
|
||||||
|
if (m_cur) {
|
||||||
|
while (m_cur != common) {
|
||||||
|
m_cur->m_cmd->undo();
|
||||||
|
m_cur = m_cur->m_parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_state) {
|
||||||
|
std::stack<UndoState*> redo_parents;
|
||||||
|
UndoState* p = new_state;
|
||||||
|
while (p != common) {
|
||||||
|
redo_parents.push(p);
|
||||||
|
p = p->m_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!redo_parents.empty()) {
|
||||||
|
p = redo_parents.top();
|
||||||
|
redo_parents.pop();
|
||||||
|
|
||||||
|
p->m_cmd->redo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cur = new_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace undo
|
50
src/undo2/undo_history.h
Normal file
50
src/undo2/undo_history.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Aseprite Undo2 Library
|
||||||
|
// Copyright (C) 2015 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifndef UNDO2_UNDO_HISTORY_H_INCLUDED
|
||||||
|
#define UNDO2_UNDO_HISTORY_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace undo2 {
|
||||||
|
|
||||||
|
class UndoCommand;
|
||||||
|
class UndoState;
|
||||||
|
|
||||||
|
class UndoHistory {
|
||||||
|
public:
|
||||||
|
UndoHistory();
|
||||||
|
virtual ~UndoHistory();
|
||||||
|
|
||||||
|
const UndoState* firstState() const { return m_first; }
|
||||||
|
const UndoState* lastState() const { return m_last; }
|
||||||
|
const UndoState* currentState() const { return m_cur; }
|
||||||
|
|
||||||
|
void add(UndoCommand* cmd);
|
||||||
|
bool canUndo() const;
|
||||||
|
bool canRedo() const;
|
||||||
|
void undo();
|
||||||
|
void redo();
|
||||||
|
|
||||||
|
// By default, UndoHistory creates branches if we undo a command
|
||||||
|
// and call add(). With this method we can disable this behavior
|
||||||
|
// and call clearRedo() automatically when a new command is added
|
||||||
|
// in the history. (Like a regular undo history works.)
|
||||||
|
void setCreateBranches(bool state);
|
||||||
|
void clearRedo();
|
||||||
|
|
||||||
|
private:
|
||||||
|
UndoState* findCommonParent(UndoState* a, UndoState* b);
|
||||||
|
void moveTo(UndoState* new_state);
|
||||||
|
|
||||||
|
UndoState* m_first;
|
||||||
|
UndoState* m_last;
|
||||||
|
UndoState* m_cur; // Current action that can be undone
|
||||||
|
bool m_createBranches;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace undo2
|
||||||
|
|
||||||
|
#endif // UNDO2_UNDO_HISTORY_H_INCLUDED
|
33
src/undo2/undo_state.h
Normal file
33
src/undo2/undo_state.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// Aseprite Undo2 Library
|
||||||
|
// Copyright (C) 2015 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifndef UNDO2_UNDO_STATE_H_INCLUDED
|
||||||
|
#define UNDO2_UNDO_STATE_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace undo2 {
|
||||||
|
|
||||||
|
class UndoCommand;
|
||||||
|
class UndoHistory;
|
||||||
|
|
||||||
|
// Represents a state that can be undone. If we are in this state,
|
||||||
|
// is because the command was already executed.
|
||||||
|
class UndoState {
|
||||||
|
friend class UndoHistory;
|
||||||
|
public:
|
||||||
|
UndoState* prev() { return m_prev; }
|
||||||
|
UndoState* next() { return m_next; }
|
||||||
|
UndoCommand* cmd() { return m_cmd; }
|
||||||
|
private:
|
||||||
|
UndoState* m_prev;
|
||||||
|
UndoState* m_next;
|
||||||
|
UndoState* m_parent; // Parent state, after we undo
|
||||||
|
UndoCommand* m_cmd;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace undo2
|
||||||
|
|
||||||
|
#endif // UNDO2_UNDO_STATE_H_INCLUDED
|
226
src/undo2/undo_tests.cpp
Normal file
226
src/undo2/undo_tests.cpp
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
// Aseprite Undo2 Library
|
||||||
|
// Copyright (C) 2015 David Capello
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "undo2/undo_command.h"
|
||||||
|
#include "undo2/undo_history.h"
|
||||||
|
|
||||||
|
using namespace undo2;
|
||||||
|
|
||||||
|
class Cmd : public UndoCommand {
|
||||||
|
public:
|
||||||
|
template<typename UndoT, typename RedoT>
|
||||||
|
Cmd(RedoT redoFunc, UndoT undoFunc)
|
||||||
|
: m_redo(redoFunc), m_undo(undoFunc) {
|
||||||
|
}
|
||||||
|
void redo() override { m_redo(); }
|
||||||
|
void undo() override { m_undo(); }
|
||||||
|
private:
|
||||||
|
std::function<void()> m_redo;
|
||||||
|
std::function<void()> m_undo;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Undo, Basics)
|
||||||
|
{
|
||||||
|
UndoHistory history;
|
||||||
|
|
||||||
|
int model = 0;
|
||||||
|
EXPECT_EQ(0, model);
|
||||||
|
|
||||||
|
Cmd cmd1(
|
||||||
|
[&]{ model = 1; }, // redo
|
||||||
|
[&]{ model = 0; }); // undo
|
||||||
|
Cmd cmd2(
|
||||||
|
[&]{ model = 2; }, // redo
|
||||||
|
[&]{ model = 1; }); // undo
|
||||||
|
|
||||||
|
EXPECT_EQ(0, model);
|
||||||
|
cmd1.redo();
|
||||||
|
EXPECT_EQ(1, model);
|
||||||
|
cmd2.redo();
|
||||||
|
EXPECT_EQ(2, model);
|
||||||
|
|
||||||
|
EXPECT_FALSE(history.canUndo());
|
||||||
|
EXPECT_FALSE(history.canRedo());
|
||||||
|
history.add(&cmd1);
|
||||||
|
EXPECT_TRUE(history.canUndo());
|
||||||
|
EXPECT_FALSE(history.canRedo());
|
||||||
|
history.add(&cmd2);
|
||||||
|
EXPECT_TRUE(history.canUndo());
|
||||||
|
EXPECT_FALSE(history.canRedo());
|
||||||
|
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(1, model);
|
||||||
|
EXPECT_TRUE(history.canUndo());
|
||||||
|
EXPECT_TRUE(history.canRedo());
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(0, model);
|
||||||
|
EXPECT_FALSE(history.canUndo());
|
||||||
|
EXPECT_TRUE(history.canRedo());
|
||||||
|
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(1, model);
|
||||||
|
EXPECT_TRUE(history.canUndo());
|
||||||
|
EXPECT_TRUE(history.canRedo());
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(2, model);
|
||||||
|
EXPECT_TRUE(history.canUndo());
|
||||||
|
EXPECT_FALSE(history.canRedo());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Undo, Linear)
|
||||||
|
{
|
||||||
|
UndoHistory history;
|
||||||
|
history.setCreateBranches(false);
|
||||||
|
|
||||||
|
int model = 0;
|
||||||
|
EXPECT_EQ(0, model);
|
||||||
|
|
||||||
|
// 1 --- 3 --- 4
|
||||||
|
Cmd cmd1([&]{ model = 1; }, [&]{ model = 0; });
|
||||||
|
Cmd cmd2([&]{ model = 2; }, [&]{ model = 1; });
|
||||||
|
Cmd cmd3([&]{ model = 3; }, [&]{ model = 1; });
|
||||||
|
Cmd cmd4([&]{ model = 4; }, [&]{ model = 3; });
|
||||||
|
|
||||||
|
cmd1.redo(); history.add(&cmd1);
|
||||||
|
cmd2.redo(); history.add(&cmd2);
|
||||||
|
history.undo();
|
||||||
|
cmd3.redo(); history.add(&cmd3); // Removes state 2
|
||||||
|
cmd4.redo(); history.add(&cmd4);
|
||||||
|
|
||||||
|
EXPECT_EQ(4, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(3, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(1, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(0, model);
|
||||||
|
EXPECT_FALSE(history.canUndo());
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(1, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(3, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(4, model);
|
||||||
|
EXPECT_FALSE(history.canRedo());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Undo, Tree)
|
||||||
|
{
|
||||||
|
UndoHistory history;
|
||||||
|
int model = 0;
|
||||||
|
|
||||||
|
// 1 --- 2
|
||||||
|
// \
|
||||||
|
// ------ 3 --- 4
|
||||||
|
Cmd cmd1([&]{ model = 1; }, [&]{ model = 0; });
|
||||||
|
Cmd cmd2([&]{ model = 2; }, [&]{ model = 1; });
|
||||||
|
Cmd cmd3([&]{ model = 3; }, [&]{ model = 1; });
|
||||||
|
Cmd cmd4([&]{ model = 4; }, [&]{ model = 3; });
|
||||||
|
|
||||||
|
cmd1.redo(); history.add(&cmd1);
|
||||||
|
cmd2.redo(); history.add(&cmd2);
|
||||||
|
history.undo();
|
||||||
|
cmd3.redo(); history.add(&cmd3); // Creates a branch in the history
|
||||||
|
cmd4.redo(); history.add(&cmd4);
|
||||||
|
|
||||||
|
EXPECT_EQ(4, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(3, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(2, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(1, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(0, model);
|
||||||
|
EXPECT_FALSE(history.canUndo());
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(1, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(2, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(3, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(4, model);
|
||||||
|
EXPECT_FALSE(history.canRedo());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Undo, ComplexTree)
|
||||||
|
{
|
||||||
|
UndoHistory history;
|
||||||
|
int model = 0;
|
||||||
|
|
||||||
|
// 1 --- 2 --- 3 --- 4 ------ 7 --- 8
|
||||||
|
// \ /
|
||||||
|
// ------------- 5 --- 6
|
||||||
|
Cmd cmd1([&]{ model = 1; }, [&]{ model = 0; });
|
||||||
|
Cmd cmd2([&]{ model = 2; }, [&]{ model = 1; });
|
||||||
|
Cmd cmd3([&]{ model = 3; }, [&]{ model = 2; });
|
||||||
|
Cmd cmd4([&]{ model = 4; }, [&]{ model = 3; });
|
||||||
|
Cmd cmd5([&]{ model = 5; }, [&]{ model = 2; });
|
||||||
|
Cmd cmd6([&]{ model = 6; }, [&]{ model = 5; });
|
||||||
|
Cmd cmd7([&]{ model = 7; }, [&]{ model = 5; });
|
||||||
|
Cmd cmd8([&]{ model = 8; }, [&]{ model = 7; });
|
||||||
|
|
||||||
|
cmd1.redo(); history.add(&cmd1);
|
||||||
|
cmd2.redo(); history.add(&cmd2);
|
||||||
|
cmd3.redo(); history.add(&cmd3);
|
||||||
|
cmd4.redo(); history.add(&cmd4);
|
||||||
|
history.undo();
|
||||||
|
cmd5.redo(); history.add(&cmd5);
|
||||||
|
cmd6.redo(); history.add(&cmd6);
|
||||||
|
history.undo();
|
||||||
|
cmd7.redo(); history.add(&cmd7);
|
||||||
|
cmd8.redo(); history.add(&cmd8);
|
||||||
|
|
||||||
|
EXPECT_EQ(8, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(7, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(6, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(5, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(4, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(3, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(2, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(1, model);
|
||||||
|
history.undo();
|
||||||
|
EXPECT_EQ(0, model);
|
||||||
|
EXPECT_FALSE(history.canUndo());
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(1, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(2, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(3, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(4, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(5, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(6, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(7, model);
|
||||||
|
history.redo();
|
||||||
|
EXPECT_EQ(8, model);
|
||||||
|
EXPECT_FALSE(history.canRedo());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user