mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-26 12:35:33 +00:00
Convert undo library into a submodule
This commit is contained in:
parent
45fcd9e95e
commit
5ea955df4d
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -33,3 +33,6 @@
|
||||
[submodule "src/observable"]
|
||||
path = src/observable
|
||||
url = https://github.com/dacap/observable.git
|
||||
[submodule "src/undo"]
|
||||
path = src/undo
|
||||
url = https://github.com/aseprite/undo.git
|
||||
|
@ -85,16 +85,20 @@ set(OBSERVABLE_TESTS OFF CACHE BOOL "Compile observable tests")
|
||||
add_subdirectory(observable)
|
||||
include_directories(observable)
|
||||
|
||||
# Disable clip examples
|
||||
set(CLIP_EXAMPLES OFF CACHE BOOL "Compile clip examples")
|
||||
add_subdirectory(clip)
|
||||
|
||||
# Disable undo tests
|
||||
set(UNDO_TESTS OFF CACHE BOOL "Compile undo tests")
|
||||
add_subdirectory(undo)
|
||||
|
||||
# Our base library
|
||||
add_subdirectory(base)
|
||||
|
||||
# Directory where base/config.h file is located
|
||||
include_directories(${BASE_INCLUDE_DIR})
|
||||
|
||||
# Disable clip examples
|
||||
set(CLIP_EXAMPLES OFF CACHE BOOL "Compile clip examples")
|
||||
add_subdirectory(clip)
|
||||
|
||||
add_subdirectory(cfg)
|
||||
add_subdirectory(css)
|
||||
add_subdirectory(doc)
|
||||
@ -108,7 +112,6 @@ add_subdirectory(render)
|
||||
add_subdirectory(script)
|
||||
add_subdirectory(she)
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(undo)
|
||||
|
||||
if(ENABLE_UPDATER)
|
||||
add_subdirectory(updater)
|
||||
@ -178,7 +181,6 @@ if(ENABLE_TESTS)
|
||||
include(FindTests)
|
||||
|
||||
find_tests(base base-lib)
|
||||
find_tests(undo undo-lib)
|
||||
find_tests(gfx gfx-lib)
|
||||
find_tests(doc doc-lib)
|
||||
find_tests(render render-lib)
|
||||
|
@ -459,7 +459,7 @@ target_link_libraries(app-lib
|
||||
script-lib
|
||||
she
|
||||
ui-lib
|
||||
undo-lib
|
||||
undo
|
||||
${TINYXML_LIBRARY}
|
||||
${JPEG_LIBRARIES}
|
||||
${GIF_LIBRARIES}
|
||||
|
1
src/undo
Submodule
1
src/undo
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit f39b188e29d0f9adaa49c8705c0f492939d967a9
|
@ -1,5 +0,0 @@
|
||||
# Aseprite Undo Library
|
||||
# Copyright (C) 2015 David Capello
|
||||
|
||||
add_library(undo-lib
|
||||
undo_history.cpp)
|
@ -1,20 +0,0 @@
|
||||
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.
|
@ -1,4 +0,0 @@
|
||||
# Aseprite Undo Library
|
||||
*Copyright (C) 2015 David Capello*
|
||||
|
||||
> Distributed under [MIT license](LICENSE.txt)
|
@ -1,23 +0,0 @@
|
||||
// Aseprite Undo Library
|
||||
// Copyright (C) 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef UNDO_UNDO_COMMAND_H_INCLUDED
|
||||
#define UNDO_UNDO_COMMAND_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace undo {
|
||||
|
||||
class UndoCommand {
|
||||
public:
|
||||
virtual ~UndoCommand() { }
|
||||
virtual void undo() = 0;
|
||||
virtual void redo() = 0;
|
||||
virtual void dispose() = 0;
|
||||
};
|
||||
|
||||
} // namespace undo
|
||||
|
||||
#endif // UNDO_UNDO_COMMAND_H_INCLUDED
|
@ -1,153 +0,0 @@
|
||||
// Aseprite Undo 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 "undo/undo_history.h"
|
||||
|
||||
#include "undo/undo_command.h"
|
||||
#include "undo/undo_state.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <stack>
|
||||
|
||||
namespace undo {
|
||||
|
||||
UndoHistory::UndoHistory()
|
||||
: m_first(nullptr)
|
||||
, m_last(nullptr)
|
||||
, m_cur(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
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::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)
|
||||
{
|
||||
UndoState* state = new UndoState(cmd);
|
||||
state->m_prev = m_last;
|
||||
state->m_next = nullptr;
|
||||
state->m_parent = m_cur;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const UndoState* UndoHistory::findCommonParent(const UndoState* a,
|
||||
const UndoState* b)
|
||||
{
|
||||
const UndoState* pA = a;
|
||||
const 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(const UndoState* new_state)
|
||||
{
|
||||
const 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<const UndoState*> redo_parents;
|
||||
const 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 = const_cast<UndoState*>(new_state);
|
||||
}
|
||||
|
||||
} // namespace undo
|
@ -1,47 +0,0 @@
|
||||
// Aseprite Undo Library
|
||||
// Copyright (C) 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef UNDO_UNDO_HISTORY_H_INCLUDED
|
||||
#define UNDO_UNDO_HISTORY_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
namespace undo {
|
||||
|
||||
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();
|
||||
void clearRedo();
|
||||
|
||||
// This can be used to jump to a specific UndoState in the whole
|
||||
// history.
|
||||
void moveTo(const UndoState* new_state);
|
||||
|
||||
private:
|
||||
const UndoState* findCommonParent(const UndoState* a,
|
||||
const UndoState* b);
|
||||
|
||||
UndoState* m_first;
|
||||
UndoState* m_last;
|
||||
UndoState* m_cur; // Current action that can be undone
|
||||
};
|
||||
|
||||
} // namespace undo
|
||||
|
||||
#endif // UNDO_UNDO_HISTORY_H_INCLUDED
|
@ -1,45 +0,0 @@
|
||||
// Aseprite Undo Library
|
||||
// Copyright (C) 2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef UNDO_UNDO_STATE_H_INCLUDED
|
||||
#define UNDO_UNDO_STATE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "undo/undo_command.h"
|
||||
|
||||
namespace undo {
|
||||
|
||||
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(UndoCommand* cmd)
|
||||
: m_prev(nullptr)
|
||||
, m_next(nullptr)
|
||||
, m_parent(nullptr)
|
||||
, m_cmd(cmd) {
|
||||
}
|
||||
~UndoState() {
|
||||
if (m_cmd)
|
||||
m_cmd->dispose();
|
||||
}
|
||||
UndoState* prev() const { return m_prev; }
|
||||
UndoState* next() const { return m_next; }
|
||||
UndoCommand* cmd() const { return m_cmd; }
|
||||
private:
|
||||
UndoState* m_prev;
|
||||
UndoState* m_next;
|
||||
UndoState* m_parent; // Parent state, after we undo
|
||||
UndoCommand* m_cmd;
|
||||
};
|
||||
|
||||
} // namespace undo
|
||||
|
||||
#endif // UNDO_UNDO_STATE_H_INCLUDED
|
@ -1,182 +0,0 @@
|
||||
// Aseprite Undo Library
|
||||
// Copyright (C) 2015-2016 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 <gtest/gtest.h>
|
||||
|
||||
#include "undo/undo_command.h"
|
||||
#include "undo/undo_history.h"
|
||||
|
||||
using namespace undo;
|
||||
|
||||
class Cmd : public UndoCommand {
|
||||
public:
|
||||
Cmd(int& model, int redo_value, int undo_value)
|
||||
: m_model(model)
|
||||
, m_redo_value(redo_value)
|
||||
, m_undo_value(undo_value) {
|
||||
}
|
||||
void redo() override { m_model = m_redo_value; }
|
||||
void undo() override { m_model = m_undo_value; }
|
||||
void dispose() override { }
|
||||
private:
|
||||
int& m_model;
|
||||
int m_redo_value;
|
||||
int m_undo_value;
|
||||
};
|
||||
|
||||
TEST(Undo, Basics)
|
||||
{
|
||||
int model = 0;
|
||||
Cmd cmd1(model, 1, 0);
|
||||
Cmd cmd2(model, 2, 1);
|
||||
|
||||
EXPECT_EQ(0, model);
|
||||
cmd1.redo();
|
||||
EXPECT_EQ(1, model);
|
||||
cmd2.redo();
|
||||
EXPECT_EQ(2, model);
|
||||
|
||||
UndoHistory history;
|
||||
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, Tree)
|
||||
{
|
||||
// 1 --- 2
|
||||
// |
|
||||
// ------ 3 --- 4
|
||||
int model = 0;
|
||||
Cmd cmd1(model, 1, 0);
|
||||
Cmd cmd2(model, 2, 1);
|
||||
Cmd cmd3(model, 3, 1);
|
||||
Cmd cmd4(model, 4, 3);
|
||||
|
||||
UndoHistory history;
|
||||
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)
|
||||
{
|
||||
// 1 --- 2 --- 3 --- 4 ------ 7 --- 8
|
||||
// | |
|
||||
// ------------- 5 --- 6
|
||||
int model = 0;
|
||||
Cmd cmd1(model, 1, 0);
|
||||
Cmd cmd2(model, 2, 1);
|
||||
Cmd cmd3(model, 3, 2);
|
||||
Cmd cmd4(model, 4, 3);
|
||||
Cmd cmd5(model, 5, 2);
|
||||
Cmd cmd6(model, 6, 5);
|
||||
Cmd cmd7(model, 7, 5);
|
||||
Cmd cmd8(model, 8, 7);
|
||||
|
||||
UndoHistory history;
|
||||
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