Merge branch '1.0'

Conflicts:
	src/app/commands/cmd_sprite_size.cpp
	src/app/document_exporter.cpp
This commit is contained in:
David Capello 2014-11-07 19:30:39 -03:00
commit 5e2cefe212
37 changed files with 1115 additions and 437 deletions

View File

@ -57,7 +57,7 @@ https://github.com/aseprite/aseprite
You can clone it locally using the following command (read-only URL):
git clone git://github.com/aseprite/aseprite.git
git clone --recursive https://github.com/aseprite/aseprite.git
On Windows you can use programs like
[msysgit](http://msysgit.github.io/) to clone the repository.

View File

@ -23,6 +23,7 @@
</hbox>
<check text="Show timeline automatically" id="autotimeline" tooltip="Show the timeline automatically&#10;when a new frame or layer is added." />
<check text="Expand menu bar items on mouseover" id="expand_menubar_on_mouseover" tooltip="Check this option to get&#10;this old menus behavior." />
<check text="Center editor when zoom with keys or zoom tool" id="center_on_zoom" />
<separator horizontal="true" />
<link id="locate_file" text="Locate Configuration File" />
<link id="locate_crash_folder" text="Locate Crash Folder" />

View File

@ -153,6 +153,46 @@ static RETSIGTYPE osx_signal_handler(int num)
static BOOL handle_mouse_enter(
NSRect frame, NSRect view, NSPoint point,
int* mx, int* my, int* buttons)
{
if (_mouse_installed && !_mouse_on &&
(osx_window) && (NSPointInRect(point, view))) {
*mx = point.x;
*my = frame.size.height - point.y;
*buttons = 0;
_mouse_on = TRUE;
osx_hide_native_mouse();
if (osx_mouse_enter_callback)
osx_mouse_enter_callback();
return YES;
}
else
return NO;
}
static BOOL handle_mouse_leave()
{
if (_mouse_installed) {
if (_mouse_on) {
_mouse_on = FALSE;
osx_show_native_mouse();
if (osx_mouse_leave_callback)
osx_mouse_leave_callback();
}
return YES;
}
else
return NO;
}
/* osx_event_handler:
* Event handling function; gets repeatedly called inside a dedicated thread.
*/
@ -164,8 +204,8 @@ void osx_event_handler()
NSPoint point;
NSRect frame, view;
int dx = 0, dy = 0, dz = 0;
int mx=_mouse_x;
int my=_mouse_y;
int mx = _mouse_x;
int my = _mouse_y;
static int buttons = 0;
int event_type;
BOOL gotmouseevent = NO;
@ -225,19 +265,10 @@ void osx_event_handler()
case NSLeftMouseDown:
case NSOtherMouseDown:
case NSRightMouseDown:
/* App is regaining focus */
if (![NSApp isActive]) {
/* App is regaining focus */
if (_mouse_installed) {
if ((osx_window) && (NSPointInRect(point, view))) {
mx = point.x;
my = frame.size.height - point.y;
buttons = 0;
if (!_mouse_on) {
_mouse_on = TRUE;
osx_hide_native_mouse();
}
}
}
handle_mouse_enter(frame, view, point, &mx, &my, &buttons);
if (osx_window)
[osx_window invalidateCursorRectsForView: [osx_window contentView]];
if (_keyboard_installed)
@ -259,6 +290,9 @@ void osx_event_handler()
case NSLeftMouseUp:
case NSOtherMouseUp:
case NSRightMouseUp:
if ([NSApp isActive])
handle_mouse_enter(frame, view, point, &mx, &my, &buttons);
buttons &= ~((event_type == NSLeftMouseUp) ? 0x1 : 0);
buttons &= ~((event_type == NSRightMouseUp) ? 0x2 : 0);
buttons &= ~((event_type == NSOtherMouseUp) ? 0x4 : 0);
@ -271,11 +305,14 @@ void osx_event_handler()
case NSRightMouseDragged:
case NSOtherMouseDragged:
case NSMouseMoved:
if ([NSApp isActive])
handle_mouse_enter(frame, view, point, &mx, &my, &buttons);
dx += [event deltaX];
dy += [event deltaY];
mx=point.x;
my=frame.size.height-point.y;
mx = point.x;
my = frame.size.height - point.y;
[NSApp sendEvent: event];
gotmouseevent = YES;
@ -288,35 +325,16 @@ void osx_event_handler()
case NSMouseEntered:
if (([event trackingNumber] == osx_mouse_tracking_rect) && ([NSApp isActive])) {
if (_mouse_installed) {
mx = point.x;
my = frame.size.height - point.y;
buttons = 0;
if (handle_mouse_enter(frame, view, point, &mx, &my, &buttons))
gotmouseevent = YES;
if (!_mouse_on) {
_mouse_on = TRUE;
osx_hide_native_mouse();
}
if (osx_mouse_enter_callback)
osx_mouse_enter_callback();
}
}
[NSApp sendEvent: event];
break;
case NSMouseExited:
if ([event trackingNumber] == osx_mouse_tracking_rect) {
if (_mouse_installed) {
if (handle_mouse_leave())
gotmouseevent = YES;
if (_mouse_on) {
_mouse_on = FALSE;
osx_show_native_mouse();
}
if (osx_mouse_leave_callback)
osx_mouse_leave_callback();
}
}
[NSApp sendEvent: event];
break;
@ -328,6 +346,8 @@ void osx_event_handler()
[osx_window invalidateCursorRectsForView: [osx_window contentView]];
if (_keyboard_installed)
osx_keyboard_focused(TRUE, 0);
handle_mouse_enter(frame, view, point, &mx, &my, &buttons);
}
_switch_in();
break;
@ -335,6 +355,7 @@ void osx_event_handler()
case NSApplicationDeactivatedEventType:
if (osx_window && _keyboard_installed)
osx_keyboard_focused(FALSE, 0);
handle_mouse_leave();
_switch_out();
break;

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -25,6 +25,8 @@
#include "app/app_options.h"
#include "app/check_update.h"
#include "app/color_utils.h"
#include "app/commands/cmd_save_file.h"
#include "app/commands/cmd_sprite_size.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/console.h"
@ -111,9 +113,7 @@ public:
App* App::m_instance = NULL;
// Initializes the application loading the modules, setting the
// graphics mode, loading the configuration and resources, etc.
App::App(int argc, const char* argv[])
App::App()
: m_modules(NULL)
, m_legacy(NULL)
, m_isGui(false)
@ -122,23 +122,22 @@ App::App(int argc, const char* argv[])
{
ASSERT(m_instance == NULL);
m_instance = this;
}
void App::initialize(int argc, const char* argv[])
{
AppOptions options(argc, argv);
// Initializes the application loading the modules, setting the
// graphics mode, loading the configuration and resources, etc.
m_modules = new Modules(!options.startUI(), options.verbose());
m_isGui = options.startUI();
m_isShell = options.startShell();
m_legacy = new LegacyModules(isGui() ? REQUIRE_INTERFACE: 0);
m_files = options.files();
if (options.hasExporterParams()) {
if (options.hasExporterParams())
m_exporter.reset(new DocumentExporter);
m_exporter->setDataFilename(options.data());
m_exporter->setTextureFilename(options.sheet());
m_exporter->setScale(options.scale());
}
// Register well-known image file types.
FileFormatsManager::instance()->registerAllFormats();
@ -177,20 +176,16 @@ App::App(int argc, const char* argv[])
// Set system palette to the default one.
set_current_palette(NULL, true);
}
int App::run()
{
UIContext* context = UIContext::instance();
// Initialize GUI interface
UIContext* ctx = UIContext::instance();
if (isGui()) {
PRINTF("GUI mode\n");
// Setup the GUI cursor and redraw screen
ui::set_use_native_cursors(
context->settings()->experimental()->useNativeCursor());
ctx->settings()->experimental()->useNativeCursor());
jmouse_set_cursor(kArrowCursor);
@ -215,25 +210,127 @@ int App::run()
// Procress options
PRINTF("Processing options...\n");
{
// Open file specified in the command line
if (!options.values().empty()) {
Console console;
for (const std::string& filename : m_files) {
// Load the sprite
Document* document = load_document(context, filename.c_str());
if (!document) {
if (!isGui())
console.printf("Error loading file \"%s\"\n", filename.c_str());
}
else {
// Add the given file in the argument as a "recent file" only
// if we are running in GUI mode. If the program is executed
// in batch mode this is not desirable.
if (isGui())
getRecentFiles()->addRecentFile(filename.c_str());
bool splitLayers = false;
std::string importLayer;
// Add the document to the exporter.
if (m_exporter != NULL)
m_exporter->addDocument(document);
for (const auto& value : options.values()) {
const AppOptions::Option* opt = value.option();
// Special options/commands
if (opt) {
// --data <file.json>
if (opt == &options.data()) {
if (m_exporter)
m_exporter->setDataFilename(value.value());
}
// --sheet <file.png>
else if (opt == &options.sheet()) {
if (m_exporter)
m_exporter->setTextureFilename(value.value());
}
// --sheet-width <width>
else if (opt == &options.sheetWidth()) {
if (m_exporter)
m_exporter->setTextureWidth(strtol(value.value().c_str(), NULL, 0));
}
// --sheet-height <height>
else if (opt == &options.sheetHeight()) {
if (m_exporter)
m_exporter->setTextureHeight(strtol(value.value().c_str(), NULL, 0));
}
// --sheet-pack
else if (opt == &options.sheetPack()) {
if (m_exporter)
m_exporter->setTexturePack(true);
}
// --split-layers
else if (opt == &options.splitLayers()) {
splitLayers = true;
}
// --import-layer <layer-name>
else if (opt == &options.importLayer()) {
importLayer = value.value();
}
// --save-as <filename>
else if (opt == &options.saveAs()) {
Document* doc = NULL;
if (!ctx->documents().empty())
doc = dynamic_cast<Document*>(ctx->documents().lastAdded());
if (!doc) {
console.printf("A document is needed before --save-as argument\n");
}
else {
ctx->setActiveDocument(doc);
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SaveFileCopyAs);
static_cast<SaveFileBaseCommand*>(command)->setFilename(value.value());
ctx->executeCommand(command);
}
}
// --scale <factor>
else if (opt == &options.scale()) {
Command* command = CommandsModule::instance()->getCommandByName(CommandId::SpriteSize);
double scale = strtod(value.value().c_str(), NULL);
static_cast<SpriteSizeCommand*>(command)->setScale(scale, scale);
// Scale all sprites
for (auto doc : ctx->documents()) {
ctx->setActiveDocument(doc);
ctx->executeCommand(command);
}
}
}
// File names aren't associated to any option
else {
const std::string& filename = value.value();
// Load the sprite
Document* doc = load_document(ctx, filename.c_str());
if (!doc) {
if (!isGui())
console.printf("Error loading file \"%s\"\n", filename.c_str());
}
else {
// Add the given file in the argument as a "recent file" only
// if we are running in GUI mode. If the program is executed
// in batch mode this is not desirable.
if (isGui())
getRecentFiles()->addRecentFile(filename.c_str());
if (m_exporter != NULL) {
if (!importLayer.empty()) {
std::vector<Layer*> layers;
Layer* foundLayer = NULL;
doc->sprite()->getLayersList(layers);
for (Layer* layer : layers) {
if (layer->name() == importLayer) {
foundLayer = layer;
break;
}
}
if (foundLayer)
m_exporter->addDocument(doc, foundLayer);
}
else if (splitLayers) {
std::vector<Layer*> layers;
doc->sprite()->getLayersList(layers);
for (auto layer : layers)
m_exporter->addDocument(doc, layer);
}
else
m_exporter->addDocument(doc);
}
}
if (!importLayer.empty())
importLayer.clear();
if (splitLayers)
splitLayers = false;
}
}
}
@ -245,7 +342,10 @@ int App::run()
m_exporter->exportSheet();
m_exporter.reset(NULL);
}
}
void App::run()
{
// Run the GUI
if (isGui()) {
#ifdef ENABLE_UPDATER
@ -265,32 +365,10 @@ int App::run()
// Run the GUI main message loop
gui_run();
// Destroy all documents in the UIContext.
const doc::Documents& docs = m_modules->m_ui_context.documents();
while (!docs.empty()) {
doc::Document* doc = docs.back();
// First we close the document. In this way we receive recent
// notifications related to the document as an app::Document. If
// we delete the document directly, we destroy the app::Document
// too early, and then doc::~Document() call
// DocumentsObserver::onRemoveDocument(). In this way, observers
// could think that they have a fully created app::Document when
// in reality it's a doc::Document (in the middle of a
// destruction process).
//
// TODO: This problem is because we're extending doc::Document,
// in the future, we should remove app::Document.
doc->close();
delete doc;
}
// Destroy the window.
m_mainWindow.reset(NULL);
}
// Start shell to execute scripts.
else if (m_isShell) {
if (m_isShell) {
m_systemConsole.prepareShell();
if (m_modules->m_scriptingEngine.supportEval()) {
@ -302,7 +380,30 @@ int App::run()
}
}
return 0;
// Destroy all documents in the UIContext.
const doc::Documents& docs = m_modules->m_ui_context.documents();
while (!docs.empty()) {
doc::Document* doc = docs.back();
// First we close the document. In this way we receive recent
// notifications related to the document as an app::Document. If
// we delete the document directly, we destroy the app::Document
// too early, and then doc::~Document() call
// DocumentsObserver::onRemoveDocument(). In this way, observers
// could think that they have a fully created app::Document when
// in reality it's a doc::Document (in the middle of a
// destruction process).
//
// TODO: This problem is because we're extending doc::Document,
// in the future, we should remove app::Document.
doc->close();
delete doc;
}
if (isGui()) {
// Destroy the window.
m_mainWindow.reset(NULL);
}
}
// Finishes the Aseprite application.
@ -405,7 +506,8 @@ void app_refresh_screen()
void app_rebuild_documents_tabs()
{
App::instance()->getMainWindow()->getTabsBar()->updateTabsText();
if (App::instance()->isGui())
App::instance()->getMainWindow()->getTabsBar()->updateTabsText();
}
PixelFormat app_get_current_pixel_format()

View File

@ -51,7 +51,7 @@ namespace app {
class App {
public:
App(int argc, const char* argv[]);
App();
~App();
static App* instance() { return m_instance; }
@ -63,8 +63,10 @@ namespace app {
bool isPortable();
// Runs the Aseprite application. In GUI mode it's the top-level
// window, in console/scripting it just runs the specified scripts.
int run();
// window, in console/scripting it just runs the specified
// scripts.
void initialize(int argc, const char* argv[]);
void run();
tools::ToolBox* getToolBox() const;
RecentFiles* getRecentFiles() const;

View File

@ -289,9 +289,10 @@ void AppMenus::applyShortcutToMenuitemsWithCommand(Menu* menu, Command* command,
Widget* child = *it;
if (child->getType() == kMenuItemWidget) {
ASSERT(dynamic_cast<AppMenuItem*>(child) != NULL);
AppMenuItem* menuitem = dynamic_cast<AppMenuItem*>(child);
if (!menuitem)
continue;
AppMenuItem* menuitem = static_cast<AppMenuItem*>(child);
Command* mi_command = menuitem->getCommand();
Params* mi_params = menuitem->getParams();

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -35,49 +35,40 @@ AppOptions::AppOptions(int argc, const char* argv[])
: m_exeName(base::get_file_name(argv[0]))
, m_startUI(true)
, m_startShell(false)
, m_verbose(false)
, m_scale(1.0)
, m_verboseEnabled(false)
, m_palette(m_po.add("palette").requiresValue("<filename>").description("Use a specific palette by default"))
, m_shell(m_po.add("shell").description("Start an interactive console to execute scripts"))
, m_batch(m_po.add("batch").description("Do not start the UI"))
, m_saveAs(m_po.add("save-as").requiresValue("<filename>").description("Save the last given document with other format"))
, m_scale(m_po.add("scale").requiresValue("<factor>").description("Resize all previous opened documents"))
, m_data(m_po.add("data").requiresValue("<filename.json>").description("File to store the sprite sheet metadata"))
, m_sheet(m_po.add("sheet").requiresValue("<filename.png>").description("Image file to save the texture"))
, m_sheetWidth(m_po.add("sheet-width").requiresValue("<pixels>").description("Sprite sheet width"))
, m_sheetHeight(m_po.add("sheet-height").requiresValue("<pixels>").description("Sprite sheet height"))
, m_sheetPack(m_po.add("sheet-pack").description("Use a packing algorithm to avoid waste of space\nin the texture"))
, m_splitLayers(m_po.add("split-layers").description("Import each layer of the next given sprite as\na separated image in the sheet"))
, m_importLayer(m_po.add("import-layer").requiresValue("<name>").description("Import just one layer of the next given sprite"))
, m_verbose(m_po.add("verbose").description("Explain what is being done"))
, m_help(m_po.add("help").mnemonic('?').description("Display this help and exits"))
, m_version(m_po.add("version").description("Output version information and exit"))
{
Option& palette = m_po.add("palette").requiresValue("<filename>").description("Use a specific palette by default");
Option& shell = m_po.add("shell").description("Start an interactive console to execute scripts");
Option& batch = m_po.add("batch").description("Do not start the UI");
// Option& dataFormat = m_po.add("format").requiresValue("<name>").description("Select the format for the sprite sheet data");
Option& data = m_po.add("data").requiresValue("<filename>").description("File to store the sprite sheet metadata (.json file)");
//Option& textureFormat = m_po.add("texture-format").requiresValue("<name>").description("Output texture format.");
Option& sheet = m_po.add("sheet").requiresValue("<filename>").description("Image file to save the texture (.png)");
//Option& scale = m_po.add("scale").requiresValue("<float>").description("");
//Option& scaleMode = m_po.add("scale-mode").requiresValue("<mode>").description("Export the first given document to a JSON object");
//Option& splitLayers = m_po.add("split-layers").description("Specifies that each layer of the given file should be saved as a different image in the sheet.");
//Option& rotsprite = m_po.add("rotsprite").requiresValue("<angle1,angle2,...>").description("Specifies different angles to export the given image.");
//Option& merge = m_po.add("merge").requiresValue("<datafiles>").description("Merge several sprite sheets in one.");
Option& verbose = m_po.add("verbose").description("Explain what is being done (in stderr or a log file)");
Option& help = m_po.add("help").mnemonic('?').description("Display this help and exits");
Option& version = m_po.add("version").description("Output version information and exit");
try {
m_po.parse(argc, argv);
m_verbose = verbose.enabled();
m_paletteFileName = palette.value();
m_startShell = shell.enabled();
// m_dataFormat = dataFormat.value();
m_data = data.value();
// m_textureFormat = textureFormat.value();
m_sheet = sheet.value();
// if (scale.enabled())
// m_scale = std::strtod(scale.value().c_str(), NULL);
// m_scaleMode = scaleMode.value();
m_verboseEnabled = m_po.enabled(m_verbose);
m_paletteFileName = m_po.value_of(m_palette);
m_startShell = m_po.enabled(m_shell);
if (help.enabled()) {
if (m_po.enabled(m_help)) {
showHelp();
m_startUI = false;
}
else if (version.enabled()) {
else if (m_po.enabled(m_version)) {
showVersion();
m_startUI = false;
}
if (shell.enabled() || batch.enabled()) {
if (m_po.enabled(m_shell) || m_po.enabled(m_batch)) {
m_startUI = false;
}
}
@ -88,6 +79,13 @@ AppOptions::AppOptions(int argc, const char* argv[])
}
}
bool AppOptions::hasExporterParams() const
{
return
m_po.enabled(m_data) ||
m_po.enabled(m_sheet);
}
void AppOptions::showHelp()
{
std::cout

View File

@ -30,33 +30,34 @@ namespace app {
class AppOptions {
public:
typedef base::ProgramOptions PO;
typedef PO::Option Option;
typedef PO::ValueList ValueList;
AppOptions(int argc, const char* argv[]);
bool startUI() const { return m_startUI; }
bool startShell() const { return m_startShell; }
bool verbose() const { return m_verbose; }
bool verbose() const { return m_verboseEnabled; }
const std::string& paletteFileName() const { return m_paletteFileName; }
const base::ProgramOptions::ValueList& files() const {
const ValueList& values() const {
return m_po.values();
}
// Export options
const std::string& dataFormat() const { return m_dataFormat; }
const std::string& data() const { return m_data; }
const std::string& textureFormat() const { return m_textureFormat; }
const std::string& sheet() const { return m_sheet; }
const double scale() const { return m_scale; }
const std::string& scaleMode() const { return m_scaleMode; }
const Option& saveAs() const { return m_saveAs; }
const Option& scale() const { return m_scale; }
const Option& data() const { return m_data; }
const Option& sheet() const { return m_sheet; }
const Option& sheetWidth() const { return m_sheetWidth; }
const Option& sheetHeight() const { return m_sheetHeight; }
const Option& sheetPack() const { return m_sheetPack; }
const Option& splitLayers() const { return m_splitLayers; }
const Option& importLayer() const { return m_importLayer; }
bool hasExporterParams() {
return
!m_dataFormat.empty() ||
!m_data.empty() ||
!m_textureFormat.empty() ||
!m_sheet.empty();
}
bool hasExporterParams() const;
private:
void showHelp();
@ -66,15 +67,26 @@ private:
base::ProgramOptions m_po;
bool m_startUI;
bool m_startShell;
bool m_verbose;
bool m_verboseEnabled;
std::string m_paletteFileName;
std::string m_dataFormat;
std::string m_data;
std::string m_textureFormat;
std::string m_sheet;
double m_scale;
std::string m_scaleMode;
Option& m_palette;
Option& m_shell;
Option& m_batch;
Option& m_saveAs;
Option& m_scale;
Option& m_data;
Option& m_sheet;
Option& m_sheetWidth;
Option& m_sheetHeight;
Option& m_sheetPack;
Option& m_splitLayers;
Option& m_importLayer;
Option& m_verbose;
Option& m_help;
Option& m_version;
};
} // namespace app

View File

@ -78,6 +78,9 @@ public:
ui::MenuBar::expandOnMouseover()))
expandMenubarOnMouseover()->setSelected(true);
if (get_config_bool("Editor", "CenterOnZoom", false))
centerOnZoom()->setSelected(true);
if (m_settings->experimental()->useNativeCursor())
nativeCursor()->setSelected(true);
@ -154,6 +157,8 @@ public:
set_config_bool("Options", "ExpandMenuBarOnMouseover", expandOnMouseover);
ui::MenuBar::setExpandOnMouseover(expandOnMouseover);
set_config_bool("Editor", "CenterOnZoom", centerOnZoom()->isSelected());
m_settings->setShowSpriteEditorScrollbars(showScrollbars()->isSelected());
m_settings->setZoomWithScrollWheel(wheelZoom()->isSelected());
m_settings->setRightClickMode(static_cast<RightClickMode>(rightClickBehavior()->getSelectedItemIndex()));

View File

@ -100,7 +100,7 @@ static void save_document_in_background(Context* context, Document* document, bo
else if (fop_is_stop(fop)) {
document->impossibleToBackToSavedState();
}
else {
else if (context->isUiAvailable()) {
App::instance()->getRecentFiles()->addRecentFile(document->filename().c_str());
if (mark_as_saved)
document->markAsSaved();

View File

@ -35,6 +35,10 @@ namespace app {
return m_selectedFilename;
}
void setFilename(const std::string& fn) {
m_filename = fn;
}
protected:
void onLoadParams(Params* params) override;
bool onEnabled(Context* context) override;

View File

@ -20,7 +20,9 @@
#include "config.h"
#endif
#include "app/commands/cmd_sprite_size.h"
#include "app/commands/command.h"
#include "app/commands/params.h"
#include "app/context_access.h"
#include "app/document_api.h"
#include "app/find_widget.h"
@ -157,34 +159,48 @@ protected:
};
class SpriteSizeCommand : public Command {
public:
SpriteSizeCommand();
Command* clone() const override { return new SpriteSizeCommand(*this); }
protected:
bool onEnabled(Context* context);
void onExecute(Context* context);
private:
void onLockRatioClick();
void onWidthPxChange();
void onHeightPxChange();
void onWidthPercChange();
void onHeightPercChange();
CheckBox* m_lockRatio;
Entry* m_widthPx;
Entry* m_heightPx;
Entry* m_widthPerc;
Entry* m_heightPerc;
};
SpriteSizeCommand::SpriteSizeCommand()
: Command("SpriteSize",
"Sprite Size",
CmdRecordableFlag)
{
m_width = 0;
m_height = 0;
m_scaleX = 1.0;
m_scaleY = 1.0;
m_resizeMethod = doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR;
}
Command* SpriteSizeCommand::clone() const
{
return new SpriteSizeCommand(*this);
}
void SpriteSizeCommand::onLoadParams(Params* params)
{
std::string width = params->get("width");
if (!width.empty()) {
m_width = std::strtol(width.c_str(), NULL, 10);
}
else
m_width = 0;
std::string height = params->get("height");
if (!height.empty()) {
m_height = std::strtol(height.c_str(), NULL, 10);
}
else
m_height = 0;
std::string resize_method = params->get("resize-method");
if (!resize_method.empty()) {
if (resize_method == "bilinear")
m_resizeMethod = doc::algorithm::RESIZE_METHOD_BILINEAR;
else
m_resizeMethod = doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR;
}
else
m_resizeMethod = doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR;
}
bool SpriteSizeCommand::onEnabled(Context* context)
@ -195,58 +211,63 @@ bool SpriteSizeCommand::onEnabled(Context* context)
void SpriteSizeCommand::onExecute(Context* context)
{
const ContextReader reader(UIContext::instance()); // TODO use the context in sprite size command
const ContextReader reader(context);
const Sprite* sprite(reader.sprite());
int new_width = (m_width ? m_width: sprite->width()*m_scaleX);
int new_height = (m_height ? m_height: sprite->height()*m_scaleY);
ResizeMethod resize_method = m_resizeMethod;
// 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");
m_heightPx = app::find_widget<Entry>(window, "height_px");
m_widthPerc = app::find_widget<Entry>(window, "width_perc");
m_heightPerc = app::find_widget<Entry>(window, "height_perc");
m_lockRatio = app::find_widget<CheckBox>(window, "lock_ratio");
ComboBox* method = app::find_widget<ComboBox>(window, "method");
Widget* ok = app::find_widget<Widget>(window, "ok");
if (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");
m_heightPx = app::find_widget<Entry>(window, "height_px");
m_widthPerc = app::find_widget<Entry>(window, "width_perc");
m_heightPerc = app::find_widget<Entry>(window, "height_perc");
m_lockRatio = app::find_widget<CheckBox>(window, "lock_ratio");
ComboBox* method = app::find_widget<ComboBox>(window, "method");
Widget* ok = app::find_widget<Widget>(window, "ok");
m_widthPx->setTextf("%d", sprite->width());
m_heightPx->setTextf("%d", sprite->height());
m_widthPx->setTextf("%d", new_width);
m_heightPx->setTextf("%d", new_height);
m_lockRatio->Click.connect(Bind<void>(&SpriteSizeCommand::onLockRatioClick, this));
m_widthPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPxChange, this));
m_heightPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPxChange, this));
m_widthPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPercChange, this));
m_heightPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPercChange, this));
m_lockRatio->Click.connect(Bind<void>(&SpriteSizeCommand::onLockRatioClick, this));
m_widthPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPxChange, this));
m_heightPx->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPxChange, this));
m_widthPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onWidthPercChange, this));
m_heightPerc->EntryChange.connect(Bind<void>(&SpriteSizeCommand::onHeightPercChange, this));
method->addItem("Nearest-neighbor");
method->addItem("Bilinear");
method->setSelectedItemIndex(get_config_int("SpriteSize", "Method",
doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR));
method->addItem("Nearest-neighbor");
method->addItem("Bilinear");
method->setSelectedItemIndex(get_config_int("SpriteSize", "Method",
doc::algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR));
window->remapWindow();
window->centerWindow();
window->remapWindow();
window->centerWindow();
load_window_pos(window, "SpriteSize");
window->setVisible(true);
window->openWindowInForeground();
save_window_pos(window, "SpriteSize");
load_window_pos(window, "SpriteSize");
window->setVisible(true);
window->openWindowInForeground();
save_window_pos(window, "SpriteSize");
if (window->getKiller() == ok) {
int new_width = m_widthPx->getTextInt();
int new_height = m_heightPx->getTextInt();
ResizeMethod resize_method =
(ResizeMethod)method->getSelectedItemIndex();
if (window->getKiller() != ok)
return;
new_width = m_widthPx->getTextInt();
new_height = m_heightPx->getTextInt();
resize_method = (ResizeMethod)method->getSelectedItemIndex();
set_config_int("SpriteSize", "Method", resize_method);
{
SpriteSizeJob job(reader, new_width, new_height, resize_method);
job.startJob();
job.waitJob();
}
ContextWriter writer(reader);
update_screen_for_document(writer.document());
}
{
SpriteSizeJob job(reader, new_width, new_height, resize_method);
job.startJob();
job.waitJob();
}
ContextWriter writer(reader);
update_screen_for_document(writer.document());
}
void SpriteSizeCommand::onLockRatioClick()

View File

@ -0,0 +1,72 @@
/* Aseprite
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef APP_COMMANDS_CMD_SPRITE_SIZE_H_INCLUDED
#define APP_COMMANDS_CMD_SPRITE_SIZE_H_INCLUDED
#pragma once
#include "app/commands/command.h"
#include "doc/algorithm/resize_image.h"
#include <string>
namespace ui {
class CheckBox;
class Entry;
}
namespace app {
class SpriteSizeCommand : public Command {
public:
SpriteSizeCommand();
Command* clone() const override;
void setScale(double x, double y) {
m_scaleX = x;
m_scaleY = y;
}
protected:
virtual void onLoadParams(Params* params) override;
virtual bool onEnabled(Context* context) override;
virtual void onExecute(Context* context) override;
private:
void onLockRatioClick();
void onWidthPxChange();
void onHeightPxChange();
void onWidthPercChange();
void onHeightPercChange();
ui::CheckBox* m_lockRatio;
ui::Entry* m_widthPx;
ui::Entry* m_heightPx;
ui::Entry* m_widthPerc;
ui::Entry* m_heightPerc;
int m_width;
int m_height;
double m_scaleX;
double m_scaleY;
doc::algorithm::ResizeMethod m_resizeMethod;
};
} // namespace app
#endif

View File

@ -29,14 +29,16 @@
#include "app/ui_context.h"
#include "base/path.h"
#include "base/unique_ptr.h"
#include "gfx/size.h"
#include "doc/cel.h"
#include "doc/dithering_method.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/palette.h"
#include "doc/primitives.h"
#include "doc/sprite.h"
#include "doc/stock.h"
#include "gfx/packing_rects.h"
#include "gfx/size.h"
#include <cstdio>
#include <fstream>
@ -48,16 +50,18 @@ namespace app {
class DocumentExporter::Sample {
public:
Sample(Document* document, Sprite* sprite,
Sample(Document* document, Sprite* sprite, Layer* layer,
FrameNumber frame, const std::string& filename) :
m_document(document),
m_sprite(sprite),
m_layer(layer),
m_frame(frame),
m_filename(filename) {
}
Document* document() const { return m_document; }
Sprite* sprite() const { return m_sprite; }
Layer* layer() const { return m_layer; }
FrameNumber frame() const { return m_frame; }
std::string filename() const { return m_filename; }
const gfx::Size& originalSize() const { return m_originalSize; }
@ -78,6 +82,7 @@ public:
private:
Document* m_document;
Sprite* m_sprite;
Layer* m_layer;
FrameNumber m_frame;
std::string m_filename;
gfx::Size m_originalSize;
@ -109,39 +114,99 @@ private:
class DocumentExporter::LayoutSamples {
public:
virtual ~LayoutSamples() { }
virtual void layoutSamples(Samples& samples) = 0;
virtual void layoutSamples(Samples& samples, int& width, int& height) = 0;
};
class DocumentExporter::SimpleLayoutSamples :
public DocumentExporter::LayoutSamples {
public:
void layoutSamples(Samples& samples) override {
void layoutSamples(Samples& samples, int& width, int& height) override {
const Sprite* oldSprite = NULL;
const Layer* oldLayer = NULL;
gfx::Point framePt(0, 0);
for (Samples::iterator it=samples.begin(), end=samples.end();
it != end; ++it) {
const Sprite* sprite = it->sprite();
for (auto& sample : samples) {
const Sprite* sprite = sample.sprite();
const Layer* layer = sample.layer();
gfx::Size size(sprite->width(), sprite->height());
it->setOriginalSize(size);
it->setTrimmedBounds(gfx::Rect(gfx::Point(0, 0), size));
it->setInTextureBounds(gfx::Rect(framePt, size));
// All frames of each sprite in one row.
if (oldSprite != NULL && oldSprite != it->sprite()) {
framePt.x = 0;
framePt.y += size.h;
}
else {
framePt.x += size.w;
if (oldSprite) {
// If the user didn't specified a width for the texture, we put
// each sprite/layer in a different row.
if (width == 0) {
// New sprite or layer, go to next row.
if (oldSprite != sprite || oldLayer != layer) {
framePt.x = 0;
framePt.y += oldSprite->height(); // We're skipping the previous sprite height
}
}
// When a texture width is specified, we can put different
// sprites/layers in each row until we reach the texture
// right-border.
else if (framePt.x+size.w > width) {
framePt.x = 0;
framePt.y += oldSprite->height();
// TODO framePt.y+size.h > height ?
}
}
oldSprite = it->sprite();
sample.setOriginalSize(size);
sample.setTrimmedBounds(gfx::Rect(gfx::Point(0, 0), size));
sample.setInTextureBounds(gfx::Rect(framePt, size));
// Next frame position.
framePt.x += size.w;
oldSprite = sprite;
oldLayer = layer;
}
}
};
class DocumentExporter::BestFitLayoutSamples :
public DocumentExporter::LayoutSamples {
public:
void layoutSamples(Samples& samples, int& width, int& height) override {
gfx::PackingRects pr;
for (auto& sample : samples) {
const Sprite* sprite = sample.sprite();
gfx::Size size(sprite->width(), sprite->height());
sample.setOriginalSize(size);
sample.setTrimmedBounds(gfx::Rect(gfx::Point(0, 0), size));
pr.add(size);
}
if (width == 0 || height == 0) {
gfx::Size sz = pr.bestFit();
width = sz.w;
height = sz.h;
}
else
pr.pack(gfx::Size(width, height));
auto it = samples.begin();
for (auto& rc : pr) {
ASSERT(it != samples.end());
it->setInTextureBounds(rc);
++it;
}
}
};
DocumentExporter::DocumentExporter()
: m_dataFormat(DefaultDataFormat)
, m_textureFormat(DefaultTextureFormat)
, m_textureWidth(0)
, m_textureHeight(0)
, m_texturePack(false)
, m_scaleMode(DefaultScaleMode)
, m_scale(1.0)
{
}
void DocumentExporter::exportSheet()
{
// We output the metadata to std::cout if the user didn't specify a file.
@ -166,15 +231,22 @@ void DocumentExporter::exportSheet()
}
// 2) Layout those samples in a texture field.
SimpleLayoutSamples layout;
layout.layoutSamples(samples);
if (m_texturePack) {
BestFitLayoutSamples layout;
layout.layoutSamples(samples, m_textureWidth, m_textureHeight);
}
else {
SimpleLayoutSamples layout;
layout.layoutSamples(samples, m_textureWidth, m_textureHeight);
}
// 3) Create and render the texture.
base::UniquePtr<Document> textureDocument(
createEmptyTexture(samples));
Sprite* texture = textureDocument->sprite();
Image* textureImage = static_cast<LayerImage*>(texture->folder()->getFirstLayer())
Image* textureImage = static_cast<LayerImage*>(
texture->folder()->getFirstLayer())
->getCel(FrameNumber(0))->image();
renderTexture(samples, textureImage);
@ -193,15 +265,14 @@ void DocumentExporter::captureSamples(Samples& samples)
{
std::vector<char> buf(32);
for (std::vector<Document*>::iterator
it = m_documents.begin(),
end = m_documents.end(); it != end; ++it) {
Document* document = *it;
Sprite* sprite = document->sprite();
for (auto& item : m_documents) {
Document* doc = item.doc;
Sprite* sprite = doc->sprite();
Layer* layer = item.layer;
for (FrameNumber frame=FrameNumber(0);
frame<sprite->totalFrames(); ++frame) {
std::string filename = document->filename();
std::string filename = doc->filename();
if (sprite->totalFrames() > FrameNumber(1)) {
int frameNumWidth =
@ -212,11 +283,16 @@ void DocumentExporter::captureSamples(Samples& samples)
std::string path = base::get_file_path(filename);
std::string title = base::get_file_title(filename);
if (layer) {
title += "-";
title += layer->name();
}
std::string ext = base::get_file_extension(filename);
filename = base::join_path(path, title + &buf[0] + "." + ext);
}
samples.addSample(Sample(document, sprite, frame, filename));
samples.addSample(Sample(doc, sprite, layer, frame, filename));
}
}
}
@ -225,7 +301,7 @@ Document* DocumentExporter::createEmptyTexture(const Samples& samples)
{
Palette* palette = NULL;
PixelFormat pixelFormat = IMAGE_INDEXED;
gfx::Rect fullTextureBounds;
gfx::Rect fullTextureBounds(0, 0, m_textureWidth, m_textureHeight);
int maxColors = 256;
for (Samples::const_iterator
@ -268,21 +344,26 @@ void DocumentExporter::renderTexture(const Samples& samples, Image* textureImage
{
textureImage->clear(0);
for (Samples::const_iterator
it = samples.begin(),
end = samples.end(); it != end; ++it) {
for (const auto& sample : samples) {
// Make the sprite compatible with the texture so the render()
// works correctly.
if (it->sprite()->pixelFormat() != textureImage->pixelFormat()) {
DocumentApi docApi(it->document(), NULL); // DocumentApi without undo
docApi.setPixelFormat(it->sprite(), textureImage->pixelFormat(),
if (sample.sprite()->pixelFormat() != textureImage->pixelFormat()) {
DocumentApi docApi(sample.document(), NULL); // DocumentApi without undo
docApi.setPixelFormat(
sample.sprite(),
textureImage->pixelFormat(),
DITHERING_NONE);
}
it->sprite()->render(textureImage,
it->inTextureBounds().x - it->trimmedBounds().x,
it->inTextureBounds().y - it->trimmedBounds().y,
it->frame());
int x = sample.inTextureBounds().x - sample.trimmedBounds().x;
int y = sample.inTextureBounds().y - sample.trimmedBounds().y;
if (sample.layer()) {
layer_render(sample.layer(), textureImage, x, y, sample.frame());
}
else {
sample.sprite()->render(textureImage, x, y, sample.frame());
}
}
}
@ -292,18 +373,19 @@ void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os,
for (Samples::const_iterator
it = samples.begin(),
end = samples.end(); it != end; ) {
gfx::Size srcSize = it->originalSize();
gfx::Rect spriteSourceBounds = it->trimmedBounds();
gfx::Rect frameBounds = it->inTextureBounds();
const Sample& sample = *it;
gfx::Size srcSize = sample.originalSize();
gfx::Rect spriteSourceBounds = sample.trimmedBounds();
gfx::Rect frameBounds = sample.inTextureBounds();
os << " \"" << it->filename() << "\": {\n"
os << " \"" << sample.filename() << "\": {\n"
<< " \"frame\": { "
<< "\"x\": " << frameBounds.x << ", "
<< "\"y\": " << frameBounds.y << ", "
<< "\"w\": " << frameBounds.w << ", "
<< "\"h\": " << frameBounds.h << " },\n"
<< " \"rotated\": false,\n"
<< " \"trimmed\": " << (it->trimmed() ? "true": "false") << ",\n"
<< " \"trimmed\": " << (sample.trimmed() ? "true": "false") << ",\n"
<< " \"spriteSourceSize\": { "
<< "\"x\": " << spriteSourceBounds.x << ", "
<< "\"y\": " << spriteSourceBounds.y << ", "
@ -312,7 +394,7 @@ void DocumentExporter::createDataFile(const Samples& samples, std::ostream& os,
<< " \"sourceSize\": { "
<< "\"w\": " << srcSize.w << ", "
<< "\"h\": " << srcSize.h << " },\n"
<< " \"duration\": " << it->sprite()->getFrameDuration(it->frame()) << "\n"
<< " \"duration\": " << sample.sprite()->getFrameDuration(sample.frame()) << "\n"
<< " }";
if (++it != samples.end())

View File

@ -1,5 +1,5 @@
/* Aseprite
* Copyright (C) 2001-2013 David Capello
* Copyright (C) 2001-2014 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -29,6 +29,7 @@
namespace doc {
class Image;
class Layer;
}
namespace app {
@ -50,11 +51,7 @@ namespace app {
DefaultScaleMode
};
DocumentExporter() :
m_dataFormat(DefaultDataFormat),
m_textureFormat(DefaultTextureFormat),
m_scaleMode(DefaultScaleMode) {
}
DocumentExporter();
void setDataFormat(DataFormat format) {
m_dataFormat = format;
@ -72,6 +69,18 @@ namespace app {
m_textureFilename = filename;
}
void setTextureWidth(int width) {
m_textureWidth = width;
}
void setTextureHeight(int height) {
m_textureHeight = height;
}
void setTexturePack(bool state) {
m_texturePack = state;
}
void setScale(double scale) {
m_scale = scale;
}
@ -80,8 +89,8 @@ namespace app {
m_scaleMode = mode;
}
void addDocument(Document* document) {
m_documents.push_back(document);
void addDocument(Document* document, doc::Layer* layer = NULL) {
m_documents.push_back(Item(document, layer));
}
void exportSheet();
@ -91,19 +100,33 @@ namespace app {
class Samples;
class LayoutSamples;
class SimpleLayoutSamples;
class BestFitLayoutSamples;
void captureSamples(Samples& samples);
Document* createEmptyTexture(const Samples& samples);
void renderTexture(const Samples& samples, doc::Image* textureImage);
void createDataFile(const Samples& samples, std::ostream& os, doc::Image* textureImage);
class Item {
public:
Document* doc;
doc::Layer* layer;
Item(Document* doc, doc::Layer* layer)
: doc(doc), layer(layer) {
}
};
typedef std::vector<Item> Items;
DataFormat m_dataFormat;
std::string m_dataFilename;
TextureFormat m_textureFormat;
std::string m_textureFilename;
int m_textureWidth;
int m_textureHeight;
bool m_texturePack;
double m_scale;
ScaleMode m_scaleMode;
std::vector<Document*> m_documents;
Items m_documents;
DISABLE_COPYING(DocumentExporter);
};

View File

@ -22,6 +22,7 @@
#include "app/job.h"
#include "app/app.h"
#include "app/ui/status_bar.h"
#include "base/mutex.h"
#include "base/scoped_lock.h"
@ -34,7 +35,7 @@ static const int kMonitoringPeriod = 100;
namespace app {
Job::Job(const char* job_name)
Job::Job(const char* jobName)
{
m_mutex = NULL;
m_thread = NULL;
@ -44,24 +45,29 @@ Job::Job(const char* job_name)
m_canceled_flag = false;
m_mutex = new base::mutex();
m_progress = StatusBar::instance()->addProgress();
m_alert_window = ui::Alert::create("%s<<Working...||&Cancel", job_name);
m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window));
m_timer->Tick.connect(&Job::onMonitoringTick, this);
m_timer->start();
if (App::instance()->isGui()) {
m_progress = StatusBar::instance()->addProgress();
m_alert_window = ui::Alert::create("%s<<Working...||&Cancel", jobName);
m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window));
m_timer->Tick.connect(&Job::onMonitoringTick, this);
m_timer->start();
}
}
Job::~Job()
{
ASSERT(!m_timer->isRunning());
ASSERT(m_thread == NULL);
if (App::instance()->isGui()) {
ASSERT(!m_timer->isRunning());
ASSERT(m_thread == NULL);
if (m_alert_window != NULL)
m_alert_window->closeWindow(NULL);
if (m_alert_window != NULL)
m_alert_window->closeWindow(NULL);
if (m_progress)
delete m_progress;
if (m_progress)
delete m_progress;
}
if (m_mutex)
delete m_mutex;
@ -70,19 +76,22 @@ Job::~Job()
void Job::startJob()
{
m_thread = new base::thread(&Job::thread_proc, this);
m_alert_window->openWindowInForeground();
// The job was canceled by the user?
{
base::scoped_lock hold(*m_mutex);
if (!m_done_flag)
m_canceled_flag = true;
if (m_alert_window) {
m_alert_window->openWindowInForeground();
// The job was canceled by the user?
{
base::scoped_lock hold(*m_mutex);
if (!m_done_flag)
m_canceled_flag = true;
}
}
}
void Job::waitJob()
{
if (m_timer->isRunning())
if (m_timer && m_timer->isRunning())
m_timer->stop();
if (m_thread) {

View File

@ -34,7 +34,7 @@ namespace app {
class Job {
public:
Job(const char* job_name);
Job(const char* jobName);
virtual ~Job();
// Starts the job calling onJob() event in another thread and

View File

@ -220,7 +220,8 @@ void update_screen_for_document(Document* document)
// Well, change to the default palette.
if (set_current_palette(NULL, false)) {
// If the palette changes, refresh the whole screen.
Manager::getDefault()->invalidate();
if (Manager::getDefault())
Manager::getDefault()->invalidate();
}
}
// With a document.

View File

@ -1301,7 +1301,7 @@ void Editor::setZoomAndCenterInMouse(int zoom, int mouse_x, int mouse_y, ZoomBeh
switch (zoomBehavior) {
case kCofiguredZoomBehavior:
centerMouse = get_config_bool("Editor", "CenterMouseInZoom", true);
centerMouse = get_config_bool("Editor", "CenterOnZoom", false);
break;
case kCenterOnZoom:
centerMouse = true;

View File

@ -1246,7 +1246,7 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev)
{
int scale = jguiscale();
Graphics* g = ev.getGraphics();
AppMenuItem* widget = static_cast<AppMenuItem*>(ev.getSource());
MenuItem* widget = static_cast<MenuItem*>(ev.getSource());
gfx::Rect bounds = widget->getClientBounds();
gfx::Color fg, bg;
int c, bar;
@ -1328,17 +1328,19 @@ void SkinTheme::paintMenuItem(ui::PaintEvent& ev)
}
}
// Draw the keyboard shortcut
else if (widget->getKey() && !widget->getKey()->accels().empty()) {
int old_align = widget->getAlign();
else if (AppMenuItem* appMenuItem = dynamic_cast<AppMenuItem*>(widget)) {
if (appMenuItem->getKey() && !appMenuItem->getKey()->accels().empty()) {
int old_align = appMenuItem->getAlign();
pos = bounds;
pos.w -= widget->child_spacing/4;
pos = bounds;
pos.w -= widget->child_spacing/4;
std::string buf = widget->getKey()->accels().front().toString();
std::string buf = appMenuItem->getKey()->accels().front().toString();
widget->setAlign(JI_RIGHT | JI_MIDDLE);
drawTextString(g, buf.c_str(), fg, ColorNone, widget, pos, 0);
widget->setAlign(old_align);
widget->setAlign(JI_RIGHT | JI_MIDDLE);
drawTextString(g, buf.c_str(), fg, ColorNone, widget, pos, 0);
widget->setAlign(old_align);
}
}
}
}

View File

@ -22,6 +22,7 @@
#include "app/app.h"
#include "app/document.h"
#include "app/document_location.h"
#include "app/modules/editors.h"
#include "app/settings/ui_settings_impl.h"
#include "app/ui/color_bar.h"
@ -64,8 +65,16 @@ UIContext::~UIContext()
ASSERT(documents().empty());
}
bool UIContext::isUiAvailable() const
{
return App::instance()->isGui();
}
DocumentView* UIContext::activeView() const
{
if (!isUiAvailable())
return NULL;
Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
WorkspaceView* view = workspace->activeView();
if (DocumentView* docView = dynamic_cast<DocumentView*>(view))
@ -156,6 +165,10 @@ void UIContext::onRemoveDocument(doc::Document* doc)
{
Context::onRemoveDocument(doc);
// We don't destroy views in batch mode.
if (!isUiAvailable())
return;
Workspace* workspace = App::instance()->getMainWindow()->getWorkspace();
DocumentViews docViews;
@ -179,8 +192,16 @@ void UIContext::onRemoveDocument(doc::Document* doc)
void UIContext::onGetActiveLocation(DocumentLocation* location) const
{
DocumentView* view = activeView();
if (view)
if (view) {
view->getDocumentLocation(location);
}
// Default/dummy location (maybe for batch/command line mode)
else if (Document* doc = activeDocument()) {
location->document(doc);
location->sprite(doc->sprite());
location->layer(doc->sprite()->indexToLayer(LayerIndex(0)));
location->frame(FrameNumber(0));
}
}
} // namespace app

View File

@ -36,7 +36,7 @@ namespace app {
UIContext();
virtual ~UIContext();
virtual bool isUiAvailable() const { return true; }
bool isUiAvailable() const override;
DocumentView* activeView() const;
void setActiveView(DocumentView* documentView);

View File

@ -85,7 +85,7 @@ void ProgramOptions::parse(int argc, const char* argv[])
}
Option* option = *it;
option->setEnabled(true);
std::string optionValue;
if (option->doesRequireValue()) {
if (usedBy != 0) {
@ -104,9 +104,11 @@ void ProgramOptions::parse(int argc, const char* argv[])
}
// Set the value specified for this argument
option->setValue(argv[++i]);
optionValue = argv[++i];
usedBy = option->mnemonic();
}
m_values.push_back(Value(option, optionValue));
}
}
// Use name
@ -133,7 +135,6 @@ void ProgramOptions::parse(int argc, const char* argv[])
}
Option* option = *it;
option->setEnabled(true);
if (option->doesRequireValue()) {
// If the option was specified without '=', we can get the
@ -147,15 +148,14 @@ void ProgramOptions::parse(int argc, const char* argv[])
}
optionValue = argv[++i];
}
// Set the value specified for this argument
option->setValue(optionValue);
}
m_values.push_back(Value(option, optionValue));
}
}
// Add values
// Add values without a related option.
else {
m_values.push_back(arg);
m_values.push_back(Value(NULL, arg));
}
}
}
@ -163,14 +163,24 @@ void ProgramOptions::parse(int argc, const char* argv[])
void ProgramOptions::reset()
{
m_values.clear();
for_each(m_options.begin(), m_options.end(), &ProgramOptions::resetOption);
}
// static
void ProgramOptions::resetOption(Option* option)
bool ProgramOptions::enabled(const Option& option) const
{
option->setEnabled(false);
option->setValue("");
for (const auto& value : m_values) {
if (value.option() == &option)
return true;
}
return false;
}
std::string ProgramOptions::value_of(const Option& option) const
{
for (const auto& value : m_values) {
if (value.option() == &option)
return value.value();
}
return "";
}
} // namespace base
@ -182,8 +192,8 @@ std::ostream& operator<<(std::ostream& os, const base::ProgramOptions& po)
it=po.options().begin(), end=po.options().end(); it != end; ++it) {
const base::ProgramOptions::Option* option = *it;
size_t optionWidth =
std::min<int>(26, 6+option->name().size()+1+
(option->doesRequireValue() ? option->getValueName().size()+1: 0));
6+option->name().size()+1+
(option->doesRequireValue() ? option->getValueName().size()+1: 0);
if (maxOptionWidth < optionWidth)
maxOptionWidth = optionWidth;
@ -207,7 +217,8 @@ std::ostream& operator<<(std::ostream& os, const base::ProgramOptions& po)
bool multilines = (option->description().find('\n') != std::string::npos);
if (!multilines) {
os << std::setw(maxOptionWidth - optionWidth + 1) << ' ' << option->description();
os << std::setw(maxOptionWidth - optionWidth + 1) << ' ' << option->description()
<< "\n";
}
else {
std::istringstream s(option->description());
@ -220,7 +231,8 @@ std::ostream& operator<<(std::ostream& os, const base::ProgramOptions& po)
}
}
}
os << "\n";
else
os << "\n";
}
return os;

View File

@ -39,16 +39,13 @@ namespace base {
public:
Option(const std::string& name)
: m_name(name)
, m_mnemonic(0)
, m_enabled(false) {
, m_mnemonic(0) {
}
// Getters
const std::string& name() const { return m_name; }
const std::string& description() const { return m_description; }
const std::string& value() const { return m_value; }
const std::string& getValueName() const { return m_valueName; }
char mnemonic() const { return m_mnemonic; }
bool enabled() const { return m_enabled; }
bool doesRequireValue() const { return !m_valueName.empty(); }
// Setters
Option& description(const std::string& desc) { m_description = desc; return *this; }
@ -58,21 +55,29 @@ namespace base {
return *this;
}
private:
void setValue(const std::string& value) { m_value = value; }
void setEnabled(bool enabled) { m_enabled = enabled; }
std::string m_name; // Name of the option (e.g. "help" for "--help")
std::string m_description; // Description of the option (this can be used when the help is printed).
std::string m_value; // The value specified by the user in the command line.
std::string m_valueName; // Empty if this option doesn't require a value, or the name of the expected value.
char m_mnemonic; // One character that can be used in the command line to use this option.
bool m_enabled; // True if the user specified this argument.
friend class ProgramOptions;
};
class Value {
public:
Value(Option* option, const std::string& value)
: m_option(option)
, m_value(value) {
}
const Option* option() const { return m_option; }
const std::string& value() const { return m_value; }
private:
Option* m_option;
std::string m_value;
};
typedef std::vector<Option*> OptionList;
typedef std::vector<std::string> ValueList;
typedef std::vector<Value> ValueList;
ProgramOptions();
@ -88,23 +93,19 @@ namespace base {
// Detects which options where specified in the command line.
void parse(int argc, const char* argv[]);
// Reset all options values/flags.
// Reset all option values/flags.
void reset();
// Returns the list of available options. To know the list of
// specified options you can iterate this list asking for
// Option::enabled() flag to know if the option was specified by
// the user in the command line.
// Returns the list of available options for the user.
const OptionList& options() const { return m_options; }
// Returns the list of values that are not associated to any
// options. E.g. a list of files specified in the command line to
// be opened.
// List of specified options/values in the command line.
const ValueList& values() const { return m_values; }
private:
static void resetOption(Option* option);
bool enabled(const Option& option) const;
std::string value_of(const Option& option) const;
private:
OptionList m_options;
ValueList m_values;
};

View File

@ -21,13 +21,13 @@ TEST(ProgramOptions, OptionMembers)
EXPECT_EQ("help", help.name());
EXPECT_EQ("Show the help", help.description());
EXPECT_EQ('h', help.mnemonic());
EXPECT_FALSE(help.enabled());
EXPECT_FALSE(po.enabled(help));
EXPECT_FALSE(help.doesRequireValue());
EXPECT_EQ("output", output.name());
EXPECT_EQ("", output.description());
EXPECT_EQ('O', output.mnemonic());
EXPECT_FALSE(output.enabled());
EXPECT_FALSE(po.enabled(output));
EXPECT_TRUE(output.doesRequireValue());
}
@ -36,20 +36,20 @@ TEST(ProgramOptions, Reset)
ProgramOptions po;
ProgramOptions::Option& help = po.add("help");
ProgramOptions::Option& file = po.add("file").requiresValue("FILE");
EXPECT_FALSE(help.enabled());
EXPECT_FALSE(file.enabled());
EXPECT_EQ("", file.value());
EXPECT_FALSE(po.enabled(help));
EXPECT_FALSE(po.enabled(file));
EXPECT_EQ("", po.value_of(file));
const char* argv[] = { "program.exe", "--help", "--file=readme.txt" };
po.parse(3, argv);
EXPECT_TRUE(help.enabled());
EXPECT_TRUE(file.enabled());
EXPECT_EQ("readme.txt", file.value());
EXPECT_TRUE(po.enabled(help));
EXPECT_TRUE(po.enabled(file));
EXPECT_EQ("readme.txt", po.value_of(file));
po.reset();
EXPECT_FALSE(help.enabled());
EXPECT_FALSE(file.enabled());
EXPECT_EQ("", file.value());
EXPECT_FALSE(po.enabled(help));
EXPECT_FALSE(po.enabled(file));
EXPECT_EQ("", po.value_of(file));
}
TEST(ProgramOptions, Parse)
@ -61,50 +61,59 @@ TEST(ProgramOptions, Parse)
const char* argv1[] = { "program.exe", "-?" };
po.parse(2, argv1);
EXPECT_TRUE(help.enabled());
EXPECT_TRUE(po.enabled(help));
const char* argv2[] = { "program.exe", "--help" };
po.reset();
po.parse(2, argv2);
EXPECT_TRUE(help.enabled());
EXPECT_TRUE(po.enabled(help));
const char* argv3[] = { "program.exe", "--input", "hello.cpp", "--output", "hello.exe" };
po.reset();
po.parse(5, argv3);
EXPECT_FALSE(help.enabled());
EXPECT_TRUE(input.enabled());
EXPECT_TRUE(output.enabled());
EXPECT_EQ("hello.cpp", input.value());
EXPECT_EQ("hello.exe", output.value());
EXPECT_FALSE(po.enabled(help));
EXPECT_TRUE(po.enabled(input));
EXPECT_TRUE(po.enabled(output));
EXPECT_EQ("hello.cpp", po.value_of(input));
EXPECT_EQ("hello.exe", po.value_of(output));
const char* argv4[] = { "program.exe", "--input=hi.c", "--output=out.exe" };
po.reset();
po.parse(3, argv4);
EXPECT_FALSE(help.enabled());
EXPECT_TRUE(input.enabled());
EXPECT_TRUE(output.enabled());
EXPECT_EQ("hi.c", input.value());
EXPECT_EQ("out.exe", output.value());
EXPECT_FALSE(po.enabled(help));
EXPECT_TRUE(po.enabled(input));
EXPECT_TRUE(po.enabled(output));
EXPECT_EQ("hi.c", po.value_of(input));
EXPECT_EQ("out.exe", po.value_of(output));
const char* argv5[] = { "program.exe", "-?i", "input.md", "-o", "output.html", "extra-file.txt" };
po.reset();
po.parse(6, argv5);
EXPECT_TRUE(help.enabled());
EXPECT_TRUE(input.enabled());
EXPECT_TRUE(output.enabled());
EXPECT_EQ("input.md", input.value());
EXPECT_EQ("output.html", output.value());
ASSERT_EQ(1, po.values().size());
EXPECT_EQ("extra-file.txt", po.values()[0]);
EXPECT_TRUE(po.enabled(help));
EXPECT_TRUE(po.enabled(input));
EXPECT_TRUE(po.enabled(output));
EXPECT_EQ("input.md", po.value_of(input));
EXPECT_EQ("output.html", po.value_of(output));
ASSERT_EQ(4, po.values().size());
EXPECT_EQ(&help, po.values()[0].option());
EXPECT_EQ(&input, po.values()[1].option());
EXPECT_EQ(&output, po.values()[2].option());
EXPECT_EQ(NULL, po.values()[3].option());
EXPECT_EQ("", po.values()[0].value());
EXPECT_EQ("input.md", po.values()[1].value());
EXPECT_EQ("output.html", po.values()[2].value());
EXPECT_EQ("extra-file.txt", po.values()[3].value());
const char* argv6[] = { "program.exe", "value1", "value2", "-o", "output", "value3", "--input=input", "value4" };
po.reset();
po.parse(8, argv6);
ASSERT_EQ(4, po.values().size());
EXPECT_EQ("value1", po.values()[0]);
EXPECT_EQ("value2", po.values()[1]);
EXPECT_EQ("value3", po.values()[2]);
EXPECT_EQ("value4", po.values()[3]);
ASSERT_EQ(6, po.values().size());
EXPECT_EQ("value1", po.values()[0].value());
EXPECT_EQ("value2", po.values()[1].value());
EXPECT_EQ("output", po.values()[2].value());
EXPECT_EQ("value3", po.values()[3].value());
EXPECT_EQ("input", po.values()[4].value());
EXPECT_EQ("value4", po.values()[5].value());
}
TEST(ProgramOptions, ParseErrors)
@ -125,19 +134,19 @@ TEST(ProgramOptions, ParseErrors)
const char* argv4[] = { "program.exe", "-?a" };
po.reset();
EXPECT_FALSE(help.enabled());
EXPECT_FALSE(po.enabled(help));
EXPECT_THROW(po.parse(2, argv4), InvalidProgramOption);
EXPECT_TRUE(help.enabled()); // -? is parsed anyway, -a is the invalid option
EXPECT_TRUE(po.enabled(help)); // -? is parsed anyway, -a is the invalid option
const char* argv5[] = { "program.exe", "-io", "input-and-output.txt" };
po.reset();
EXPECT_THROW(po.parse(2, argv5), ProgramOptionNeedsValue);
po.reset();
EXPECT_THROW(po.parse(3, argv5), InvalidProgramOptionsCombination);
EXPECT_TRUE(input.enabled());
EXPECT_TRUE(output.enabled());
EXPECT_EQ("input-and-output.txt", input.value());
EXPECT_EQ("", output.value());
EXPECT_TRUE(po.enabled(input));
EXPECT_FALSE(po.enabled(output));
EXPECT_EQ("input-and-output.txt", po.value_of(input));
EXPECT_EQ("", po.value_of(output));
}
int main(int argc, char** argv)

View File

@ -35,6 +35,7 @@ namespace doc {
Document* front() const { return m_docs.front(); }
Document* back() const { return m_docs.back(); }
Document* lastAdded() const { return front(); }
int size() const { return m_docs.size(); }
bool empty() const { return m_docs.empty(); }

View File

@ -18,23 +18,24 @@ typedef base::ProgramOptions PO;
static void run(int argc, const char* argv[])
{
PO po;
PO::Option& inputFn = po.add("input").requiresValue("<filename>");
PO::Option& inputOpt = po.add("input").requiresValue("<filename>");
PO::Option& widgetId = po.add("widgetid").requiresValue("<filename>");
po.parse(argc, argv);
// Try to load the XML file
TiXmlDocument* doc = NULL;
if (inputFn.enabled()) {
base::FileHandle inputFile(base::open_file(inputFn.value(), "rb"));
std::string inputFilename = po.value_of(inputOpt);
if (!inputFilename.empty()) {
base::FileHandle inputFile(base::open_file(inputFilename, "rb"));
doc = new TiXmlDocument();
doc->SetValue(inputFn.value().c_str());
doc->SetValue(inputFilename.c_str());
if (!doc->LoadFile(inputFile))
throw std::runtime_error("invalid input file");
}
if (doc && widgetId.enabled())
gen_ui_class(doc, inputFn.value(), widgetId.value());
if (doc && po.enabled(widgetId))
gen_ui_class(doc, inputFilename, po.value_of(widgetId));
}
int main(int argc, const char* argv[])

View File

@ -1,8 +1,9 @@
# ASEPRITE
# Aseprite
# Copyright (C) 2001-2014 David Capello
add_library(gfx-lib
hsv.cpp
packing_rects.cpp
region.cpp
rgb.cpp
transformation.cpp)

98
src/gfx/packing_rects.cpp Normal file
View File

@ -0,0 +1,98 @@
// Aseprite Gfx Library
// Copyright (C) 2001-2014 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 "gfx/packing_rects.h"
#include "gfx/region.h"
#include "gfx/size.h"
namespace gfx {
void PackingRects::add(const Size& sz)
{
m_rects.push_back(Rect(sz));
}
void PackingRects::add(const Rect& rc)
{
m_rects.push_back(rc);
}
Size PackingRects::bestFit()
{
Size size(0, 0);
// Calculate the amount of pixels that we need, the texture cannot
// be smaller than that.
int neededArea = 0;
for (const auto& rc : m_rects) {
neededArea += rc.w * rc.h;
}
int w = 1;
int h = 1;
int z = 0;
bool fit = false;
while (true) {
if (w*h >= neededArea) {
fit = pack(Size(w, h));
if (fit) {
size = Size(w, h);
break;
}
}
if ((++z) & 1)
w *= 2;
else
h *= 2;
}
return size;
}
static bool by_area(const Rect* a, const Rect* b) {
return a->w*a->h > b->w*b->h;
}
bool PackingRects::pack(const Size& size)
{
m_bounds = Rect(size);
// We cannot sort m_rects because we want to
std::vector<Rect*> rectPtrs(m_rects.size());
int i = 0;
for (auto& rc : m_rects)
rectPtrs[i++] = &rc;
std::sort(rectPtrs.begin(), rectPtrs.end(), by_area);
gfx::Region rgn(m_bounds);
for (auto rcPtr : rectPtrs) {
gfx::Rect& rc = *rcPtr;
for (int v=0; v<=m_bounds.h-rc.h; ++v) {
for (int u=0; u<=m_bounds.w-rc.w; ++u) {
gfx::Rect possible(u, v, rc.w, rc.h);
Region::Overlap overlap = rgn.contains(possible);
if (overlap == Region::In) {
rc = possible;
rgn.createSubtraction(rgn, gfx::Region(rc));
goto next_rc;
}
}
}
return false; // There is not enough room for "rc"
next_rc:;
}
return true;
}
} // namespace gfx

53
src/gfx/packing_rects.h Normal file
View File

@ -0,0 +1,53 @@
// Aseprite Gfx Library
// Copyright (C) 2001-2014 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef GFX_TEXTURE_SIZE_H_INCLUDED
#define GFX_TEXTURE_SIZE_H_INCLUDED
#pragma once
#include "gfx/fwd.h"
#include "gfx/rect.h"
#include <vector>
namespace gfx {
// TODO add support for rotations
class PackingRects {
public:
typedef std::vector<Rect> Rects;
typedef Rects::const_iterator const_iterator;
// Iterate over all given rectangles (in the same order they where
// given in addSize() calls).
const_iterator begin() const { return m_rects.begin(); }
const_iterator end() const { return m_rects.end(); }
size_t size() const { return m_rects.size(); }
const Rect& operator[](int i) const { return m_rects[i]; }
// Adds a new rectangle.
void add(const Size& sz);
void add(const Rect& rc);
// Returns the best size for the texture.
Size bestFit();
// Rearrange all given rectangles to best fit a texture size.
// Returns true if all rectangles were correctly arranged or false
// if there is not enough space.
bool pack(const Size& size);
// Returns the bounds of the packed area.
const Rect& bounds() const { return m_bounds; }
private:
Rect m_bounds;
Rects m_rects;
};
} // namespace gfx
#endif

View File

@ -0,0 +1,96 @@
// Aseprite Gfx Library
// Copyright (C) 2001-2014 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#include <gtest/gtest.h>
#include "gfx/packing_rects.h"
#include "gfx/rect_io.h"
#include "gfx/size.h"
using namespace gfx;
TEST(PackingRects, Simple)
{
PackingRects pr;
pr.add(Size(256, 128));
EXPECT_FALSE(pr.pack(Size(256, 120)));
EXPECT_TRUE(pr.pack(Size(256, 128)));
EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]);
EXPECT_EQ(Rect(0, 0, 256, 128), pr.bounds());
}
TEST(PackingRects, SimpleTwoRects)
{
PackingRects pr;
pr.add(Size(256, 128));
pr.add(Size(256, 120));
EXPECT_TRUE(pr.pack(Size(256, 256)));
EXPECT_EQ(Rect(0, 0, 256, 256), pr.bounds());
EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]);
EXPECT_EQ(Rect(0, 128, 256, 120), pr[1]);
}
TEST(PackingRects, BestFit)
{
PackingRects pr;
pr.add(Size(10, 12));
pr.bestFit();
EXPECT_EQ(Rect(0, 0, 16, 16), pr.bounds());
}
TEST(PackingRects, BestFitTwoRects)
{
PackingRects pr;
pr.add(Size(256, 128));
pr.add(Size(256, 127));
pr.bestFit();
EXPECT_EQ(Rect(0, 0, 256, 256), pr.bounds());
EXPECT_EQ(Rect(0, 0, 256, 128), pr[0]);
EXPECT_EQ(Rect(0, 128, 256, 127), pr[1]);
}
TEST(PackingRects, BestFit6Frames100x100)
{
PackingRects pr;
pr.add(Size(100, 100));
pr.add(Size(100, 100));
pr.add(Size(100, 100));
pr.add(Size(100, 100));
pr.add(Size(100, 100));
pr.add(Size(100, 100));
pr.bestFit();
EXPECT_EQ(Rect(0, 0, 512, 256), pr.bounds());
EXPECT_EQ(Rect(0, 0, 100, 100), pr[0]);
EXPECT_EQ(Rect(100, 0, 100, 100), pr[1]);
EXPECT_EQ(Rect(200, 0, 100, 100), pr[2]);
EXPECT_EQ(Rect(300, 0, 100, 100), pr[3]);
EXPECT_EQ(Rect(400, 0, 100, 100), pr[4]);
EXPECT_EQ(Rect(0, 100, 100, 100), pr[5]);
}
TEST(PackingRects, KeepSameRectsOrder)
{
PackingRects pr;
pr.add(Size(10, 10));
pr.add(Size(20, 20));
pr.add(Size(30, 30));
pr.bestFit();
EXPECT_EQ(Rect(0, 0, 64, 32), pr.bounds());
EXPECT_EQ(Rect(50, 0, 10, 10), pr[0]);
EXPECT_EQ(Rect(30, 0, 20, 20), pr[1]);
EXPECT_EQ(Rect(0, 0, 30, 30), pr[2]);
}
int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

27
src/gfx/rect_io.h Normal file
View File

@ -0,0 +1,27 @@
// Aseprite Gfx Library
// Copyright (C) 2001-2014 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef GFX_RECT_IO_H_INCLUDED
#define GFX_RECT_IO_H_INCLUDED
#pragma once
#include "gfx/rect.h"
#include <iosfwd>
namespace gfx {
std::ostream& operator<<(std::ostream& os, const Rect& rect)
{
return os << "("
<< rect.x << ", "
<< rect.y << ", "
<< rect.w << ", "
<< rect.h << ")";
}
}
#endif

View File

@ -6,26 +6,14 @@
#include <gtest/gtest.h>
#include "gfx/rect.h"
#include "gfx/size.h"
#include "gfx/border.h"
#include "gfx/rect.h"
#include "gfx/rect_io.h"
#include "gfx/size.h"
using namespace std;
using namespace gfx;
namespace gfx {
ostream& operator<<(ostream& os, const Rect& rect)
{
return os << "("
<< rect.x << ", "
<< rect.y << ", "
<< rect.w << ", "
<< rect.h << ")";
}
}
TEST(Rect, Ctor)
{
EXPECT_EQ(Rect(0, 0, 0, 0), Rect());

View File

@ -154,6 +154,11 @@ bool Region::contains(const PointT<int>& pt) const
Region::Overlap Region::contains(const Rect& rect) const
{
static_assert(
Out == PIXMAN_REGION_OUT &&
In == PIXMAN_REGION_IN &&
Part == PIXMAN_REGION_PART, "Pixman constants have changed");
pixman_box32 box = { rect.x, rect.y, rect.x2(), rect.y2() };
return (Region::Overlap)pixman_region32_contains_rectangle(&m_region, &box);
}

View File

@ -70,7 +70,7 @@ int app_main(int argc, char* argv[])
she::ScopedHandle<she::System> system(she::create_system());
MemLeak memleak;
ui::GuiSystem guiSystem;
app::App app(argc, const_cast<const char**>(argv));
app::App app;
// Change the name of the memory dump file
{
@ -79,7 +79,9 @@ int app_main(int argc, char* argv[])
memoryDump.setFileName(filename);
}
return app.run();
app.initialize(argc, const_cast<const char**>(argv));
app.run();
return 0;
}
catch (std::exception& e) {
std::cerr << e.what() << '\n';

View File

@ -75,32 +75,40 @@ Accelerator::Accelerator(const std::string& str)
// Scancode
// Word with one character
else if (tok.size() == 1) {
if ((tok[0] >= 'a') && (tok[0] <= 'z')) {
m_unicodeChar = tok[0];
}
else {
m_unicodeChar = tok[0];
else if (base::utf8_length(tok) == 1) {
std::wstring wstr = base::from_utf8(tok);
if (wstr.size() != 1) {
ASSERT(false && "Something wrong converting utf-8 to wchar string");
continue;
}
if ((tok[0] >= 'a') && (tok[0] <= 'z'))
m_scancode = (KeyScancode)((int)kKeyA + std::tolower(tok[0]) - 'a');
else if ((tok[0] >= '0') && (tok[0] <= '9'))
m_scancode = (KeyScancode)((int)kKey0 + tok[0] - '0');
wchar_t wchr = wstr[0];
wchr = tolower(wchr);
if ((wchr >= 'a') && (wchr <= 'z')) {
m_unicodeChar = wchr;
m_scancode = (KeyScancode)((int)kKeyA + wchr - 'a');
}
else {
switch (tok[0]) {
case '~': m_scancode = kKeyTilde; break;
case '-': m_scancode = kKeyMinus; break;
case '=': m_scancode = kKeyEquals; break;
case '[': m_scancode = kKeyOpenbrace; break;
case ']': m_scancode = kKeyClosebrace; break;
case ';': m_scancode = kKeyColon; break;
case '\'': m_scancode = kKeyQuote; break;
case '\\': m_scancode = kKeyBackslash; break;
case ',': m_scancode = kKeyComma; break;
case '.': m_scancode = kKeyStop; break;
case '/': m_scancode = kKeySlash; break;
case '*': m_scancode = kKeyAsterisk; break;
m_unicodeChar = wchr;
if ((wchr >= '0') && (wchr <= '9'))
m_scancode = (KeyScancode)((int)kKey0 + wchr - '0');
else {
switch (wchr) {
case '~': m_scancode = kKeyTilde; break;
case '-': m_scancode = kKeyMinus; break;
case '=': m_scancode = kKeyEquals; break;
case '[': m_scancode = kKeyOpenbrace; break;
case ']': m_scancode = kKeyClosebrace; break;
case ';': m_scancode = kKeyColon; break;
case '\'': m_scancode = kKeyQuote; break;
case '\\': m_scancode = kKeyBackslash; break;
case ',': m_scancode = kKeyComma; break;
case '.': m_scancode = kKeyStop; break;
case '/': m_scancode = kKeySlash; break;
case '*': m_scancode = kKeyAsterisk; break;
}
}
}
}

View File

@ -105,7 +105,6 @@ bool TextBox::onProcessMessage(Message* msg)
case kMouseMoveMessage: {
View* view = View::getView(this);
if (view && hasCapture()) {
gfx::Rect vp = view->getViewportBounds();
gfx::Point scroll = view->getViewScroll();
gfx::Point newPos = static_cast<MouseMessage*>(msg)->position();