Add option to resize the final output of "Save Copy As" command (fix #685)

This commit is contained in:
David Capello 2015-06-03 16:34:27 -03:00
parent c6f2c48d66
commit e0fea708f8
10 changed files with 169 additions and 32 deletions

View File

@ -145,6 +145,10 @@
<option id="opacity_step" type="int" default="28" migrate="Onionskin.OpacityStep" /> <option id="opacity_step" type="int" default="28" migrate="Onionskin.OpacityStep" />
<option id="type" type="OnionskinType" default="OnionskinType::MERGE" migrate="Onionskin.Type" /> <option id="type" type="OnionskinType" default="OnionskinType::MERGE" migrate="Onionskin.Type" />
</section> </section>
<section id="save_copy">
<option id="filename" type="std::string" />
<option id="resize_scale" type="double" default="1" />
</section>
<section id="sprite_sheet"> <section id="sprite_sheet">
<option id="type" type="SpriteSheetType" default="SpriteSheetType::NONE" /> <option id="type" type="SpriteSheetType" default="SpriteSheetType::NONE" />
<option id="columns" type="int" default="0" /> <option id="columns" type="int" default="0" />

View File

@ -20,6 +20,23 @@
<label text="File type:" /> <label text="File type:" />
<hbox cell_align="horizontal"> <hbox cell_align="horizontal">
<combobox id="file_type" minwidth="70" /> <combobox id="file_type" minwidth="70" />
<hbox id="resize_options">
<label text="Resize:" />
<combobox id="resize">
<listitem text="25%" value="0.25" />
<listitem text="50%" value="0.5" />
<listitem text="100%" value="1" />
<listitem text="200%" value="2" />
<listitem text="300%" value="3" />
<listitem text="400%" value="4" />
<listitem text="500%" value="5" />
<listitem text="600%" value="6" />
<listitem text="700%" value="7" />
<listitem text="800%" value="8" />
<listitem text="900%" value="9" />
<listitem text="1000%" value="10" />
</combobox>
</hbox>
<boxfiller /> <boxfiller />
<box horizontal="true" homogeneous="true"> <box horizontal="true" homogeneous="true">
<button text="&amp;OK" closewindow="true" id="ok" magnet="true" width="60" /> <button text="&amp;OK" closewindow="true" id="ok" magnet="true" width="60" />

View File

@ -13,6 +13,7 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/params.h" #include "app/commands/params.h"
#include "app/console.h" #include "app/console.h"
#include "app/context_access.h" #include "app/context_access.h"
@ -20,9 +21,11 @@
#include "app/file_selector.h" #include "app/file_selector.h"
#include "app/job.h" #include "app/job.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/recent_files.h" #include "app/recent_files.h"
#include "app/ui/status_bar.h" #include "app/ui/status_bar.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/convert_to.h"
#include "base/fs.h" #include "base/fs.h"
#include "base/path.h" #include "base/path.h"
#include "base/thread.h" #include "base/thread.h"
@ -32,6 +35,28 @@
namespace app { namespace app {
class SaveAsCopyDelegate : public FileSelectorDelegate {
public:
SaveAsCopyDelegate(const app::Document* doc, double scale)
: m_doc(doc), m_resizeScale(scale) { }
bool hasResizeCombobox() override {
return true;
}
double getResizeScale() override {
return m_resizeScale;
}
void setResizeScale(double scale) override {
m_resizeScale = scale;
}
private:
const app::Document* m_doc;
double m_resizeScale;
};
class SaveFileJob : public Job, public IFileOpProgress { class SaveFileJob : public Job, public IFileOpProgress {
public: public:
SaveFileJob(FileOp* fop) SaveFileJob(FileOp* fop)
@ -71,8 +96,8 @@ private:
}; };
static void save_document_in_background(const Context* context, static void save_document_in_background(const Context* context,
const Document* document, bool mark_as_saved, const Document* document, bool mark_as_saved,
const std::string& fn_format) const std::string& fn_format)
{ {
base::UniquePtr<FileOp> fop(fop_to_save_document(context, base::UniquePtr<FileOp> fop(fop_to_save_document(context,
document, document->filename().c_str(), fn_format.c_str())); document, document->filename().c_str(), fn_format.c_str()));
@ -125,11 +150,19 @@ bool SaveFileBaseCommand::onEnabled(Context* context)
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable); return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
} }
void SaveFileBaseCommand::saveAsDialog(const ContextReader& reader, const char* dlgTitle, bool markAsSaved) bool SaveFileBaseCommand::saveAsDialog(Context* context,
const char* dlgTitle,
FileSelectorDelegate* delegate)
{ {
const Document* document = reader.document(); const Document* document = context->activeDocument();
std::string filename; std::string filename;
// If there is a delegate, we're doing a "Save Copy As", so we don't
// have to mark the file as saved.
bool saveCopyAs = (delegate != nullptr);
bool markAsSaved = (!saveCopyAs);
double scale = 1.0;
if (!m_filename.empty()) { if (!m_filename.empty()) {
filename = m_filename; filename = m_filename;
} }
@ -138,16 +171,23 @@ void SaveFileBaseCommand::saveAsDialog(const ContextReader& reader, const char*
filename = document->filename(); filename = document->filename();
std::string newfilename = app::show_file_selector( std::string newfilename = app::show_file_selector(
dlgTitle, filename, exts, FileSelectorType::Save); dlgTitle, filename, exts,
FileSelectorType::Save,
delegate);
if (newfilename.empty()) if (newfilename.empty())
return; return false;
filename = newfilename; filename = newfilename;
if (delegate &&
delegate->hasResizeCombobox()) {
scale = delegate->getResizeScale();
}
} }
std::string oldFilename; std::string oldFilename;
{ {
ContextWriter writer(reader); ContextWriter writer(context);
Document* documentWriter = writer.document(); Document* documentWriter = writer.document();
oldFilename = documentWriter->filename(); oldFilename = documentWriter->filename();
@ -156,20 +196,53 @@ void SaveFileBaseCommand::saveAsDialog(const ContextReader& reader, const char*
m_selectedFilename = filename; m_selectedFilename = filename;
} }
// Apply scale
bool undoResize = false;
if (scale != 1.0) {
Command* resizeCmd = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
ASSERT(resizeCmd);
if (resizeCmd) {
int width = document->sprite()->width();
int height = document->sprite()->height();
int newWidth = int(double(width) * scale);
int newHeight = int(double(height) * scale);
if (newWidth < 1) newWidth = 1;
if (newHeight < 1) newHeight = 1;
if (width != newWidth || height != newHeight) {
Params params;
params.set("use-ui", "false");
params.set("width", base::convert_to<std::string>(newWidth).c_str());
params.set("height", base::convert_to<std::string>(newHeight).c_str());
params.set("resize-method", "nearest-neighbor"); // TODO add algorithm in the UI?
context->executeCommand(resizeCmd, params);
undoResize = true;
}
}
}
// Save the document // Save the document
save_document_in_background( save_document_in_background(
reader.context(), const_cast<Document*>(document), context, const_cast<Document*>(document),
markAsSaved, m_filenameFormat); markAsSaved, m_filenameFormat);
// Undo resize
if (undoResize) {
Command* undoCmd = CommandsModule::instance()->getCommandByName(CommandId::Undo);
if (undoCmd)
context->executeCommand(undoCmd);
}
{ {
ContextWriter writer(reader); ContextWriter writer(context);
Document* documentWriter = writer.document(); Document* documentWriter = writer.document();
if (documentWriter->isModified()) if (document->isModified())
documentWriter->setFilename(oldFilename); documentWriter->setFilename(oldFilename);
else else
documentWriter->incrementVersion(); documentWriter->incrementVersion();
} }
return true;
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -192,23 +265,23 @@ SaveFileCommand::SaveFileCommand()
// [main thread] // [main thread]
void SaveFileCommand::onExecute(Context* context) void SaveFileCommand::onExecute(Context* context)
{ {
const ContextReader reader(context); Document* document = context->activeDocument();
const Document* document(reader.document());
// If the document is associated to a file in the file-system, we can // If the document is associated to a file in the file-system, we can
// save it directly without user interaction. // save it directly without user interaction.
if (document->isAssociatedToFile()) { if (document->isAssociatedToFile()) {
ContextWriter writer(reader); ContextWriter writer(context);
Document* documentWriter = writer.document(); Document* documentWriter = writer.document();
save_document_in_background(context, documentWriter, true, save_document_in_background(
context, documentWriter, true,
m_filenameFormat.c_str()); m_filenameFormat.c_str());
} }
// If the document isn't associated to a file, we must to show the // If the document isn't associated to a file, we must to show the
// save-as dialog to the user to select for first time the file-name // save-as dialog to the user to select for first time the file-name
// for this document. // for this document.
else { else {
saveAsDialog(reader, "Save File", true); saveAsDialog(context, "Save File");
} }
} }
@ -228,8 +301,7 @@ SaveFileAsCommand::SaveFileAsCommand()
void SaveFileAsCommand::onExecute(Context* context) void SaveFileAsCommand::onExecute(Context* context)
{ {
const ContextReader reader(context); saveAsDialog(context, "Save As");
saveAsDialog(reader, "Save As", true);
} }
class SaveFileCopyAsCommand : public SaveFileBaseCommand { class SaveFileCopyAsCommand : public SaveFileBaseCommand {
@ -248,17 +320,29 @@ SaveFileCopyAsCommand::SaveFileCopyAsCommand()
void SaveFileCopyAsCommand::onExecute(Context* context) void SaveFileCopyAsCommand::onExecute(Context* context)
{ {
const ContextReader reader(context); const Document* document = context->activeDocument();
const Document* document(reader.document()); std::string oldFilename = document->filename();
std::string old_filename = document->filename();
// show "Save As" dialog // show "Save As" dialog
saveAsDialog(reader, "Save Copy As", false); DocumentPreferences& docPref = Preferences::instance().document(document);
SaveAsCopyDelegate delegate(document, docPref.saveCopy.resizeScale());
// Is a default output filename in the preferences?
if (!docPref.saveCopy.filename().empty()) {
ContextWriter writer(context);
writer.document()->setFilename(
docPref.saveCopy.filename());
}
if (saveAsDialog(context, "Save Copy As", &delegate)) {
docPref.saveCopy.filename(document->filename());
docPref.saveCopy.resizeScale(delegate.getResizeScale());
}
// Restore the file name. // Restore the file name.
{ {
ContextWriter writer(reader); ContextWriter writer(context);
writer.document()->setFilename(old_filename.c_str()); writer.document()->setFilename(oldFilename.c_str());
} }
} }

View File

@ -14,7 +14,7 @@
#include <string> #include <string>
namespace app { namespace app {
class ContextReader; class FileSelectorDelegate;
class SaveFileBaseCommand : public Command { class SaveFileBaseCommand : public Command {
public: public:
@ -28,7 +28,8 @@ namespace app {
void onLoadParams(const Params& params) override; void onLoadParams(const Params& params) override;
bool onEnabled(Context* context) override; bool onEnabled(Context* context) override;
void saveAsDialog(const ContextReader& reader, const char* dlgTitle, bool markAsSaved); bool saveAsDialog(Context* context, const char* dlgTitle,
FileSelectorDelegate* delegate = nullptr);
std::string m_filename; std::string m_filename;
std::string m_filenameFormat; std::string m_filenameFormat;

View File

@ -155,6 +155,7 @@ SpriteSizeCommand::SpriteSizeCommand()
"Sprite Size", "Sprite Size",
CmdRecordableFlag) CmdRecordableFlag)
{ {
m_useUI = true;
m_width = 0; m_width = 0;
m_height = 0; m_height = 0;
m_scaleX = 1.0; m_scaleX = 1.0;
@ -169,6 +170,9 @@ Command* SpriteSizeCommand::clone() const
void SpriteSizeCommand::onLoadParams(const Params& params) void SpriteSizeCommand::onLoadParams(const Params& params)
{ {
std::string useUI = params.get("use-ui");
m_useUI = (useUI.empty() || (useUI == "true"));
std::string width = params.get("width"); std::string width = params.get("width");
if (!width.empty()) { if (!width.empty()) {
m_width = std::strtol(width.c_str(), NULL, 10); m_width = std::strtol(width.c_str(), NULL, 10);
@ -208,7 +212,7 @@ void SpriteSizeCommand::onExecute(Context* context)
int new_height = (m_height ? m_height: int(sprite->height()*m_scaleY)); int new_height = (m_height ? m_height: int(sprite->height()*m_scaleY));
ResizeMethod resize_method = m_resizeMethod; ResizeMethod resize_method = m_resizeMethod;
if (context->isUIAvailable()) { if (m_useUI && context->isUIAvailable()) {
// load the window widget // load the window widget
base::UniquePtr<Window> window(app::load_widget<Window>("sprite_size.xml", "sprite_size")); base::UniquePtr<Window> window(app::load_widget<Window>("sprite_size.xml", "sprite_size"));
m_widthPx = app::find_widget<Entry>(window, "width_px"); m_widthPx = app::find_widget<Entry>(window, "width_px");

View File

@ -49,6 +49,7 @@ namespace app {
ui::Entry* m_widthPerc; ui::Entry* m_widthPerc;
ui::Entry* m_heightPerc; ui::Entry* m_heightPerc;
bool m_useUI;
int m_width; int m_width;
int m_height; int m_height;
double m_scaleX; double m_scaleX;

View File

@ -21,10 +21,12 @@
namespace app { namespace app {
std::string show_file_selector(const std::string& title, std::string show_file_selector(
const std::string& title,
const std::string& initialPath, const std::string& initialPath,
const std::string& showExtensions, const std::string& showExtensions,
FileSelectorType type) FileSelectorType type,
FileSelectorDelegate* delegate)
{ {
if (Preferences::instance().experimental.useNativeFileDialog() && if (Preferences::instance().experimental.useNativeFileDialog() &&
she::instance()->nativeDialogs()) { she::instance()->nativeDialogs()) {
@ -55,7 +57,7 @@ std::string show_file_selector(const std::string& title,
} }
} }
FileSelector fileSelector(type); FileSelector fileSelector(type, delegate);
return fileSelector.show(title, initialPath, showExtensions); return fileSelector.show(title, initialPath, showExtensions);
} }

View File

@ -15,11 +15,20 @@ namespace app {
enum class FileSelectorType { Open, Save }; enum class FileSelectorType { Open, Save };
class FileSelectorDelegate {
public:
virtual ~FileSelectorDelegate() { }
virtual bool hasResizeCombobox() = 0;
virtual double getResizeScale() = 0;
virtual void setResizeScale(double scale) = 0;
};
std::string show_file_selector( std::string show_file_selector(
const std::string& title, const std::string& title,
const std::string& initialPath, const std::string& initialPath,
const std::string& showExtensions, const std::string& showExtensions,
FileSelectorType type); FileSelectorType type,
FileSelectorDelegate* delegate = nullptr);
} // namespace app } // namespace app

View File

@ -24,6 +24,7 @@
#include "app/ui/skin/skin_parts.h" #include "app/ui/skin/skin_parts.h"
#include "app/widget_loader.h" #include "app/widget_loader.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/convert_to.h"
#include "base/fs.h" #include "base/fs.h"
#include "base/path.h" #include "base/path.h"
#include "base/split_string.h" #include "base/split_string.h"
@ -233,10 +234,13 @@ private:
FileSelector* m_filesel; FileSelector* m_filesel;
}; };
FileSelector::FileSelector(FileSelectorType type) FileSelector::FileSelector(FileSelectorType type, FileSelectorDelegate* delegate)
: m_type(type) : m_type(type)
, m_delegate(delegate)
, m_navigationLocked(false) , m_navigationLocked(false)
{ {
bool withResizeOptions = (delegate && delegate->hasResizeCombobox());
addChild(new ArrowNavigator(this)); addChild(new ArrowNavigator(this));
m_fileName = new CustomFileNameEntry; m_fileName = new CustomFileNameEntry;
@ -288,6 +292,12 @@ FileSelector::FileSelector(FileSelectorType type)
m_fileList->FileSelected.connect(Bind<void>(&FileSelector::onFileListFileSelected, this)); m_fileList->FileSelected.connect(Bind<void>(&FileSelector::onFileListFileSelected, this));
m_fileList->FileAccepted.connect(Bind<void>(&FileSelector::onFileListFileAccepted, this)); m_fileList->FileAccepted.connect(Bind<void>(&FileSelector::onFileListFileAccepted, this));
m_fileList->CurrentFolderChanged.connect(Bind<void>(&FileSelector::onFileListCurrentFolderChanged, this)); m_fileList->CurrentFolderChanged.connect(Bind<void>(&FileSelector::onFileListCurrentFolderChanged, this));
resizeOptions()->setVisible(withResizeOptions);
if (withResizeOptions) {
resize()->setValue("1"); // 100% is default
resize()->setValue(base::convert_to<std::string>(m_delegate->getResizeScale()));
}
} }
void FileSelector::goBack() void FileSelector::goBack()
@ -570,6 +580,10 @@ again:
std::string lastpath = folder->getKeyName(); std::string lastpath = folder->getKeyName();
set_config_string("FileSelect", "CurrentDirectory", set_config_string("FileSelect", "CurrentDirectory",
lastpath.c_str()); lastpath.c_str());
if (m_delegate && m_delegate->hasResizeCombobox()) {
m_delegate->setResizeScale(base::convert_to<double>(resize()->getValue()));
}
} }
return result; return result;

View File

@ -30,7 +30,7 @@ namespace app {
class FileSelector : public app::gen::FileSelector { class FileSelector : public app::gen::FileSelector {
public: public:
FileSelector(FileSelectorType type); FileSelector(FileSelectorType type, FileSelectorDelegate* delegate);
void goBack(); void goBack();
void goForward(); void goForward();
@ -58,6 +58,7 @@ namespace app {
std::string getSelectedExtension() const; std::string getSelectedExtension() const;
FileSelectorType m_type; FileSelectorType m_type;
FileSelectorDelegate* m_delegate;
std::string m_defExtension; std::string m_defExtension;
CustomFileNameEntry* m_fileName; CustomFileNameEntry* m_fileName;
FileList* m_fileList; FileList* m_fileList;