Add CommandWithNewParams to load command parameters from Lua tables directly

With these new classes (Param/NewParams/CommandWithNewParams(Base)) we
can load parameters for commands from a Lua table directly without
converting to a string. So instead of

   Lua value -> string -> C++ param type

We can do

   Lua value -> C++ param type

directly.
This commit is contained in:
David Capello 2019-01-07 16:42:55 -03:00
parent 496d15c428
commit 4335f0c728
6 changed files with 249 additions and 44 deletions

View File

@ -194,7 +194,6 @@ if(ENABLE_UI)
commands/cmd_add_color.cpp
commands/cmd_advanced_mode.cpp
commands/cmd_cancel.cpp
commands/cmd_canvas_size.cpp
commands/cmd_cel_properties.cpp
commands/cmd_change_brush.cpp
commands/cmd_change_color.cpp
@ -213,7 +212,6 @@ if(ENABLE_UI)
commands/cmd_duplicate_sprite.cpp
commands/cmd_duplicate_view.cpp
commands/cmd_exit.cpp
commands/cmd_export_sprite_sheet.cpp
commands/cmd_eyedropper.cpp
commands/cmd_fill_and_stroke.cpp
commands/cmd_fit_screen.cpp
@ -506,9 +504,11 @@ add_library(app-lib
color_spaces.cpp
color_utils.cpp
commands/cmd_background_from_layer.cpp
commands/cmd_canvas_size.cpp
commands/cmd_cel_opacity.cpp
commands/cmd_change_pixel_format.cpp
commands/cmd_crop.cpp
commands/cmd_export_sprite_sheet.cpp
commands/cmd_flatten_layers.cpp
commands/cmd_layer_from_background.cpp
commands/cmd_load_palette.cpp
@ -523,6 +523,7 @@ add_library(app-lib
commands/command.cpp
commands/commands.cpp
commands/move_thing.cpp
commands/new_params.cpp
commands/quick_command.cpp
console.cpp
context.cpp

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -9,7 +10,7 @@
#endif
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/new_params.h"
#include "app/context.h"
#include "app/context_access.h"
#include "app/doc.h"
@ -581,43 +582,26 @@ private:
bool m_dataFilenameAskOverwrite;
};
class ExportSpriteSheetCommand : public Command {
struct ExportSpriteSheetParams : public NewParams {
Param<bool> ui { this, true, "ui" };
Param<bool> askOverwrite { this, true, { "askOverwrite", "ask-overwrite" } };
};
class ExportSpriteSheetCommand : public CommandWithNewParams<ExportSpriteSheetParams> {
public:
ExportSpriteSheetCommand();
Command* clone() const override { return new ExportSpriteSheetCommand(*this); }
void setUseUI(bool useUI) { m_useUI = useUI; }
protected:
void onLoadParams(const Params& params) override;
bool onEnabled(Context* context) override;
void onExecute(Context* context) override;
private:
bool m_useUI;
bool m_askOverwrite;
};
ExportSpriteSheetCommand::ExportSpriteSheetCommand()
: Command(CommandId::ExportSpriteSheet(), CmdRecordableFlag)
, m_useUI(true)
, m_askOverwrite(true)
: CommandWithNewParams(CommandId::ExportSpriteSheet(), CmdRecordableFlag)
{
}
void ExportSpriteSheetCommand::onLoadParams(const Params& params)
{
if (params.has_param("ui"))
m_useUI = params.get_as<bool>("ui");
else
m_useUI = true;
if (params.has_param("ask-overwrite"))
m_askOverwrite = params.get_as<bool>("ask-overwrite");
else
m_askOverwrite = true;
}
bool ExportSpriteSheetCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable);
@ -629,9 +613,10 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
Doc* document = site.document();
Sprite* sprite = site.sprite();
DocumentPreferences& docPref(Preferences::instance().document(document));
bool askOverwrite = m_askOverwrite;
if (m_useUI && context->isUIAvailable()) {
#ifdef ENABLE_UI
bool askOverwrite = params().askOverwrite();
if (params().ui() && context->isUIAvailable()) {
ExportSpriteSheetWindow window(site, docPref);
window.openWindowInForeground();
if (!window.ok())
@ -670,6 +655,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
askOverwrite = false; // Already asked in the ExportSpriteSheetWindow
}
#endif // ENABLE_UI
app::SpriteSheetType type = docPref.spriteSheet.type();
int columns = docPref.spriteSheet.columns();
@ -693,11 +679,13 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
const bool listFrameTags = docPref.spriteSheet.listFrameTags();
const bool listSlices = docPref.spriteSheet.listSlices();
#ifdef ENABLE_UI
if (context->isUIAvailable() && askOverwrite) {
if (!ask_overwrite(true, filename,
true, dataFilename))
return; // Do not overwrite
}
#endif
SelectedFrames selFrames;
FrameTag* frameTag =
@ -781,9 +769,13 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
if (!newDocument)
return;
StatusBar* statusbar = StatusBar::instance();
if (statusbar)
statusbar->showTip(1000, "Sprite Sheet Generated");
#ifdef ENABLE_UI
if (context->isUIAvailable()) {
StatusBar* statusbar = StatusBar::instance();
if (statusbar)
statusbar->showTip(1000, "Sprite Sheet Generated");
}
#endif
// Copy background and grid preferences
{

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -11,6 +11,7 @@ FOR_EACH_COMMAND(CanvasSize)
FOR_EACH_COMMAND(CelOpacity)
FOR_EACH_COMMAND(ChangePixelFormat)
FOR_EACH_COMMAND(CropSprite)
FOR_EACH_COMMAND(ExportSpriteSheet)
FOR_EACH_COMMAND(FlattenLayers)
FOR_EACH_COMMAND(LayerFromBackground)
FOR_EACH_COMMAND(LoadPalette)
@ -54,7 +55,6 @@ FOR_EACH_COMMAND(DuplicateLayer)
FOR_EACH_COMMAND(DuplicateSprite)
FOR_EACH_COMMAND(DuplicateView)
FOR_EACH_COMMAND(Exit)
FOR_EACH_COMMAND(ExportSpriteSheet)
FOR_EACH_COMMAND(Eyedropper)
FOR_EACH_COMMAND(Fill)
FOR_EACH_COMMAND(FitScreen)

View File

@ -0,0 +1,62 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/commands/new_params.h"
#include "app/script/luacpp.h"
namespace app {
template<>
void Param<bool>::fromString(const std::string& value)
{
m_value = (value == "1" || value == "true");
}
#ifdef ENABLE_SCRIPTING
template<>
void Param<bool>::fromLua(lua_State* L, int index)
{
m_value = lua_toboolean(L, index);
}
void CommandWithNewParamsBase::onLoadParams(const Params& params)
{
if (m_skipLoadParams) {
m_skipLoadParams = false;
return;
}
onResetValues();
for (const auto& pair : params) {
if (ParamBase* p = onGetParam(pair.first))
p->fromString(pair.second);
}
}
void CommandWithNewParamsBase::loadParamsFromLuaTable(lua_State* L, int index)
{
onResetValues();
if (lua_istable(L, index)) {
lua_pushnil(L);
while (lua_next(L, index) != 0) {
if (const char* k = lua_tostring(L, -2)) {
if (ParamBase* p = onGetParam(k))
p->fromLua(L, -1);
}
lua_pop(L, 1); // Pop the value, leave the key
}
}
m_skipLoadParams = true;
}
#endif
} // namespace app

View File

@ -0,0 +1,145 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_COMMANDS_NEW_PARAMS_H
#define APP_COMMANDS_NEW_PARAMS_H
#include "app/commands/command.h"
#include "app/commands/params.h"
#include <map>
#include <string>
#include <utility>
#ifdef ENABLE_SCRIPTING
extern "C" struct lua_State;
#endif
namespace app {
class ParamBase {
public:
virtual ~ParamBase() { }
virtual void resetValue() = 0;
virtual void fromString(const std::string& value) = 0;
#ifdef ENABLE_SCRIPTING
virtual void fromLua(lua_State* L, int index) = 0;
#endif
};
class NewParams {
public:
void addParam(const char* id, ParamBase* param) {
m_params[id] = param;
}
void resetValues() {
for (auto& pair : m_params)
pair.second->resetValue();
}
ParamBase* getParam(const std::string& id) {
auto it = m_params.find(id);
if (it != m_params.end())
return it->second;
else
return nullptr;
}
private:
std::map<std::string, ParamBase*> m_params;
};
template<typename T>
class Param : public ParamBase {
public:
Param(NewParams* params,
const T& defaultValue,
const char* id)
: m_defaultValue(defaultValue)
, m_value(defaultValue) {
if (id)
params->addParam(id, this);
}
Param(NewParams* params,
const T& defaultValue,
const std::initializer_list<const char*> ids)
: Param(params, defaultValue, nullptr) {
for (const auto& id : ids)
params->addParam(id, this);
}
void resetValue() override {
m_value = m_defaultValue;
}
void fromString(const std::string& value) override;
#ifdef ENABLE_SCRIPTING
void fromLua(lua_State* L, int index) override;
#endif
const T& operator()() const {
return m_value;
}
void operator()(const T& value) {
m_value = value;
}
private:
T m_defaultValue;
T m_value;
};
class CommandWithNewParamsBase : public Command {
public:
template<typename...Args>
CommandWithNewParamsBase(Args&&...args)
: Command(std::forward<Args>(args)...) { }
#ifdef ENABLE_SCRIPTING
void loadParamsFromLuaTable(lua_State* L, int index);
#endif
protected:
void onLoadParams(const Params& params) override;
virtual void onResetValues() = 0;
virtual ParamBase* onGetParam(const std::string& k) = 0;
private:
#ifdef ENABLE_SCRIPTING
bool m_skipLoadParams = false;
#endif
};
template<typename T>
class CommandWithNewParams : public CommandWithNewParamsBase {
public:
template<typename...Args>
CommandWithNewParams(Args&&...args)
: CommandWithNewParamsBase(std::forward<Args>(args)...) { }
T& params() {
return m_params;
}
private:
void onResetValues() override {
m_params.resetValues();
}
ParamBase* onGetParam(const std::string& k) override {
return m_params.getParam(k);
}
T m_params;
};
} // namespace app
#endif

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -11,6 +12,7 @@
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/new_params.h"
#include "app/commands/params.h"
#include "app/context.h"
#include "app/script/luacpp.h"
@ -31,18 +33,21 @@ int Command_call(lua_State* L)
auto command = get_ptr<Command>(L, 1);
Params params;
if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
const char* k = lua_tostring(L, -2);
if (k) {
const char* v = luaL_tolstring(L, -1, nullptr);
if (v) {
params.set(k, v);
if (auto commandLua = dynamic_cast<CommandWithNewParamsBase*>(command)) {
commandLua->loadParamsFromLuaTable(L, 2);
}
else {
if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
if (const char* k = lua_tostring(L, -2)) {
const char* v = luaL_tolstring(L, -1, nullptr);
if (v)
params.set(k, v);
lua_pop(L, 1); // pop the value generated by luaL_tolstring()
}
lua_pop(L, 1);
lua_pop(L, 1); // pop the value lua_next(), leave the key in the stack
}
lua_pop(L, 1);
}
}