mirror of https://github.com/aseprite/aseprite.git
Add option to resize the final output of "Save Copy As" command (fix #685)
This commit is contained in:
parent
c6f2c48d66
commit
e0fea708f8
|
@ -145,6 +145,10 @@
|
|||
<option id="opacity_step" type="int" default="28" migrate="Onionskin.OpacityStep" />
|
||||
<option id="type" type="OnionskinType" default="OnionskinType::MERGE" migrate="Onionskin.Type" />
|
||||
</section>
|
||||
<section id="save_copy">
|
||||
<option id="filename" type="std::string" />
|
||||
<option id="resize_scale" type="double" default="1" />
|
||||
</section>
|
||||
<section id="sprite_sheet">
|
||||
<option id="type" type="SpriteSheetType" default="SpriteSheetType::NONE" />
|
||||
<option id="columns" type="int" default="0" />
|
||||
|
|
|
@ -20,6 +20,23 @@
|
|||
<label text="File type:" />
|
||||
<hbox cell_align="horizontal">
|
||||
<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 />
|
||||
<box horizontal="true" homogeneous="true">
|
||||
<button text="&OK" closewindow="true" id="ok" magnet="true" width="60" />
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context_access.h"
|
||||
|
@ -20,9 +21,11 @@
|
|||
#include "app/file_selector.h"
|
||||
#include "app/job.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/recent_files.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "base/thread.h"
|
||||
|
@ -32,6 +35,28 @@
|
|||
|
||||
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 {
|
||||
public:
|
||||
SaveFileJob(FileOp* fop)
|
||||
|
@ -125,11 +150,19 @@ bool SaveFileBaseCommand::onEnabled(Context* context)
|
|||
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;
|
||||
|
||||
// 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()) {
|
||||
filename = m_filename;
|
||||
}
|
||||
|
@ -138,16 +171,23 @@ void SaveFileBaseCommand::saveAsDialog(const ContextReader& reader, const char*
|
|||
filename = document->filename();
|
||||
|
||||
std::string newfilename = app::show_file_selector(
|
||||
dlgTitle, filename, exts, FileSelectorType::Save);
|
||||
dlgTitle, filename, exts,
|
||||
FileSelectorType::Save,
|
||||
delegate);
|
||||
|
||||
if (newfilename.empty())
|
||||
return;
|
||||
return false;
|
||||
|
||||
filename = newfilename;
|
||||
if (delegate &&
|
||||
delegate->hasResizeCombobox()) {
|
||||
scale = delegate->getResizeScale();
|
||||
}
|
||||
}
|
||||
|
||||
std::string oldFilename;
|
||||
{
|
||||
ContextWriter writer(reader);
|
||||
ContextWriter writer(context);
|
||||
Document* documentWriter = writer.document();
|
||||
oldFilename = documentWriter->filename();
|
||||
|
||||
|
@ -156,20 +196,53 @@ void SaveFileBaseCommand::saveAsDialog(const ContextReader& reader, const char*
|
|||
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_document_in_background(
|
||||
reader.context(), const_cast<Document*>(document),
|
||||
context, const_cast<Document*>(document),
|
||||
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();
|
||||
|
||||
if (documentWriter->isModified())
|
||||
if (document->isModified())
|
||||
documentWriter->setFilename(oldFilename);
|
||||
else
|
||||
documentWriter->incrementVersion();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -192,23 +265,23 @@ SaveFileCommand::SaveFileCommand()
|
|||
// [main thread]
|
||||
void SaveFileCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(context);
|
||||
const Document* document(reader.document());
|
||||
Document* document = context->activeDocument();
|
||||
|
||||
// If the document is associated to a file in the file-system, we can
|
||||
// save it directly without user interaction.
|
||||
if (document->isAssociatedToFile()) {
|
||||
ContextWriter writer(reader);
|
||||
ContextWriter writer(context);
|
||||
Document* documentWriter = writer.document();
|
||||
|
||||
save_document_in_background(context, documentWriter, true,
|
||||
save_document_in_background(
|
||||
context, documentWriter, true,
|
||||
m_filenameFormat.c_str());
|
||||
}
|
||||
// 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
|
||||
// for this document.
|
||||
else {
|
||||
saveAsDialog(reader, "Save File", true);
|
||||
saveAsDialog(context, "Save File");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,8 +301,7 @@ SaveFileAsCommand::SaveFileAsCommand()
|
|||
|
||||
void SaveFileAsCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(context);
|
||||
saveAsDialog(reader, "Save As", true);
|
||||
saveAsDialog(context, "Save As");
|
||||
}
|
||||
|
||||
class SaveFileCopyAsCommand : public SaveFileBaseCommand {
|
||||
|
@ -248,17 +320,29 @@ SaveFileCopyAsCommand::SaveFileCopyAsCommand()
|
|||
|
||||
void SaveFileCopyAsCommand::onExecute(Context* context)
|
||||
{
|
||||
const ContextReader reader(context);
|
||||
const Document* document(reader.document());
|
||||
std::string old_filename = document->filename();
|
||||
const Document* document = context->activeDocument();
|
||||
std::string oldFilename = document->filename();
|
||||
|
||||
// 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.
|
||||
{
|
||||
ContextWriter writer(reader);
|
||||
writer.document()->setFilename(old_filename.c_str());
|
||||
ContextWriter writer(context);
|
||||
writer.document()->setFilename(oldFilename.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <string>
|
||||
|
||||
namespace app {
|
||||
class ContextReader;
|
||||
class FileSelectorDelegate;
|
||||
|
||||
class SaveFileBaseCommand : public Command {
|
||||
public:
|
||||
|
@ -28,7 +28,8 @@ namespace app {
|
|||
void onLoadParams(const Params& params) 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_filenameFormat;
|
||||
|
|
|
@ -155,6 +155,7 @@ SpriteSizeCommand::SpriteSizeCommand()
|
|||
"Sprite Size",
|
||||
CmdRecordableFlag)
|
||||
{
|
||||
m_useUI = true;
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
m_scaleX = 1.0;
|
||||
|
@ -169,6 +170,9 @@ Command* SpriteSizeCommand::clone() const
|
|||
|
||||
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");
|
||||
if (!width.empty()) {
|
||||
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));
|
||||
ResizeMethod resize_method = m_resizeMethod;
|
||||
|
||||
if (context->isUIAvailable()) {
|
||||
if (m_useUI && context->isUIAvailable()) {
|
||||
// load the window widget
|
||||
base::UniquePtr<Window> window(app::load_widget<Window>("sprite_size.xml", "sprite_size"));
|
||||
m_widthPx = app::find_widget<Entry>(window, "width_px");
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace app {
|
|||
ui::Entry* m_widthPerc;
|
||||
ui::Entry* m_heightPerc;
|
||||
|
||||
bool m_useUI;
|
||||
int m_width;
|
||||
int m_height;
|
||||
double m_scaleX;
|
||||
|
|
|
@ -21,10 +21,12 @@
|
|||
|
||||
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& showExtensions,
|
||||
FileSelectorType type)
|
||||
FileSelectorType type,
|
||||
FileSelectorDelegate* delegate)
|
||||
{
|
||||
if (Preferences::instance().experimental.useNativeFileDialog() &&
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,11 +15,20 @@ namespace app {
|
|||
|
||||
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(
|
||||
const std::string& title,
|
||||
const std::string& initialPath,
|
||||
const std::string& showExtensions,
|
||||
FileSelectorType type);
|
||||
FileSelectorType type,
|
||||
FileSelectorDelegate* delegate = nullptr);
|
||||
|
||||
} // namespace app
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "app/ui/skin/skin_parts.h"
|
||||
#include "app/widget_loader.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/convert_to.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/path.h"
|
||||
#include "base/split_string.h"
|
||||
|
@ -233,10 +234,13 @@ private:
|
|||
FileSelector* m_filesel;
|
||||
};
|
||||
|
||||
FileSelector::FileSelector(FileSelectorType type)
|
||||
FileSelector::FileSelector(FileSelectorType type, FileSelectorDelegate* delegate)
|
||||
: m_type(type)
|
||||
, m_delegate(delegate)
|
||||
, m_navigationLocked(false)
|
||||
{
|
||||
bool withResizeOptions = (delegate && delegate->hasResizeCombobox());
|
||||
|
||||
addChild(new ArrowNavigator(this));
|
||||
|
||||
m_fileName = new CustomFileNameEntry;
|
||||
|
@ -288,6 +292,12 @@ FileSelector::FileSelector(FileSelectorType type)
|
|||
m_fileList->FileSelected.connect(Bind<void>(&FileSelector::onFileListFileSelected, this));
|
||||
m_fileList->FileAccepted.connect(Bind<void>(&FileSelector::onFileListFileAccepted, 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()
|
||||
|
@ -570,6 +580,10 @@ again:
|
|||
std::string lastpath = folder->getKeyName();
|
||||
set_config_string("FileSelect", "CurrentDirectory",
|
||||
lastpath.c_str());
|
||||
|
||||
if (m_delegate && m_delegate->hasResizeCombobox()) {
|
||||
m_delegate->setResizeScale(base::convert_to<double>(resize()->getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace app {
|
|||
|
||||
class FileSelector : public app::gen::FileSelector {
|
||||
public:
|
||||
FileSelector(FileSelectorType type);
|
||||
FileSelector(FileSelectorType type, FileSelectorDelegate* delegate);
|
||||
|
||||
void goBack();
|
||||
void goForward();
|
||||
|
@ -58,6 +58,7 @@ namespace app {
|
|||
std::string getSelectedExtension() const;
|
||||
|
||||
FileSelectorType m_type;
|
||||
FileSelectorDelegate* m_delegate;
|
||||
std::string m_defExtension;
|
||||
CustomFileNameEntry* m_fileName;
|
||||
FileList* m_fileList;
|
||||
|
|
Loading…
Reference in New Issue