mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-11 09:40:42 +00:00
Merge branch 'saved-undo-state' (#3578)
This commit is contained in:
commit
d029efbcc0
@ -997,6 +997,14 @@
|
||||
<text color="listitem_selected_text" align="left middle" x="1" state="selected" />
|
||||
<text color="disabled" align="left middle" x="1" state="disabled" />
|
||||
</style>
|
||||
<style id="undo_saved_item" extends="list_item">
|
||||
<text color="listitem_normal_text" align="left middle" x="11" />
|
||||
<text color="listitem_selected_text" align="left middle" x="11" state="selected" />
|
||||
<text color="disabled" align="left middle" x="11" state="disabled" />
|
||||
<icon part="icon_save" color="listitem_normal_text" align="left middle" x="1" />
|
||||
<icon part="icon_save" color="listitem_selected_text" align="left middle" x="1" state="selected" />
|
||||
<icon part="icon_save" color="disabled" align="left middle" x="1" state="disabled" />
|
||||
</style>
|
||||
<style id="aseprite_face">
|
||||
<icon part="aseprite_face" />
|
||||
<icon part="aseprite_face_mouse" state="mouse" />
|
||||
|
@ -987,6 +987,14 @@
|
||||
<text color="listitem_selected_text" align="left middle" x="1" state="selected" />
|
||||
<text color="disabled" align="left middle" x="1" state="disabled" />
|
||||
</style>
|
||||
<style id="undo_saved_item" extends="list_item">
|
||||
<text color="listitem_normal_text" align="left middle" x="11" />
|
||||
<text color="listitem_selected_text" align="left middle" x="11" state="selected" />
|
||||
<text color="disabled" align="left middle" x="11" state="disabled" />
|
||||
<icon part="icon_save" color="listitem_normal_text" align="left middle" x="1" />
|
||||
<icon part="icon_save" color="listitem_selected_text" align="left middle" x="1" state="selected" />
|
||||
<icon part="icon_save" color="disabled" align="left middle" x="1" state="disabled" />
|
||||
</style>
|
||||
<style id="aseprite_face">
|
||||
<icon part="aseprite_face" />
|
||||
<icon part="aseprite_face_mouse" state="mouse" />
|
||||
|
@ -231,7 +231,6 @@ if(ENABLE_UI)
|
||||
commands/cmd_copy_cel.cpp
|
||||
commands/cmd_copy_merged.cpp
|
||||
commands/cmd_cut.cpp
|
||||
commands/cmd_deselect_mask.cpp
|
||||
commands/cmd_discard_brush.cpp
|
||||
commands/cmd_duplicate_layer.cpp
|
||||
commands/cmd_duplicate_sprite.cpp
|
||||
@ -257,7 +256,6 @@ if(ENABLE_UI)
|
||||
commands/cmd_layer_visibility.cpp
|
||||
commands/cmd_link_cels.cpp
|
||||
commands/cmd_load_mask.cpp
|
||||
commands/cmd_mask_all.cpp
|
||||
commands/cmd_mask_by_color.cpp
|
||||
commands/cmd_mask_content.cpp
|
||||
commands/cmd_modify_selection.cpp
|
||||
@ -550,6 +548,7 @@ add_library(app-lib
|
||||
commands/cmd_change_pixel_format.cpp
|
||||
commands/cmd_color_quantization.cpp
|
||||
commands/cmd_crop.cpp
|
||||
commands/cmd_deselect_mask.cpp
|
||||
commands/cmd_export_sprite_sheet.cpp
|
||||
commands/cmd_fill_and_stroke.cpp
|
||||
commands/cmd_flatten_layers.cpp
|
||||
@ -557,6 +556,7 @@ add_library(app-lib
|
||||
commands/cmd_import_sprite_sheet.cpp
|
||||
commands/cmd_layer_from_background.cpp
|
||||
commands/cmd_load_palette.cpp
|
||||
commands/cmd_mask_all.cpp
|
||||
commands/cmd_merge_down_layer.cpp
|
||||
commands/cmd_new_file.cpp
|
||||
commands/cmd_new_frame.cpp
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -22,20 +22,17 @@
|
||||
namespace app {
|
||||
|
||||
CmdTransaction::CmdTransaction(const std::string& label,
|
||||
bool changeSavedState,
|
||||
int* savedCounter)
|
||||
bool changeSavedState)
|
||||
: m_ranges(nullptr)
|
||||
, m_label(label)
|
||||
, m_changeSavedState(changeSavedState)
|
||||
, m_savedCounter(savedCounter)
|
||||
{
|
||||
}
|
||||
|
||||
CmdTransaction* CmdTransaction::moveToEmptyCopy()
|
||||
{
|
||||
CmdTransaction* copy = new CmdTransaction(m_label,
|
||||
m_changeSavedState,
|
||||
m_savedCounter);
|
||||
m_changeSavedState);
|
||||
copy->m_spritePositionBefore = m_spritePositionBefore;
|
||||
copy->m_spritePositionAfter = m_spritePositionAfter;
|
||||
if (m_ranges) {
|
||||
@ -99,25 +96,16 @@ void CmdTransaction::onExecute()
|
||||
|
||||
// Execute the sequence of "cmds"
|
||||
CmdSequence::onExecute();
|
||||
|
||||
if (m_changeSavedState)
|
||||
++(*m_savedCounter);
|
||||
}
|
||||
|
||||
void CmdTransaction::onUndo()
|
||||
{
|
||||
CmdSequence::onUndo();
|
||||
|
||||
if (m_changeSavedState)
|
||||
--(*m_savedCounter);
|
||||
}
|
||||
|
||||
void CmdTransaction::onRedo()
|
||||
{
|
||||
CmdSequence::onRedo();
|
||||
|
||||
if (m_changeSavedState)
|
||||
++(*m_savedCounter);
|
||||
}
|
||||
|
||||
std::string CmdTransaction::onLabel() const
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -23,7 +23,9 @@ namespace app {
|
||||
class CmdTransaction : public CmdSequence {
|
||||
public:
|
||||
CmdTransaction(const std::string& label,
|
||||
bool changeSavedState, int* savedCounter);
|
||||
bool changeSavedState);
|
||||
|
||||
bool doesChangeSavedState() const { return m_changeSavedState; }
|
||||
|
||||
// Moves the CmdTransaction internals to a new copy in case that
|
||||
// we want to rollback this CmdTransaction and start again with
|
||||
@ -61,7 +63,6 @@ namespace app {
|
||||
std::unique_ptr<Ranges> m_ranges;
|
||||
std::string m_label;
|
||||
bool m_changeSavedState;
|
||||
int* m_savedCounter;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -48,7 +48,11 @@ void DeselectMaskCommand::onExecute(Context* context)
|
||||
tx(new cmd::DeselectMask(document));
|
||||
tx.commit();
|
||||
}
|
||||
update_screen_for_document(document);
|
||||
|
||||
#ifdef ENABLE_UI
|
||||
if (context->isUIAvailable())
|
||||
update_screen_for_document(document);
|
||||
#endif
|
||||
}
|
||||
|
||||
Command* CommandFactory::createDeselectMaskCommand()
|
||||
|
@ -59,7 +59,10 @@ void MaskAllCommand::onExecute(Context* context)
|
||||
docPref.show.selectionEdges(true);
|
||||
}
|
||||
|
||||
update_screen_for_document(document);
|
||||
#ifdef ENABLE_UI
|
||||
if (context->isUIAvailable())
|
||||
update_screen_for_document(document);
|
||||
#endif
|
||||
}
|
||||
|
||||
Command* CommandFactory::createMaskAllCommand()
|
||||
|
@ -9,7 +9,9 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/cmd.h"
|
||||
#include "app/cmd_transaction.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context.h"
|
||||
@ -23,6 +25,7 @@
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/site.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui/workspace.h"
|
||||
#include "base/mem_utils.h"
|
||||
#include "fmt/format.h"
|
||||
#include "ui/init_theme_event.h"
|
||||
@ -52,8 +55,12 @@ public:
|
||||
initTheme();
|
||||
}
|
||||
|
||||
void setUndoHistory(DocUndo* history) {
|
||||
void setUndoHistory(Doc* doc, DocUndo* history) {
|
||||
m_doc = doc;
|
||||
m_undoHistory = history;
|
||||
|
||||
updateSavedState();
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@ -94,6 +101,26 @@ public:
|
||||
view->setViewScroll(scroll);
|
||||
}
|
||||
|
||||
void updateSavedState() {
|
||||
const auto oldIsAssociatedToFile = m_isAssociatedToFile;
|
||||
const auto oldSavedState = m_savedState;
|
||||
|
||||
m_isAssociatedToFile =
|
||||
(m_doc && m_undoHistory ?
|
||||
m_doc->isAssociatedToFile() && !m_undoHistory->isSavedStateIsLost() :
|
||||
false);
|
||||
|
||||
// Get the state where this sprite was saved.
|
||||
m_savedState = nullptr;
|
||||
if (m_undoHistory && m_isAssociatedToFile)
|
||||
m_savedState = m_undoHistory->savedState();
|
||||
|
||||
if (oldIsAssociatedToFile != m_isAssociatedToFile ||
|
||||
oldSavedState != m_savedState) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
obs::signal<void(const undo::UndoState*)> Change;
|
||||
|
||||
protected:
|
||||
@ -283,6 +310,9 @@ public:
|
||||
return;
|
||||
|
||||
auto style = theme->styles.listItem();
|
||||
if (m_isAssociatedToFile && m_savedState == state) {
|
||||
style = theme->styles.undoSavedItem();
|
||||
}
|
||||
|
||||
ui::PaintWidgetPartInfo info;
|
||||
info.text = &itemText;
|
||||
@ -291,7 +321,10 @@ public:
|
||||
}
|
||||
|
||||
UndoHistoryWindow* m_window;
|
||||
Doc* m_doc = nullptr;
|
||||
DocUndo* m_undoHistory = nullptr;
|
||||
bool m_isAssociatedToFile = false;
|
||||
const undo::UndoState* m_savedState = nullptr;
|
||||
int m_itemHeight;
|
||||
};
|
||||
|
||||
@ -348,7 +381,7 @@ private:
|
||||
m_actions.invalidate();
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
selectState(m_doc->undoHistory()->currentState());
|
||||
selectCurrentState();
|
||||
Console::showException(ex);
|
||||
}
|
||||
}
|
||||
@ -376,19 +409,24 @@ private:
|
||||
|
||||
++m_nitems;
|
||||
|
||||
m_actions.updateSavedState();
|
||||
m_actions.invalidate();
|
||||
view()->updateView();
|
||||
|
||||
selectState(history->currentState());
|
||||
selectCurrentState();
|
||||
}
|
||||
|
||||
void onDeleteUndoState(DocUndo* history,
|
||||
undo::UndoState* state) override {
|
||||
m_actions.updateSavedState();
|
||||
--m_nitems;
|
||||
}
|
||||
|
||||
void onCurrentUndoStateChange(DocUndo* history) override {
|
||||
selectState(history->currentState());
|
||||
selectCurrentState();
|
||||
|
||||
// TODO DocView should be an DocUndoObserver and update its state automatically
|
||||
App::instance()->workspace()->updateTabs();
|
||||
}
|
||||
|
||||
void onClearRedo(DocUndo* history) override {
|
||||
@ -399,6 +437,10 @@ private:
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
void onNewSavedState(DocUndo* history) override {
|
||||
m_actions.updateSavedState();
|
||||
}
|
||||
|
||||
void attachDocument(Doc* doc) {
|
||||
if (m_doc == doc)
|
||||
return;
|
||||
@ -428,15 +470,15 @@ private:
|
||||
|
||||
void setUndoHistory(DocUndo* history) {
|
||||
m_nitems = 0;
|
||||
m_actions.setUndoHistory(history);
|
||||
m_actions.setUndoHistory(m_doc, history);
|
||||
view()->updateView();
|
||||
|
||||
if (history)
|
||||
m_actions.selectState(history->currentState());
|
||||
selectCurrentState();
|
||||
}
|
||||
|
||||
void selectState(const undo::UndoState* state) {
|
||||
m_actions.selectState(state);
|
||||
void selectCurrentState() {
|
||||
m_actions.selectState(m_doc->undoHistory()->currentState());
|
||||
}
|
||||
|
||||
void updateTitle() {
|
||||
|
@ -19,6 +19,7 @@ FOR_EACH_COMMAND(ConvolutionMatrix)
|
||||
FOR_EACH_COMMAND(CopyColors)
|
||||
FOR_EACH_COMMAND(CopyTiles)
|
||||
FOR_EACH_COMMAND(CropSprite)
|
||||
FOR_EACH_COMMAND(DeselectMask)
|
||||
FOR_EACH_COMMAND(Despeckle)
|
||||
FOR_EACH_COMMAND(ExportSpriteSheet)
|
||||
FOR_EACH_COMMAND(ExportTileset)
|
||||
@ -30,6 +31,7 @@ FOR_EACH_COMMAND(ImportSpriteSheet)
|
||||
FOR_EACH_COMMAND(InvertColor)
|
||||
FOR_EACH_COMMAND(LayerFromBackground)
|
||||
FOR_EACH_COMMAND(LoadPalette)
|
||||
FOR_EACH_COMMAND(MaskAll)
|
||||
FOR_EACH_COMMAND(MergeDownLayer)
|
||||
FOR_EACH_COMMAND(MoveColors)
|
||||
FOR_EACH_COMMAND(MoveTiles)
|
||||
@ -66,7 +68,6 @@ FOR_EACH_COMMAND(Copy)
|
||||
FOR_EACH_COMMAND(CopyCel)
|
||||
FOR_EACH_COMMAND(CopyMerged)
|
||||
FOR_EACH_COMMAND(Cut)
|
||||
FOR_EACH_COMMAND(DeselectMask)
|
||||
FOR_EACH_COMMAND(DiscardBrush)
|
||||
FOR_EACH_COMMAND(DuplicateLayer)
|
||||
FOR_EACH_COMMAND(DuplicateSprite)
|
||||
@ -102,7 +103,6 @@ FOR_EACH_COMMAND(LayerProperties)
|
||||
FOR_EACH_COMMAND(LayerVisibility)
|
||||
FOR_EACH_COMMAND(LinkCels)
|
||||
FOR_EACH_COMMAND(LoadMask)
|
||||
FOR_EACH_COMMAND(MaskAll)
|
||||
FOR_EACH_COMMAND(MaskByColor)
|
||||
FOR_EACH_COMMAND(MaskContent)
|
||||
FOR_EACH_COMMAND(ModifySelection)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -276,7 +276,7 @@ void Doc::notifyLayerGroupCollapseChange(Layer* layer)
|
||||
|
||||
bool Doc::isModified() const
|
||||
{
|
||||
return !m_undo->isSavedState();
|
||||
return !m_undo->isInSavedStateOrSimilar();
|
||||
}
|
||||
|
||||
bool Doc::isAssociatedToFile() const
|
||||
@ -286,8 +286,8 @@ bool Doc::isAssociatedToFile() const
|
||||
|
||||
void Doc::markAsSaved()
|
||||
{
|
||||
m_undo->markSavedState();
|
||||
m_flags |= kAssociatedToFile;
|
||||
m_undo->markSavedState();
|
||||
}
|
||||
|
||||
void Doc::impossibleToBackToSavedState()
|
||||
|
@ -31,10 +31,6 @@ namespace app {
|
||||
|
||||
DocUndo::DocUndo()
|
||||
: m_undoHistory(this)
|
||||
, m_ctx(nullptr)
|
||||
, m_totalUndoSize(0)
|
||||
, m_savedCounter(0)
|
||||
, m_savedStateIsLost(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -144,20 +140,65 @@ void DocUndo::clearRedo()
|
||||
notify_observers(&DocUndoObserver::onClearRedo, this);
|
||||
}
|
||||
|
||||
bool DocUndo::isSavedState() const
|
||||
bool DocUndo::isInSavedStateOrSimilar() const
|
||||
{
|
||||
return (!m_savedStateIsLost && m_savedCounter == 0);
|
||||
if (m_savedStateIsLost)
|
||||
return false;
|
||||
|
||||
// Here we try to find if we can reach the saved state from the
|
||||
// currentState() undoing or redoing and the sprite is exactly the
|
||||
// same as the saved state, e.g. this can happen if the undo states
|
||||
// don't modify the sprite (like actions that change the current
|
||||
// selection/mask boundaries).
|
||||
bool savedStateWithUndoes = true;
|
||||
|
||||
auto state = currentState();
|
||||
while (state) {
|
||||
if (m_savedState == state) {
|
||||
return true;
|
||||
}
|
||||
else if (STATE_CMD(state)->doesChangeSavedState()) {
|
||||
savedStateWithUndoes = false;
|
||||
break;
|
||||
}
|
||||
state = state->prev();
|
||||
}
|
||||
|
||||
// If we reached the end of the undo history (e.g. because all undo
|
||||
// states do not modify the sprite), the only way to be in the saved
|
||||
// state is if the initial point of history is the saved state too
|
||||
// i.e. when m_savedState is nullptr (and m_savedStateIsLost is
|
||||
// false).
|
||||
if (savedStateWithUndoes && m_savedState == nullptr)
|
||||
return true;
|
||||
|
||||
// Now we try with redoes.
|
||||
state = (currentState() ? currentState()->next(): firstState());
|
||||
while (state) {
|
||||
if (STATE_CMD(state)->doesChangeSavedState()) {
|
||||
return false;
|
||||
}
|
||||
else if (m_savedState == state) {
|
||||
return true;
|
||||
}
|
||||
state = state->next();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DocUndo::markSavedState()
|
||||
{
|
||||
m_savedCounter = 0;
|
||||
m_savedState = currentState();
|
||||
m_savedStateIsLost = false;
|
||||
notify_observers(&DocUndoObserver::onNewSavedState, this);
|
||||
}
|
||||
|
||||
void DocUndo::impossibleToBackToSavedState()
|
||||
{
|
||||
// Now there is no state related to the disk state.
|
||||
m_savedState = nullptr;
|
||||
m_savedStateIsLost = true;
|
||||
notify_observers(&DocUndoObserver::onNewSavedState, this);
|
||||
}
|
||||
|
||||
std::string DocUndo::nextUndoLabel() const
|
||||
@ -270,6 +311,11 @@ void DocUndo::onDeleteUndoState(undo::UndoState* state)
|
||||
|
||||
m_totalUndoSize -= cmd->memSize();
|
||||
notify_observers(&DocUndoObserver::onDeleteUndoState, this, state);
|
||||
|
||||
// Mark this document as impossible to match the version on disk
|
||||
// because we're just going to delete the saved state.
|
||||
if (m_savedState == state)
|
||||
impossibleToBackToSavedState();
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -44,10 +44,34 @@ namespace app {
|
||||
|
||||
void clearRedo();
|
||||
|
||||
bool isSavedState() const;
|
||||
// Returns true we are in the UndoState that matches the sprite
|
||||
// version on the disk (or we are in a similar state that doesn't
|
||||
// modify that same state, e.g. if the current state modifies the
|
||||
// selection but not the pixels, we are in a similar state)
|
||||
bool isInSavedStateOrSimilar() const;
|
||||
|
||||
// Returns true if the saved state was lost, e.g. because we
|
||||
// deleted the redo history and the saved state was there.
|
||||
bool isSavedStateIsLost() const { return m_savedStateIsLost; }
|
||||
|
||||
// Marks current UndoState as the one that matches the sprite on
|
||||
// the disk (this is used after saving the file).
|
||||
void markSavedState();
|
||||
|
||||
// Indicates that now it's impossible to back to the version of
|
||||
// the sprite that matches the saved version. This can be because
|
||||
// the save process fails or because we deleted the redo history
|
||||
// where the saved state was available.
|
||||
void impossibleToBackToSavedState();
|
||||
|
||||
// Returns the position in the undo history where this sprite was
|
||||
// saved, if this is nullptr, it means that the initial state is
|
||||
// the saved state (if m_savedStateIsLost is false) or it means
|
||||
// that there is no saved state (ifm_savedStateIsLost is true)
|
||||
const undo::UndoState* savedState() const {
|
||||
return m_savedState;
|
||||
}
|
||||
|
||||
std::string nextUndoLabel() const;
|
||||
std::string nextRedoLabel() const;
|
||||
|
||||
@ -58,8 +82,6 @@ namespace app {
|
||||
|
||||
Cmd* lastExecutedCmd() const;
|
||||
|
||||
int* savedCounter() { return &m_savedCounter; }
|
||||
|
||||
const undo::UndoState* firstState() const { return m_undoHistory.firstState(); }
|
||||
const undo::UndoState* lastState() const { return m_undoHistory.lastState(); }
|
||||
const undo::UndoState* currentState() const { return m_undoHistory.currentState(); }
|
||||
@ -74,19 +96,13 @@ namespace app {
|
||||
void onDeleteUndoState(undo::UndoState* state) override;
|
||||
|
||||
undo::UndoHistory m_undoHistory;
|
||||
Context* m_ctx;
|
||||
size_t m_totalUndoSize;
|
||||
|
||||
// This counter is equal to 0 if we are in the "saved state", i.e.
|
||||
// the document on memory is equal to the document on disk. This
|
||||
// value is less than 0 if we're in a past version of the document
|
||||
// (due undoes), or greater than 0 if we are in a future version
|
||||
// (due redoes).
|
||||
int m_savedCounter;
|
||||
const undo::UndoState* m_savedState = nullptr;
|
||||
Context* m_ctx = nullptr;
|
||||
size_t m_totalUndoSize = 0;
|
||||
|
||||
// True if the saved state was invalidated/corrupted/lost in some
|
||||
// way. E.g. If the save process fails.
|
||||
bool m_savedStateIsLost;
|
||||
bool m_savedStateIsLost = false;
|
||||
|
||||
DISABLE_COPYING(DocUndo);
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2021 Igara Studio S.A.
|
||||
// Copyright (C) 2021-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2015-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -26,6 +26,7 @@ namespace app {
|
||||
virtual void onCurrentUndoStateChange(DocUndo* history) { }
|
||||
virtual void onClearRedo(DocUndo* history) { }
|
||||
virtual void onTotalUndoSizeChange(DocUndo* history) { }
|
||||
virtual void onNewSavedState(DocUndo* history) { }
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -617,6 +617,14 @@ int Sprite_get_filename(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Sprite_get_isModified(lua_State* L)
|
||||
{
|
||||
auto sprite = get_docobj<Sprite>(L, 1);
|
||||
Doc* doc = static_cast<Doc*>(sprite->document());
|
||||
lua_pushboolean(L, doc->isModified());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Sprite_get_width(lua_State* L)
|
||||
{
|
||||
auto sprite = get_docobj<Sprite>(L, 1);
|
||||
@ -854,6 +862,7 @@ const luaL_Reg Sprite_methods[] = {
|
||||
|
||||
const Property Sprite_properties[] = {
|
||||
{ "filename", Sprite_get_filename, Sprite_set_filename },
|
||||
{ "isModified", Sprite_get_isModified, nullptr },
|
||||
{ "width", Sprite_get_width, Sprite_set_width },
|
||||
{ "height", Sprite_get_height, Sprite_set_height },
|
||||
{ "colorMode", Sprite_get_colorMode, nullptr },
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -46,8 +46,7 @@ Transaction::Transaction(
|
||||
m_undo = m_doc->undoHistory();
|
||||
|
||||
m_cmds = new CmdTransaction(label,
|
||||
modification == Modification::ModifyDocument,
|
||||
m_undo->savedCounter());
|
||||
modification == Modification::ModifyDocument);
|
||||
|
||||
// Here we are executing an empty CmdTransaction, just to save the
|
||||
// SpritePosition. Sub-cmds are executed then one by one, in
|
||||
|
2
src/undo
2
src/undo
@ -1 +1 @@
|
||||
Subproject commit 3cf8f20d08bf38b06b8e0e75757a18e31ddb12d9
|
||||
Subproject commit c868a0238973f04564253133c1cd3689f9aa3913
|
66
tests/scripts/sprite_modified.lua
Normal file
66
tests/scripts/sprite_modified.lua
Normal file
@ -0,0 +1,66 @@
|
||||
-- Copyright (C) 2022 Igara Studio S.A.
|
||||
--
|
||||
-- This file is released under the terms of the MIT license.
|
||||
-- Read LICENSE.txt for more information.
|
||||
|
||||
local fn = '_test_modified.png'
|
||||
|
||||
do
|
||||
local doc = Sprite(2, 2)
|
||||
|
||||
-- New sprites are created without modifications (can be closed
|
||||
-- without warning)
|
||||
assert(not doc.isModified)
|
||||
|
||||
doc.width = 3
|
||||
assert(doc.isModified)
|
||||
app.undo()
|
||||
assert(not doc.isModified)
|
||||
app.redo()
|
||||
assert(doc.isModified)
|
||||
|
||||
-- Not modified after it's saved
|
||||
doc:saveAs(fn)
|
||||
assert(not doc.isModified)
|
||||
|
||||
-- Modified if we undo the saved state
|
||||
app.undo()
|
||||
assert(doc.width == 2)
|
||||
assert(doc.isModified)
|
||||
app.redo()
|
||||
assert(doc.width == 3)
|
||||
assert(not doc.isModified)
|
||||
|
||||
-- Selection changes shouldn't change the modified flag
|
||||
app.command.MaskAll()
|
||||
assert(not doc.isModified)
|
||||
app.command.DeselectMask()
|
||||
assert(not doc.isModified)
|
||||
doc:saveAs(fn)
|
||||
assert(not doc.isModified)
|
||||
app.undo() -- Undo Deselect
|
||||
assert(not doc.isModified)
|
||||
app.undo() -- Undo Select All
|
||||
assert(not doc.isModified)
|
||||
assert(doc.width == 3)
|
||||
app.undo() -- Undo size change
|
||||
assert(doc.isModified)
|
||||
assert(doc.width == 2)
|
||||
end
|
||||
|
||||
do
|
||||
local doc = Sprite{ fromFile=fn }
|
||||
|
||||
-- Loaded sprites are created without modifications (can be closed
|
||||
-- without warning)
|
||||
assert(not doc.isModified)
|
||||
|
||||
app.command.MaskAll()
|
||||
assert(not doc.isModified)
|
||||
doc:saveAs(fn)
|
||||
assert(not doc.isModified)
|
||||
app.undo()
|
||||
assert(not doc.isModified)
|
||||
app.redo()
|
||||
assert(not doc.isModified)
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user