Merge branch 'master' into beta

This commit is contained in:
David Capello 2021-01-29 12:19:43 -03:00
commit 289a6ab864
47 changed files with 792 additions and 289 deletions

View File

@ -50,12 +50,14 @@ You can ask for help in:
## Authors ## Authors
[Igara Studio](https://www.igarastudio.com/) is developing Aseprite: Aseprite is being developed by [Igara Studio](https://www.igarastudio.com/):
* [David Capello](https://davidcapello.com/): Lead developer, fixing * [David Capello](https://davidcapello.com/): Lead developer,
issues, new features, and user support. bug fixing & new features in desktop & web, and user support.
* [Gaspar Capello](https://github.com/Gasparoken): Developer, fixing * [Gaspar Capello](https://github.com/Gasparoken): Developer,
issues and new features. bug fixing & new features in desktop, and user support.
* [Martin Capello](https://github.com/martincapello): Developer,
new store website.
## Credits ## Credits

View File

@ -1109,10 +1109,13 @@
</item> </item>
<separator /> <separator />
<item command="LoadPalette" text="@.load_palette" /> <item command="LoadPalette" text="@.load_palette" />
<item command="SavePalette" text="@.save_palette" />
<item command="LoadPalette" text="@.load_default_palette"> <item command="LoadPalette" text="@.load_default_palette">
<param name="preset" value="default" /> <param name="preset" value="default" />
</item> </item>
<item command="SavePalette" text="@.save_palette" />
<item command="SavePalette" text="@.save_palette_as_preset">
<param name="saveAsPreset" value="true" />
</item>
<item command="SavePalette" text="@.save_as_default_palette" group="palette_files"> <item command="SavePalette" text="@.save_as_default_palette" group="palette_files">
<param name="preset" value="default" /> <param name="preset" value="default" />
</item> </item>

View File

@ -364,6 +364,12 @@
<option id="bits_per_pixel" type="int" default="0" /> <option id="bits_per_pixel" type="int" default="0" />
<option id="compress" type="bool" default="true" /> <option id="compress" type="bool" default="true" />
</section> </section>
<section id="css">
<option id="show_alert" type="bool" default="true" />
<option id="pixel_scale" type="int" default="1" />
<option id="with_vars" type="bool" default="false" />
<option id="generate_html" type="bool" default="false" />
</section>
<section id="webp"> <section id="webp">
<option id="show_alert" type="bool" default="true" /> <option id="show_alert" type="bool" default="true" />
<option id="loop" type="bool" default="true" /> <option id="loop" type="bool" default="true" />

View File

@ -1386,9 +1386,10 @@ ryb_color_wheel = RYB Color Wheel
normal_map_color_wheel = Normal Map Color Wheel normal_map_color_wheel = Normal Map Color Wheel
load_palette = L&oad Palette load_palette = L&oad Palette
save_palette = S&ave Palette save_palette = S&ave Palette
save_palette_as_preset = Save Palette as Preset
load_default_palette = Load Default Palette load_default_palette = Load Default Palette
save_as_default_palette = Save as Default Palette save_as_default_palette = Save Palette as Default
create_palette_from_current_sprite = Create Palette from Current Sprite create_palette_from_current_sprite = New Palette from Sprite
[palette_size] [palette_size]
title = Palette Size title = Palette Size
@ -1541,6 +1542,12 @@ title = TGA Options
bits_per_pixel = Bits Per Pixel bits_per_pixel = Bits Per Pixel
compress = Compress compress = Compress
[css_options]
title = CSS Options
pixel_scale = Pixel Scale
with_vars = Use CSS3 Variables
generate_html = Generate Sample HTML File
[timeline_conf] [timeline_conf]
position = Position: position = Position:
left = &Left left = &Left

View File

@ -1,17 +1,19 @@
<!-- Aseprite --> <!-- Aseprite -->
<!-- Copyright (C) 2018-2020 Igara Studio S.A. --> <!-- Copyright (C) 2018-2021 Igara Studio S.A. -->
<!-- Copyright (C) 2018 David Capello --> <!-- Copyright (C) 2018 David Capello -->
<gui i18nwarnings="false"> <gui i18nwarnings="false">
<window id="about" text="About Aseprite"> <window id="about" text="About Aseprite">
<vbox> <vbox>
<label text="" id="title" /> <label text="" id="title" />
<label text="Animated sprite editor &amp;&amp; pixel art tool" /> <label text="Animated sprite editor &amp;&amp; pixel art tool" />
<separator text="Authors:" horizontal="true" /> <separator text="Developers:" horizontal="true" />
<grid columns="2"> <grid columns="2">
<link text="David Capello" url="https://twitter.com/davidcapello" /> <link text="David Capello" url="https://twitter.com/davidcapello" />
<label text="- Lead developer, graphics &amp;&amp; maintainer" /> <label text="- Lead developer, new features &amp;&amp; bug fixing in desktop &amp;&amp; web" />
<link text="Gaspar Capello" url="https://twitter.com/Gasparoken" /> <link text="Gaspar Capello" url="https://twitter.com/Gasparoken" />
<label text="- Programmer, bug fixing" /> <label text="- Developer, new features &amp;&amp; bug fixing in desktop" />
<link text="Martin Capello" url="https://twitter.com/martincapell0" />
<label text="- Developer, new store website" />
<separator text="Contributors:" horizontal="true" cell_hspan="2" /> <separator text="Contributors:" horizontal="true" cell_hspan="2" />
<link text="Ilija Melentijevic" url="https://ilkke.net/" /> <link text="Ilija Melentijevic" url="https://ilkke.net/" />
@ -20,14 +22,14 @@
<label text="- Default dark theme introduced in v1.3" /> <label text="- Default dark theme introduced in v1.3" />
<hbox cell_hspan="2"> <hbox cell_hspan="2">
<link text="Other Contributors" url="https://www.aseprite.org/contributors/" /> <link text="Other Contributors" url="https://www.aseprite.org/contributors/" />
<label text="&amp;&amp;" /> <label text="&amp;&amp;" />
<link text="Third-Party Projects" url="" id="licenses" /> <link text="Third-Party Projects" url="" id="licenses" />
</hbox> </hbox>
</grid> </grid>
<separator horizontal="true" /> <separator horizontal="true" />
<hbox> <hbox>
<label text="Copyright (C) 2001-2020" /> <label text="Copyright (C) 2001-2021" />
<link text="Igara Studio S.A." url="https://www.igarastudio.com/" /> <link text="Igara Studio S.A." url="https://www.igarastudio.com/" />
</hbox> </hbox>
<link text="https://www.aseprite.org/" url="https://www.aseprite.org/" /> <link text="https://www.aseprite.org/" url="https://www.aseprite.org/" />

View File

@ -0,0 +1,25 @@
<!-- Aseprite -->
<!-- Copyright (C) 2018 by Igara Studio S.A. -->
<gui>
<window id="css_options" text="@.title">
<grid columns="2">
<label text="@.pixel_scale" />
<expr id="pixel_scale" magnet="true" cell_align="horizontal"/>
<check text="@.with_vars" id="with_vars" cell_hspan="2" />
<check text="@.generate_html" id="generate_html" cell_hspan="2" />
<separator horizontal="true" cell_hspan="2" />
<hbox cell_hspan="2">
<check text="@general.dont_show" id="dont_show" tooltip="@general.dont_show_tooltip" />
<boxfiller />
</hbox>
<box horizontal="true" homogeneous="true" cell_hspan="2" cell_align="right">
<button text="@general.ok" closewindow="true" id="ok" magnet="true" minwidth="60" />
<button text="@general.cancel" closewindow="true" />
</box>
</grid>
</window>
</gui>

View File

@ -440,6 +440,7 @@
pref="scripts.show_run_script_alert" /> pref="scripts.show_run_script_alert" />
<hbox> <hbox>
<label text="@.image_format_alerts" /> <label text="@.image_format_alerts" />
<check id="css_options_alert" text="!css" pref="css.show_alert" />
<check id="gif_options_alert" text="!gif" pref="gif.show_alert" /> <check id="gif_options_alert" text="!gif" pref="gif.show_alert" />
<check id="jpeg_options_alert" text="!jpeg" pref="jpeg.show_alert" /> <check id="jpeg_options_alert" text="!jpeg" pref="jpeg.show_alert" />
<check id="svg_options_alert" text="!svg" pref="svg.show_alert" /> <check id="svg_options_alert" text="!svg" pref="svg.show_alert" />

View File

@ -25,7 +25,7 @@ void global_function(int arg1, int arg2,
do { do {
... ...
} while (condition); } while (condition);
switch (condition) { switch (condition) {
case 1: case 1:

View File

@ -125,6 +125,7 @@ endif()
set(file_formats set(file_formats
file/ase_format.cpp file/ase_format.cpp
file/bmp_format.cpp file/bmp_format.cpp
file/css_format.cpp
file/fli_format.cpp file/fli_format.cpp
file/gif_format.cpp file/gif_format.cpp
file/ico_format.cpp file/ico_format.cpp
@ -606,7 +607,7 @@ add_library(app-lib
tools/pick_ink.cpp tools/pick_ink.cpp
tools/point_shape.cpp tools/point_shape.cpp
tools/stroke.cpp tools/stroke.cpp
tools/symmetries.cpp tools/symmetry.cpp
tools/tool_box.cpp tools/tool_box.cpp
tools/tool_loop_manager.cpp tools/tool_loop_manager.cpp
tools/velocity.cpp tools/velocity.cpp

View File

@ -124,6 +124,7 @@ namespace app {
obs::signal<void()> Exit; obs::signal<void()> Exit;
obs::signal<void()> PaletteChange; obs::signal<void()> PaletteChange;
obs::signal<void()> ColorSpaceChange; obs::signal<void()> ColorSpaceChange;
obs::signal<void()> PalettePresetsChange;
private: private:
class CoreModules; class CoreModules;

View File

@ -992,6 +992,7 @@ private:
advancedModeAlert()->resetWithDefaultValue(); advancedModeAlert()->resetWithDefaultValue();
invalidFgBgColorAlert()->resetWithDefaultValue(); invalidFgBgColorAlert()->resetWithDefaultValue();
runScriptAlert()->resetWithDefaultValue(); runScriptAlert()->resetWithDefaultValue();
cssOptionsAlert()->resetWithDefaultValue();
gifOptionsAlert()->resetWithDefaultValue(); gifOptionsAlert()->resetWithDefaultValue();
jpegOptionsAlert()->resetWithDefaultValue(); jpegOptionsAlert()->resetWithDefaultValue();
svgOptionsAlert()->resetWithDefaultValue(); svgOptionsAlert()->resetWithDefaultValue();

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2020 Igara Studio S.A. // Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -78,7 +78,7 @@ void RemoveLayerCommand::onExecute(Context* context)
} }
} }
else { else {
if (sprite->allLayersCount() == 1) { if (sprite->root()->layersCount() == 1) {
ui::Alert::show(Strings::alerts_cannot_delete_all_layers()); ui::Alert::show(Strings::alerts_cannot_delete_all_layers());
return; return;
} }

View File

@ -8,6 +8,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/app.h"
#include "app/commands/cmd_set_palette.h" #include "app/commands/cmd_set_palette.h"
#include "app/commands/commands.h" #include "app/commands/commands.h"
#include "app/commands/params.h" #include "app/commands/params.h"
@ -35,6 +36,7 @@ protected:
private: private:
std::string m_preset; std::string m_preset;
bool m_saveAsPreset = false;
}; };
SavePaletteCommand::SavePaletteCommand() SavePaletteCommand::SavePaletteCommand()
@ -45,6 +47,7 @@ SavePaletteCommand::SavePaletteCommand()
void SavePaletteCommand::onLoadParams(const Params& params) void SavePaletteCommand::onLoadParams(const Params& params)
{ {
m_preset = params.get("preset"); m_preset = params.get("preset");
m_saveAsPreset = (params.get("saveAsPreset") == "true");
} }
void SavePaletteCommand::onExecute(Context* context) void SavePaletteCommand::onExecute(Context* context)
@ -58,8 +61,9 @@ void SavePaletteCommand::onExecute(Context* context)
else { else {
base::paths exts = get_writable_palette_extensions(); base::paths exts = get_writable_palette_extensions();
base::paths selFilename; base::paths selFilename;
std::string initialPath = (m_saveAsPreset ? get_preset_palettes_dir(): "");
if (!app::show_file_selector( if (!app::show_file_selector(
"Save Palette", "", exts, "Save Palette", initialPath, exts,
FileSelectorType::Save, selFilename)) FileSelectorType::Save, selFilename))
return; return;
@ -74,6 +78,9 @@ void SavePaletteCommand::onExecute(Context* context)
if (!context->activeDocument()) if (!context->activeDocument())
set_current_palette(palette, false); set_current_palette(palette, false);
} }
if (m_saveAsPreset) {
App::instance()->PalettePresetsChange();
}
} }
Command* CommandFactory::createSavePaletteCommand() Command* CommandFactory::createSavePaletteCommand()

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A. // Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -332,18 +332,13 @@ Doc* Session::restoreBackupRawImages(Backup* backup,
void Session::deleteBackup(Backup* backup) void Session::deleteBackup(Backup* backup)
{ {
try { auto it = std::find(m_backups.begin(), m_backups.end(), backup);
auto it = std::find(m_backups.begin(), m_backups.end(), backup); ASSERT(it != m_backups.end());
ASSERT(it != m_backups.end()); if (it != m_backups.end())
if (it != m_backups.end()) m_backups.erase(it);
m_backups.erase(it);
if (base::is_directory(backup->dir())) if (base::is_directory(backup->dir()))
deleteDirectory(backup->dir()); deleteDirectory(backup->dir());
}
catch (const std::exception& ex) {
Console::showException(ex);
}
} }
void Session::loadPid() void Session::loadPid()

View File

@ -32,6 +32,7 @@
#include "doc/palette.h" #include "doc/palette.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "doc/tag.h" #include "doc/tag.h"
#include "doc/slice.h"
#include "os/display.h" #include "os/display.h"
#include "os/system.h" #include "os/system.h"
#include "ui/system.h" #include "ui/system.h"
@ -519,6 +520,14 @@ Doc* Doc::duplicate(DuplicateType type) const
for (const Tag* tag : sourceSprite->tags()) for (const Tag* tag : sourceSprite->tags())
spriteCopy->tags().add(new Tag(*tag)); spriteCopy->tags().add(new Tag(*tag));
// Copy slices
for (const Slice *slice : sourceSprite->slices()) {
auto sliceCopy = new Slice(*slice);
spriteCopy->slices().add(sliceCopy);
ASSERT(sliceCopy->owner() == &spriteCopy->slices());
}
// Copy color palettes // Copy color palettes
{ {
PalettesList::const_iterator it = sourceSprite->getPalettes().begin(); PalettesList::const_iterator it = sourceSprite->getPalettes().begin();

303
src/app/file/css_format.cpp Normal file
View File

@ -0,0 +1,303 @@
// Aseprite
// Copyright (c) 2018-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/console.h"
#include "app/context.h"
#include "app/doc.h"
#include "app/file/file.h"
#include "app/file/file_format.h"
#include "app/file/format_options.h"
#include "app/pref/preferences.h"
#include "base/convert_to.h"
#include "base/cfile.h"
#include "base/file_handle.h"
#include "base/string.h"
#include "doc/doc.h"
#include "ui/window.h"
#include "css_options.xml.h"
namespace app {
using namespace base;
class CssFormat : public FileFormat {
class CssOptions : public FormatOptions {
public:
CssOptions() : pixelScale(1), gutterSize(0),
generateHtml(false),
withVars(false) { }
int pixelScale;
int gutterSize;
bool generateHtml;
bool withVars;
};
const char* onGetName() const override {
return "css";
}
void onGetExtensions(base::paths& exts) const override {
exts.push_back("css");
}
dio::FileFormat onGetDioFormat() const override {
return dio::FileFormat::CSS_STYLE;
}
int onGetFlags() const override {
return
FILE_SUPPORT_SAVE |
FILE_SUPPORT_RGB |
FILE_SUPPORT_RGBA |
FILE_SUPPORT_GRAY |
FILE_SUPPORT_GRAYA |
FILE_SUPPORT_INDEXED |
FILE_SUPPORT_SEQUENCES |
FILE_SUPPORT_GET_FORMAT_OPTIONS |
FILE_SUPPORT_PALETTE_WITH_ALPHA;
}
bool onLoad(FileOp* fop) override;
#ifdef ENABLE_SAVE
bool onSave(FileOp* fop) override;
#endif
FormatOptionsPtr onAskUserForFormatOptions(FileOp* fop) override;
};
FileFormat *CreateCssFormat()
{
return new CssFormat;
}
bool CssFormat::onLoad(FileOp* fop)
{
return false;
}
#ifdef ENABLE_SAVE
bool CssFormat::onSave(FileOp* fop)
{
const Image* image = fop->sequenceImage();
int x, y, c, r, g, b, a, alpha;
const auto css_options = std::static_pointer_cast<CssOptions>(fop->formatOptions());
FileHandle handle(open_file_with_exception_sync_on_close(fop->filename(), "wb"));
FILE* f = handle.get();
auto print_color = [f](int r, int g, int b, int a) {
if (a == 255) {
fprintf(f, "#%02X%02X%02X", r, g, b);
}
else {
fprintf(f, "rgba(%d, %d, %d, %d)", r, g, b, a);
}
};
auto print_shadow_color = [f, css_options, print_color](int x, int y, int r,
int g, int b, int a,
bool comma = true) {
fprintf(f, comma?",\n":"\n");
if (css_options->withVars) {
fprintf(f, "\tcalc(%d*var(--shadow-mult)) calc(%d*var(--shadow-mult)) var(--blur) var(--spread) ",
x, y);
}
else {
int x_loc = x * (css_options->pixelScale + css_options->gutterSize);
int y_loc = y * (css_options->pixelScale + css_options->gutterSize);
fprintf(f, "%dpx %dpx ", x_loc, y_loc);
}
print_color(r, g, b, a);
};
auto print_shadow_index = [f, css_options](int x, int y, int i, bool comma=true) {
fprintf(f, comma?",\n":"\n");
fprintf(f, "\tcalc(%d*var(--shadow-mult)) calc(%d*var(--shadow-mult)) var(--blur) var(--spread) var(--color-%d)",
x, y, i);
};
if (css_options->withVars) {
fprintf(f, ":root {\n"
"\t--blur: 0px;\n"
"\t--spread: 0px;\n"
"\t--pixel-size: %dpx;\n"
"\t--gutter-size: %dpx;\n",
css_options->pixelScale,
css_options->gutterSize);
fprintf(f, "\t--shadow-mult: calc(var(--gutter-size) + var(--pixel-size));\n");
if (image->pixelFormat() == IMAGE_INDEXED) {
for (y = 0; y < 256; y++) {
fop->sequenceGetColor(y, &r, &g, &b);
fop->sequenceGetAlpha(y, &a);
fprintf(f, "\t--color-%d: ", y);
print_color(r, g, b, a);
fprintf(f, ";\n");
}
}
fprintf(f, "}\n\n");
}
fprintf(f, ".pixel-art {\n");
fprintf(f, "\tposition: relative;\n");
fprintf(f, "\ttop: 0;\n");
fprintf(f, "\tleft: 0;\n");
if (css_options->withVars) {
fprintf(f, "\theight: var(--pixel-size);\n");
fprintf(f, "\twidth: var(--pixel-size);\n");
}
else {
fprintf(f, "\theight: %dpx;\n", css_options->pixelScale);
fprintf(f, "\twidth: %dpx;\n", css_options->pixelScale);
}
fprintf(f, "\tbox-shadow:\n");
int num_printed_pixels = 0;
switch (image->pixelFormat()) {
case IMAGE_RGB: {
for (y=0; y<image->height(); y++) {
for (x=0; x<image->width(); x++) {
c = get_pixel_fast<RgbTraits>(image, x, y);
alpha = rgba_geta(c);
if (alpha != 0x00) {
print_shadow_color(x, y, rgba_getr(c), rgba_getg(c), rgba_getb(c),
alpha, num_printed_pixels>0);
num_printed_pixels ++;
}
}
fop->setProgress((float)y / (float)(image->height()));
}
break;
}
case IMAGE_GRAYSCALE: {
for (y=0; y<image->height(); y++) {
for (x=0; x<image->width(); x++) {
c = get_pixel_fast<GrayscaleTraits>(image, x, y);
auto v = graya_getv(c);
alpha = graya_geta(c);
if (alpha != 0x00) {
print_shadow_color(x, y, v, v, v, alpha, num_printed_pixels>0);
num_printed_pixels ++;
}
}
fop->setProgress((float)y / (float)(image->height()));
}
break;
}
case IMAGE_INDEXED: {
unsigned char image_palette[256][4];
for (y=0; !css_options->withVars && y<256; y++) {
fop->sequenceGetColor(y, &r, &g, &b);
image_palette[y][0] = r;
image_palette[y][1] = g;
image_palette[y][2] = b;
fop->sequenceGetAlpha(y, &a);
image_palette[y][3] = a;
}
color_t mask_color = -1;
if (fop->document()->sprite()->backgroundLayer() == NULL ||
!fop->document()->sprite()->backgroundLayer()->isVisible()) {
mask_color = fop->document()->sprite()->transparentColor();
}
for (y=0; y<image->height(); y++) {
for (x=0; x<image->width(); x++) {
c = get_pixel_fast<IndexedTraits>(image, x, y);
if (c != mask_color) {
if (css_options->withVars) {
print_shadow_index(x, y, c, num_printed_pixels>0);
}
else {
print_shadow_color(x, y,
image_palette[c][0] & 0xff,
image_palette[c][1] & 0xff,
image_palette[c][2] & 0xff,
image_palette[c][3] & 0xff,
num_printed_pixels>0);
}
num_printed_pixels ++;
}
}
fop->setProgress((float)y / (float)(image->height()));
}
break;
}
}
fprintf(f, ";\n}\n");
if (ferror(f)) {
fop->setError("Error writing file.\n");
return false;
}
if (css_options->generateHtml) {
std::string html_filepath = fop->filename() + ".html";
FileHandle handle(open_file_with_exception_sync_on_close(html_filepath, "wb"));
FILE* h = handle.get();
fprintf(h,
"<html><head><link rel=\"stylesheet\" media=\"all\" "
"href=\"%s\"></head><body><div "
"class=\"pixel-art\"></div></body></html>",
fop->filename().c_str());
if (ferror(h)) {
fop->setError("Error writing html file.\n");
return false;
}
}
return true;
}
#endif
// Shows the CSS configuration dialog.
FormatOptionsPtr CssFormat::onAskUserForFormatOptions(FileOp* fop)
{
auto opts = fop->formatOptionsOfDocument<CssOptions>();
#ifdef ENABLE_UI
if (fop->context() && fop->context()->isUIAvailable()) {
try {
auto &pref = Preferences::instance();
if (pref.isSet(pref.css.pixelScale))
opts->pixelScale = pref.css.pixelScale();
if (pref.isSet(pref.css.withVars))
opts->withVars = pref.css.withVars();
if (pref.isSet(pref.css.generateHtml))
opts->generateHtml = pref.css.generateHtml();
if (pref.css.showAlert()) {
app::gen::CssOptions win;
win.pixelScale()->setTextf("%d", opts->pixelScale);
win.withVars()->setSelected(opts->withVars);
win.generateHtml()->setSelected(opts->generateHtml);
win.openWindowInForeground();
if (win.closer() == win.ok()) {
pref.css.showAlert(!win.dontShow()->isSelected());
pref.css.pixelScale((int)win.pixelScale()->textInt());
pref.css.withVars(win.withVars()->isSelected());
pref.css.generateHtml(win.generateHtml()->isSelected());
opts->generateHtml = pref.css.generateHtml();
opts->withVars = pref.css.withVars();
opts->pixelScale = pref.css.pixelScale();
}
else {
opts.reset();
}
}
}
catch (std::exception &e) {
Console::showException(e);
return std::shared_ptr<CssOptions>(nullptr);
}
}
#endif
return opts;
}
} // namespace app

View File

@ -22,6 +22,7 @@ namespace app {
extern FileFormat* CreateAseFormat(); extern FileFormat* CreateAseFormat();
extern FileFormat* CreateBmpFormat(); extern FileFormat* CreateBmpFormat();
extern FileFormat* CreateCssFormat();
extern FileFormat* CreateFliFormat(); extern FileFormat* CreateFliFormat();
extern FileFormat* CreateGifFormat(); extern FileFormat* CreateGifFormat();
extern FileFormat* CreateIcoFormat(); extern FileFormat* CreateIcoFormat();
@ -57,6 +58,7 @@ FileFormatsManager::FileFormatsManager()
// The first format is the default image format in FileSelector // The first format is the default image format in FileSelector
registerFormat(CreateAseFormat()); registerFormat(CreateAseFormat());
registerFormat(CreateBmpFormat()); registerFormat(CreateBmpFormat());
registerFormat(CreateCssFormat());
registerFormat(CreateFliFormat()); registerFormat(CreateFliFormat());
registerFormat(CreateGifFormat()); registerFormat(CreateGifFormat());
registerFormat(CreateIcoFormat()); registerFormat(CreateIcoFormat());

View File

@ -73,7 +73,10 @@ FileFormat* CreateSvgFormat()
return new SvgFormat; return new SvgFormat;
} }
bool SvgFormat::onLoad(FileOp* fop) { return false;} bool SvgFormat::onLoad(FileOp* fop)
{
return false;
}
#ifdef ENABLE_SAVE #ifdef ENABLE_SAVE

View File

@ -161,12 +161,7 @@ bool set_current_palette(const Palette *_palette, bool forced)
std::string get_preset_palette_filename(const std::string& preset, std::string get_preset_palette_filename(const std::string& preset,
const std::string& dot_extension) const std::string& dot_extension)
{ {
ResourceFinder rf; std::string palettesDir = get_preset_palettes_dir();
rf.includeUserDir(base::join_path("palettes", ".").c_str());
std::string palettesDir = rf.getFirstOrCreateDefault();
if (!base::is_directory(palettesDir))
base::make_directory(palettesDir);
return base::join_path(palettesDir, preset + dot_extension); return base::join_path(palettesDir, preset + dot_extension);
} }
@ -176,4 +171,16 @@ std::string get_default_palette_preset_name()
return "default"; return "default";
} }
std::string get_preset_palettes_dir()
{
ResourceFinder rf;
rf.includeUserDir(base::join_path("palettes", ".").c_str());
std::string palettesDir = rf.getFirstOrCreateDefault();
if (!base::is_directory(palettesDir))
base::make_directory(palettesDir);
return palettesDir;
}
} // namespace app } // namespace app

View File

@ -33,6 +33,7 @@ namespace app {
std::string get_preset_palette_filename(const std::string& preset, std::string get_preset_palette_filename(const std::string& preset,
const std::string& dot_extension); const std::string& dot_extension);
std::string get_default_palette_preset_name(); std::string get_default_palette_preset_name();
std::string get_preset_palettes_dir();
} // namespace app } // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A. // Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -8,6 +8,7 @@
#include "app/util/wrap_point.h" #include "app/util/wrap_point.h"
#include "app/tools/ink.h" #include "app/tools/ink.h"
#include "doc/algorithm/flip_image.h"
#include "render/gradient.h" #include "render/gradient.h"
namespace app { namespace app {
@ -61,7 +62,7 @@ class BrushPointShape : public PointShape {
bool m_firstPoint; bool m_firstPoint;
Brush* m_lastBrush; Brush* m_lastBrush;
BrushType m_origBrushType; BrushType m_origBrushType;
std::shared_ptr<CompressedImage> m_compressedImage; std::array<std::shared_ptr<CompressedImage>, 4> m_compressedImages;
// For dynamics // For dynamics
DynamicsOptions m_dynamics; DynamicsOptions m_dynamics;
bool m_useDynamics; bool m_useDynamics;
@ -192,9 +193,7 @@ public:
// TODO cache compressed images (or remove them completelly) // TODO cache compressed images (or remove them completelly)
if (m_lastBrush != brush) { if (m_lastBrush != brush) {
m_lastBrush = brush; m_lastBrush = brush;
m_compressedImage.reset(new CompressedImage(brush->image(), m_compressedImages.fill(nullptr);
brush->maskBitmap(),
false));
} }
x += brush->bounds().x; x += brush->bounds().x;
@ -227,7 +226,7 @@ public:
ink->prepareForPointShape(loop, m_firstPoint, x, y); ink->prepareForPointShape(loop, m_firstPoint, x, y);
for (auto scanline : *m_compressedImage) { for (auto scanline : getCompressedImage(pt.symmetry)) {
int u = x+scanline.x; int u = x+scanline.x;
ink->prepareVForPointShape(loop, y+scanline.y); ink->prepareVForPointShape(loop, y+scanline.y);
doInkHline(u, y+scanline.y, u+scanline.w-1, loop); doInkHline(u, y+scanline.y, u+scanline.w-1, loop);
@ -241,6 +240,47 @@ public:
area.y += y; area.y += y;
} }
private:
CompressedImage& getCompressedImage(gen::SymmetryMode symmetryMode) {
auto& compressPtr = m_compressedImages[int(symmetryMode)];
if (!compressPtr) {
switch (symmetryMode) {
case gen::SymmetryMode::NONE: {
compressPtr.reset(new CompressedImage(m_lastBrush->image(),
m_lastBrush->maskBitmap(),
false));
break;
}
case gen::SymmetryMode::HORIZONTAL:
case gen::SymmetryMode::VERTICAL: {
std::unique_ptr<Image> tempImage(Image::createCopy(m_lastBrush->image()));
doc::algorithm::FlipType flip =
(symmetryMode == gen::SymmetryMode::HORIZONTAL)?
doc::algorithm::FlipType::FlipHorizontal:
doc::algorithm::FlipType::FlipVertical;
doc::algorithm::flip_image(tempImage.get(), tempImage->bounds(), flip);
compressPtr.reset(new CompressedImage(tempImage.get(),
m_lastBrush->maskBitmap(),
false));
break;
}
case gen::SymmetryMode::BOTH: {
std::unique_ptr<Image> tempImage(Image::createCopy(m_lastBrush->image()));
doc::algorithm::flip_image(tempImage.get(),
tempImage->bounds(),
doc::algorithm::FlipType::FlipVertical);
doc::algorithm::flip_image(tempImage.get(),
tempImage->bounds(),
doc::algorithm::FlipType::FlipHorizontal);
compressPtr.reset(new CompressedImage(tempImage.get(),
m_lastBrush->maskBitmap(),
false));
break;
}
}
}
return *compressPtr;
}
}; };
class FloodFillPointShape : public PointShape { class FloodFillPointShape : public PointShape {

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A. // Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello // Copyright (C) 2001-2015 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -9,6 +9,7 @@
#define APP_TOOLS_STROKE_H_INCLUDED #define APP_TOOLS_STROKE_H_INCLUDED
#pragma once #pragma once
#include "app/pref/preferences.h"
#include "gfx/point.h" #include "gfx/point.h"
#include "gfx/rect.h" #include "gfx/rect.h"
@ -25,6 +26,7 @@ namespace app {
float size = 0.0f; float size = 0.0f;
float angle = 0.0f; float angle = 0.0f;
float gradient = 0.0f; float gradient = 0.0f;
gen::SymmetryMode symmetry = gen::SymmetryMode::NONE;
Pt() { } Pt() { }
Pt(const gfx::Point& point) : x(point.x), y(point.y) { } Pt(const gfx::Point& point) : x(point.x), y(point.y) { }
Pt(int x, int y) : x(x), y(y) { } Pt(int x, int y) : x(x), y(y) { }

View File

@ -1,84 +0,0 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2015-2017 David Capello
//
// 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/tools/symmetries.h"
#include "app/tools/point_shape.h"
#include "app/tools/stroke.h"
#include "app/tools/tool_loop.h"
#include "doc/brush.h"
namespace app {
namespace tools {
void HorizontalSymmetry::generateStrokes(const Stroke& mainStroke, Strokes& strokes,
ToolLoop* loop)
{
int brushSize, brushCenter;
if (loop->getPointShape()->isFloodFill()) {
brushSize = 1;
brushCenter = 0;
}
else {
// TODO we should flip the brush center+image+bitmap or just do
// the symmetry of all pixels
auto brush = loop->getBrush();
brushSize = brush->bounds().w;
brushCenter = brush->center().x;
}
strokes.push_back(mainStroke);
Stroke stroke2;
for (const auto& pt : mainStroke) {
Stroke::Pt pt2 = pt;
pt2.x = m_x - ((pt.x-brushCenter) - m_x + 1) - (brushSize - brushCenter - 1);
stroke2.addPoint(pt2);
}
strokes.push_back(stroke2);
}
void VerticalSymmetry::generateStrokes(const Stroke& mainStroke, Strokes& strokes,
ToolLoop* loop)
{
int brushSize, brushCenter;
if (loop->getPointShape()->isFloodFill()) {
brushSize = 1;
brushCenter = 0;
}
else {
auto brush = loop->getBrush();
brushSize = brush->bounds().h;
brushCenter = brush->center().y;
}
strokes.push_back(mainStroke);
Stroke stroke2;
for (const auto& pt : mainStroke) {
Stroke::Pt pt2 = pt;
pt2.y = m_y - ((pt.y-brushCenter) - m_y + 1) - (brushSize - brushCenter - 1);
stroke2.addPoint(pt2);
}
strokes.push_back(stroke2);
}
void SymmetryCombo::generateStrokes(const Stroke& mainStroke, Strokes& strokes,
ToolLoop* loop)
{
Strokes strokes0;
m_a->generateStrokes(mainStroke, strokes0, loop);
for (const Stroke& stroke : strokes0)
m_b->generateStrokes(stroke, strokes, loop);
}
} // namespace tools
} // namespace app

View File

@ -1,50 +0,0 @@
// Aseprite
// Copyright (C) 2015-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_TOOLS_SYMMETRIES_H_INCLUDED
#define APP_TOOLS_SYMMETRIES_H_INCLUDED
#pragma once
#include "app/tools/stroke.h"
#include "app/tools/symmetry.h"
#include <memory>
namespace app {
namespace tools {
class HorizontalSymmetry : public Symmetry {
public:
HorizontalSymmetry(double x) : m_x(x) { }
void generateStrokes(const Stroke& mainStroke, Strokes& strokes,
ToolLoop* loop) override;
private:
double m_x;
};
class VerticalSymmetry : public Symmetry {
public:
VerticalSymmetry(double y) : m_y(y) { }
void generateStrokes(const Stroke& mainStroke, Strokes& strokes,
ToolLoop* loop) override;
private:
double m_y;
};
class SymmetryCombo : public Symmetry {
public:
SymmetryCombo(Symmetry* a, Symmetry* b) : m_a(a), m_b(b) { }
void generateStrokes(const Stroke& mainStroke, Strokes& strokes,
ToolLoop* loop) override;
private:
std::unique_ptr<tools::Symmetry> m_a;
std::unique_ptr<tools::Symmetry> m_b;
};
} // namespace tools
} // namespace app
#endif

View File

@ -0,0 +1,91 @@
// Aseprite
// Copyright (C) 2021 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/tools/symmetry.h"
#include "app/tools/point_shape.h"
#include "app/tools/tool_loop.h"
namespace app {
namespace tools {
void Symmetry::generateStrokes(const Stroke& stroke, Strokes& strokes,
ToolLoop* loop)
{
Stroke stroke2;
strokes.push_back(stroke);
gen::SymmetryMode symmetryMode = loop->getSymmetry()->mode();
switch (symmetryMode) {
case gen::SymmetryMode::NONE:
ASSERT(false);
break;
case gen::SymmetryMode::HORIZONTAL:
case gen::SymmetryMode::VERTICAL:
calculateSymmetricalStroke(stroke, stroke2, loop, symmetryMode);
strokes.push_back(stroke2);
break;
case gen::SymmetryMode::BOTH: {
calculateSymmetricalStroke(stroke, stroke2, loop, gen::SymmetryMode::HORIZONTAL);
strokes.push_back(stroke2);
Stroke stroke3;
calculateSymmetricalStroke(stroke, stroke3, loop, gen::SymmetryMode::VERTICAL);
strokes.push_back(stroke3);
Stroke stroke4;
calculateSymmetricalStroke(stroke3, stroke4, loop, gen::SymmetryMode::BOTH);
strokes.push_back(stroke4);
break;
}
}
}
void Symmetry::calculateSymmetricalStroke(const Stroke& refStroke, Stroke& stroke,
ToolLoop* loop, gen::SymmetryMode symmetryMode)
{
int brushSize, brushCenter;
if (loop->getPointShape()->isFloodFill()) {
brushSize = 1;
brushCenter = 0;
}
else {
// TODO we should flip the brush center+image+bitmap or just do
// the symmetry of all pixels
auto brush = loop->getBrush();
if (symmetryMode == gen::SymmetryMode::HORIZONTAL || symmetryMode == gen::SymmetryMode::BOTH) {
brushSize = brush->bounds().w;
brushCenter = brush->center().x;
}
else {
brushSize = brush->bounds().h;
brushCenter = brush->center().y;
}
}
const bool isDynamic = loop->getDynamics().isDynamic();
for (const auto& pt : refStroke) {
if (isDynamic) {
brushSize = pt.size;
brushCenter = (brushSize - brushSize % 2) / 2;
}
Stroke::Pt pt2 = pt;
pt2.symmetry = symmetryMode;
if (symmetryMode == gen::SymmetryMode::HORIZONTAL || symmetryMode == gen::SymmetryMode::BOTH)
pt2.x = 2 * (m_x + brushCenter) - pt2.x - brushSize;
else
pt2.y = 2 * (m_y + brushCenter) - pt2.y - brushSize;
stroke.addPoint(pt2);
}
}
} // namespace tools
} // namespace app

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2015 David Capello // Copyright (C) 2015 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -9,24 +10,34 @@
#pragma once #pragma once
#include "app/tools/stroke.h" #include "app/tools/stroke.h"
#include "app/pref/preferences.h"
#include <vector>
namespace app { namespace app {
namespace tools { namespace tools {
class ToolLoop; class ToolLoop;
// This class controls user input. class Symmetry {
class Symmetry { public:
public: Symmetry(gen::SymmetryMode symmetryMode, double x, double y)
virtual ~Symmetry() { } : m_symmetryMode(symmetryMode)
, m_x(x)
, m_y(y) {
}
// The "stroke" must be relative to the sprite origin. void generateStrokes(const Stroke& stroke, Strokes& strokes, ToolLoop* loop);
virtual void generateStrokes(const Stroke& stroke, Strokes& strokes, ToolLoop* loop) = 0;
};
} // namespace tools gen::SymmetryMode mode() const { return m_symmetryMode; }
private:
void calculateSymmetricalStroke(const Stroke& refStroke, Stroke& stroke,
ToolLoop* loop, gen::SymmetryMode symmetryMode);
gen::SymmetryMode m_symmetryMode;
double m_x, m_y;
};
} // namespace tools
} // namespace app } // namespace app
#endif #endif

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2016-2017 David Capello // Copyright (C) 2016-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -16,6 +16,7 @@
#include "app/ui/main_window.h" #include "app/ui/main_window.h"
#include "app/ui/separator_in_view.h" #include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h" #include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h"
#include "app/ui/workspace.h" #include "app/ui/workspace.h"
#include "base/file_handle.h" #include "base/file_handle.h"
#include "base/fs.h" #include "base/fs.h"
@ -572,7 +573,8 @@ WorkspaceView* BrowserView::cloneWorkspaceView()
void BrowserView::onWorkspaceViewSelected() void BrowserView::onWorkspaceViewSelected()
{ {
// Do nothing if (auto statusBar = StatusBar::instance())
statusBar->clearText();
} }
bool BrowserView::onCloseView(Workspace* workspace, bool quitting) bool BrowserView::onCloseView(Workspace* workspace, bool quitting)

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A. // Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -13,6 +13,7 @@
#include "app/app.h" #include "app/app.h"
#include "app/app_menus.h" #include "app/app_menus.h"
#include "app/console.h"
#include "app/crash/data_recovery.h" #include "app/crash/data_recovery.h"
#include "app/crash/session.h" #include "app/crash/session.h"
#include "app/doc.h" #include "app/doc.h"
@ -106,19 +107,31 @@ public:
m_task = new TaskWidget( m_task = new TaskWidget(
TaskWidget::kCannotCancel, TaskWidget::kCannotCancel,
[this](base::task_token& t){ [this](base::task_token& t) {
// Warning: This is executed from a worker thread try {
m_session->deleteBackup(m_backup); // Warning: This is executed from a worker thread
ui::execute_from_ui_thread( m_session->deleteBackup(m_backup);
[this]{
onDeleteTaskWidget();
// We cannot use this->deferDelete() here because it looks ui::execute_from_ui_thread(
// like the m_task field can be still in use. [this]{
setVisible(false); onDeleteTaskWidget();
updateView(); // We cannot use this->deferDelete() here because it looks
}); // like the m_task field can be still in use.
setVisible(false);
updateView();
});
}
catch (const std::exception& ex) {
std::string err = ex.what();
if (!err.empty()) {
ui::execute_from_ui_thread(
[err]{
Console().printf("Error deleting file: %s", err.c_str());
});
}
}
}); });
addChild(m_task); addChild(m_task);
updateView(); updateView();
@ -477,6 +490,7 @@ void DataRecoveryView::onDelete()
int(items.size()))) != 1) int(items.size()))) != 1)
return; // Cancel return; // Cancel
Console console;
for (auto item : items) for (auto item : items)
item->deleteBackup(); item->deleteBackup();
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -248,7 +248,8 @@ WorkspaceView* DocView::cloneWorkspaceView()
void DocView::onWorkspaceViewSelected() void DocView::onWorkspaceViewSelected()
{ {
StatusBar::instance()->showDefaultText(m_document); if (auto statusBar = StatusBar::instance())
statusBar->showDefaultText(m_document);
} }
void DocView::onClonedFrom(WorkspaceView* from) void DocView::onClonedFrom(WorkspaceView* from)

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A. // Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -29,7 +29,7 @@
#include "app/tools/freehand_algorithm.h" #include "app/tools/freehand_algorithm.h"
#include "app/tools/ink.h" #include "app/tools/ink.h"
#include "app/tools/point_shape.h" #include "app/tools/point_shape.h"
#include "app/tools/symmetries.h" #include "app/tools/symmetry.h"
#include "app/tools/tool.h" #include "app/tools/tool.h"
#include "app/tools/tool_box.h" #include "app/tools/tool_box.h"
#include "app/tools/tool_loop.h" #include "app/tools/tool_loop.h"
@ -239,27 +239,10 @@ public:
// Symmetry mode // Symmetry mode
if (Preferences::instance().symmetryMode.enabled()) { if (Preferences::instance().symmetryMode.enabled()) {
switch (m_docPref.symmetry.mode()) { if (m_docPref.symmetry.mode() != gen::SymmetryMode::NONE)
m_symmetry.reset(new tools::Symmetry(m_docPref.symmetry.mode(),
case app::gen::SymmetryMode::NONE: m_docPref.symmetry.xAxis(),
ASSERT(m_symmetry == nullptr); m_docPref.symmetry.yAxis()));
break;
case app::gen::SymmetryMode::HORIZONTAL:
m_symmetry.reset(new app::tools::HorizontalSymmetry(m_docPref.symmetry.xAxis()));
break;
case app::gen::SymmetryMode::VERTICAL:
m_symmetry.reset(new app::tools::VerticalSymmetry(m_docPref.symmetry.yAxis()));
break;
case app::gen::SymmetryMode::BOTH:
m_symmetry.reset(
new app::tools::SymmetryCombo(
new app::tools::HorizontalSymmetry(m_docPref.symmetry.xAxis()),
new app::tools::VerticalSymmetry(m_docPref.symmetry.yAxis())));
break;
}
} }
// Ignore opacity for these inks // Ignore opacity for these inks

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -261,9 +261,19 @@ void MainWindow::showHome()
m_tabsBar->selectTab(m_homeView); m_tabsBar->selectTab(m_homeView);
} }
bool MainWindow::isHomeSelected() void MainWindow::showDefaultStatusBar()
{ {
return (m_tabsBar->getSelectedTab() == m_homeView && m_homeView); if (DocView* docView = getDocView())
m_statusBar->showDefaultText(docView->document());
else if (isHomeSelected())
m_statusBar->showAbout();
else
m_statusBar->clearText();
}
bool MainWindow::isHomeSelected() const
{
return (m_homeView && m_workspace->activeView() == m_homeView);
} }
void MainWindow::showBrowser(const std::string& filename) void MainWindow::showBrowser(const std::string& filename)
@ -473,12 +483,12 @@ void MainWindow::onTabsContainerDoubleClicked(Tabs* tabs)
void MainWindow::onMouseOverTab(Tabs* tabs, TabView* tabView) void MainWindow::onMouseOverTab(Tabs* tabs, TabView* tabView)
{ {
// Note: tabView can be NULL // Note: tabView can be NULL
if (DocView* docView = dynamic_cast<DocView*>(tabView)) { if (DocView* docView = dynamic_cast<DocView*>(tabView))
m_statusBar->showDefaultText(docView->document()); m_statusBar->showDefaultText(docView->document());
} else if (tabView)
else { m_statusBar->setStatusText(0, tabView->getTabText());
else
m_statusBar->showDefaultText(); m_statusBar->showDefaultText();
}
} }
void MainWindow::onMouseLeaveTab() void MainWindow::onMouseLeaveTab()

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -71,9 +71,10 @@ namespace app {
void showNotification(INotificationDelegate* del); void showNotification(INotificationDelegate* del);
void showHomeOnOpen(); void showHomeOnOpen();
void showHome(); void showHome();
bool isHomeSelected(); void showDefaultStatusBar();
void showDevConsole(); void showDevConsole();
void showBrowser(const std::string& filename); void showBrowser(const std::string& filename);
bool isHomeSelected() const;
Mode getMode() const { return m_mode; } Mode getMode() const { return m_mode; }
void setMode(Mode mode); void setMode(Mode mode);

View File

@ -42,6 +42,7 @@ PalettePopup::PalettePopup()
addChild(m_popup); addChild(m_popup);
m_paletteListBox.DoubleClickItem.connect([this]{ onLoadPal(); }); m_paletteListBox.DoubleClickItem.connect([this]{ onLoadPal(); });
m_paletteListBox.FinishLoading.connect([this]{ onSearchChange(); });
m_popup->search()->Change.connect([this]{ onSearchChange(); }); m_popup->search()->Change.connect([this]{ onSearchChange(); });
m_popup->loadPal()->Click.connect([this]{ onLoadPal(); }); m_popup->loadPal()->Click.connect([this]{ onLoadPal(); });
m_popup->openFolder()->Click.connect([this]{ onOpenFolder(); }); m_popup->openFolder()->Click.connect([this]{ onOpenFolder(); });

View File

@ -124,6 +124,9 @@ PalettesListBox::PalettesListBox()
m_extPaletteChanges = m_extPaletteChanges =
App::instance()->extensions().PalettesChange.connect( App::instance()->extensions().PalettesChange.connect(
[this]{ reload(); }); [this]{ reload(); });
m_extPresetsChanges =
App::instance()->PalettePresetsChange.connect(
[this]{ reload(); });
} }
doc::Palette* PalettesListBox::selectedPalette() doc::Palette* PalettesListBox::selectedPalette()

View File

@ -34,6 +34,7 @@ namespace app {
ui::TooltipManager m_tooltips; ui::TooltipManager m_tooltips;
obs::scoped_connection m_extPaletteChanges; obs::scoped_connection m_extPaletteChanges;
obs::scoped_connection m_extPresetsChanges;
}; };
} // namespace app } // namespace app

View File

@ -211,8 +211,10 @@ void ResourcesListBox::onTick()
listItem.release(); listItem.release();
} }
if (m_resourcesLoader->isDone()) if (m_resourcesLoader->isDone()) {
FinishLoading();
stop(); stop();
}
} }
void ResourcesListBox::stop() void ResourcesListBox::stop()

View File

@ -9,6 +9,7 @@
#pragma once #pragma once
#include "app/res/resources_loader.h" #include "app/res/resources_loader.h"
#include "obs/signal.h"
#include "ui/listbox.h" #include "ui/listbox.h"
#include "ui/listitem.h" #include "ui/listitem.h"
#include "ui/timer.h" #include "ui/timer.h"
@ -43,6 +44,8 @@ class ResourceListItem : public ui::ListItem {
void reload(); void reload();
obs::signal<void()> FinishLoading;
protected: protected:
virtual bool onProcessMessage(ui::Message* msg) override; virtual bool onProcessMessage(ui::Message* msg) override;
virtual void onChange() override; virtual void onChange() override;

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -61,6 +61,40 @@ using namespace gfx;
using namespace ui; using namespace ui;
using namespace doc; using namespace doc;
class StatusBar::AboutStatusBar : public HBox {
public:
AboutStatusBar()
: m_label(fmt::format("{} {} by ", get_app_name(), get_app_version()))
, m_link("", "Igara Studio")
{
m_link.Click.connect(
[]{
Command* cmd = Commands::instance()->byId(CommandId::About());
UIContext::instance()->executeCommandFromMenuOrShortcut(cmd);
});
addChild(new BoxFiller);
addChild(&m_label);
addChild(&m_link);
addChild(new BoxFiller);
InitTheme.connect(
[this]{
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
ui::Style* style = theme->styles.workspaceLink();
noBorderNoChildSpacing();
m_label.setStyle(style);
m_link.setStyle(style);
m_label.noBorderNoChildSpacing();
m_link.noBorderNoChildSpacing();
});
initTheme();
}
ui::Label m_label;
ui::LinkLabel m_link;
};
class StatusBar::Indicators : public HBox { class StatusBar::Indicators : public HBox {
class Indicator : public Widget { class Indicator : public Widget {
@ -632,6 +666,7 @@ StatusBar* StatusBar::m_instance = NULL;
StatusBar::StatusBar(TooltipManager* tooltipManager) StatusBar::StatusBar(TooltipManager* tooltipManager)
: m_timeout(0) : m_timeout(0)
, m_about(new AboutStatusBar)
, m_indicators(new Indicators) , m_indicators(new Indicators)
, m_docControls(new HBox) , m_docControls(new HBox)
, m_tipwindow(nullptr) , m_tipwindow(nullptr)
@ -642,8 +677,12 @@ StatusBar::StatusBar(TooltipManager* tooltipManager)
setDoubleBuffered(true); setDoubleBuffered(true);
setFocusStop(true); setFocusStop(true);
m_about->setExpansive(true);
m_about->setVisible(true);
m_indicators->setExpansive(true); m_indicators->setExpansive(true);
m_indicators->setVisible(false);
m_docControls->setVisible(false); m_docControls->setVisible(false);
addChild(m_about);
addChild(m_indicators); addChild(m_indicators);
addChild(m_docControls); addChild(m_docControls);
@ -698,6 +737,7 @@ void StatusBar::onSelectedToolChange(tools::Tool* tool)
void StatusBar::clearText() void StatusBar::clearText()
{ {
showIndicators();
setStatusText(1, std::string()); setStatusText(1, std::string());
} }
@ -706,16 +746,11 @@ void StatusBar::clearText()
// details of the main window/docs/etc. // details of the main window/docs/etc.
void StatusBar::showDefaultText() void StatusBar::showDefaultText()
{ {
if (current_editor) { auto mainWindow = (App::instance() ? App::instance()->mainWindow(): nullptr);
showDefaultText(current_editor->document()); if (mainWindow)
} mainWindow->showDefaultStatusBar();
else if (App::instance()->mainWindow()->isHomeSelected()) { else
setStatusText(0, fmt::format("-- {} {} by David & Gaspar Capello -- Igara Studio --",
get_app_name(), get_app_version()));
}
else {
clearText(); clearText();
}
} }
void StatusBar::showDefaultText(Doc* doc) void StatusBar::showDefaultText(Doc* doc)
@ -742,18 +777,22 @@ void StatusBar::showDefaultText(Doc* doc)
void StatusBar::updateFromEditor(Editor* editor) void StatusBar::updateFromEditor(Editor* editor)
{ {
if (editor) if (editor) {
showIndicators();
m_zoomEntry->setZoom(editor->zoom()); m_zoomEntry->setZoom(editor->zoom());
}
} }
void StatusBar::showBackupIcon(BackupIcon icon) void StatusBar::showBackupIcon(BackupIcon icon)
{ {
showIndicators();
m_indicators->showBackupIcon(icon); m_indicators->showBackupIcon(icon);
} }
bool StatusBar::setStatusText(int msecs, const std::string& msg) bool StatusBar::setStatusText(int msecs, const std::string& msg)
{ {
if ((base::current_tick() > m_timeout) || (msecs > 0)) { if ((base::current_tick() > m_timeout) || (msecs > 0)) {
showIndicators();
IndicatorsGeneration(m_indicators).add(msg.c_str()); IndicatorsGeneration(m_indicators).add(msg.c_str());
m_timeout = base::current_tick() + msecs; m_timeout = base::current_tick() + msecs;
return true; return true;
@ -795,6 +834,7 @@ void StatusBar::showTip(int msecs, const std::string& msg)
void StatusBar::showColor(int msecs, const char* text, const app::Color& color) void StatusBar::showColor(int msecs, const char* text, const app::Color& color)
{ {
if ((base::current_tick() > m_timeout) || (msecs > 0)) { if ((base::current_tick() > m_timeout) || (msecs > 0)) {
showIndicators();
IndicatorsGeneration gen(m_indicators); IndicatorsGeneration gen(m_indicators);
gen.add(color); gen.add(color);
if (text) if (text)
@ -818,7 +858,9 @@ void StatusBar::showTile(int msecs, const char* text, doc::tile_t tile)
void StatusBar::showTool(int msecs, tools::Tool* tool) void StatusBar::showTool(int msecs, tools::Tool* tool)
{ {
ASSERT(tool != NULL); showIndicators();
ASSERT(tool != nullptr);
IndicatorsGeneration(m_indicators).add(tool); IndicatorsGeneration(m_indicators).add(tool);
m_timeout = base::current_tick() + msecs; m_timeout = base::current_tick() + msecs;
@ -946,4 +988,22 @@ void StatusBar::updateSnapToGridWindowPosition()
rc.y-m_snapToGridWindow->bounds().h); rc.y-m_snapToGridWindow->bounds().h);
} }
void StatusBar::showAbout()
{
if (!m_about->isVisible()) {
m_indicators->setVisible(false);
m_about->setVisible(true);
m_about->layout();
}
}
void StatusBar::showIndicators()
{
if (!m_indicators->isVisible()) {
m_about->setVisible(false);
m_indicators->setVisible(true);
m_indicators->layout();
}
}
} // namespace app } // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -56,7 +56,8 @@ namespace app {
void clearText(); void clearText();
void showDefaultText(); void showDefaultText();
void showDefaultText(Doc* document); void showDefaultText(Doc* doc);
void showAbout();
bool setStatusText(int msecs, const std::string& text); bool setStatusText(int msecs, const std::string& text);
void showTip(int msecs, const std::string& msg); void showTip(int msecs, const std::string& msg);
@ -88,9 +89,14 @@ namespace app {
void newFrame(); void newFrame();
void onChangeZoom(const render::Zoom& zoom); void onChangeZoom(const render::Zoom& zoom);
void updateSnapToGridWindowPosition(); void updateSnapToGridWindowPosition();
void showIndicators();
base::tick_t m_timeout; base::tick_t m_timeout;
// About text
class AboutStatusBar;
AboutStatusBar* m_about;
// Indicators // Indicators
class Indicators; class Indicators;
class IndicatorsGeneration; class IndicatorsGeneration;

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A. // Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -100,16 +100,17 @@ void UIContext::setActiveView(DocView* docView)
return; return;
if (docView) { if (docView) {
current_editor = docView->editor();
mainWin->getTabsBar()->selectTab(docView); mainWin->getTabsBar()->selectTab(docView);
if (mainWin->getWorkspace()->activeView() != docView) if (mainWin->getWorkspace()->activeView() != docView)
mainWin->getWorkspace()->setActiveView(docView); mainWin->getWorkspace()->setActiveView(docView);
if (current_editor)
current_editor->requestFocus();
} }
else
current_editor = (docView ? docView->editor(): nullptr); current_editor = nullptr;
if (current_editor)
current_editor->requestFocus();
mainWin->getPreviewEditor()->updateUsingEditor(current_editor); mainWin->getPreviewEditor()->updateUsingEditor(current_editor);
mainWin->getTimeline()->updateUsingEditor(current_editor); mainWin->getTimeline()->updateUsingEditor(current_editor);

View File

@ -141,6 +141,9 @@ FileFormat detect_format_by_file_extension(const std::string& filename)
if (ext == "tga") if (ext == "tga")
return FileFormat::TARGA_IMAGE; return FileFormat::TARGA_IMAGE;
if (ext == "css")
return FileFormat::CSS_STYLE;
if (ext == "webp") if (ext == "webp")
return FileFormat::WEBP_ANIMATION; return FileFormat::WEBP_ANIMATION;

View File

@ -31,6 +31,7 @@ enum class FileFormat {
SVG_IMAGE, SVG_IMAGE,
TARGA_IMAGE, TARGA_IMAGE,
WEBP_ANIMATION, WEBP_ANIMATION,
CSS_STYLE,
}; };
} // namespace dio } // namespace dio

View File

@ -8,7 +8,6 @@
#define DOC_KEYFRAMES_H_INCLUDED #define DOC_KEYFRAMES_H_INCLUDED
#pragma once #pragma once
#include "base/disable_copying.h"
#include "doc/frame.h" #include "doc/frame.h"
#include <vector> #include <vector>
@ -111,6 +110,11 @@ namespace doc {
Keyframes() { } Keyframes() { }
Keyframes(const Keyframes& other) {
for (const auto& key : other.m_keys)
m_keys.push_back(Key(key.frame(), new T(*key.value())));
}
void insert(const frame_t frame, T* value) { void insert(const frame_t frame, T* value) {
auto it = getIterator(frame); auto it = getIterator(frame);
if (it == end()) if (it == end())
@ -189,9 +193,6 @@ namespace doc {
private: private:
List m_keys; List m_keys;
// Disable operator=
DISABLE_COPYING(Keyframes);
}; };
} // namespace doc } // namespace doc

View File

@ -42,6 +42,14 @@ Slice::Slice()
{ {
} }
Slice::Slice(const Slice& other)
: WithUserData(other)
, m_owner(nullptr)
, m_name(other.m_name)
, m_keys(other.m_keys)
{
}
Slice::~Slice() Slice::~Slice()
{ {
ASSERT(!m_owner); ASSERT(!m_owner);

View File

@ -55,6 +55,7 @@ namespace doc {
typedef List::const_iterator const_iterator; typedef List::const_iterator const_iterator;
Slice(); Slice();
Slice(const Slice& other);
~Slice(); ~Slice();
int getMemSize() const override; int getMemSize() const override;
@ -90,8 +91,6 @@ namespace doc {
Slices* m_owner; Slices* m_owner;
std::string m_name; std::string m_name;
List m_keys; List m_keys;
DISABLE_COPYING(Slice);
}; };
} // namespace doc } // namespace doc

View File

@ -23,7 +23,7 @@ BEGIN
VALUE "FileDescription", "Aseprite - Animated sprites editor & pixel art tool" VALUE "FileDescription", "Aseprite - Animated sprites editor & pixel art tool"
VALUE "FileVersion", "1,3,0,0" VALUE "FileVersion", "1,3,0,0"
VALUE "InternalName", "aseprite" VALUE "InternalName", "aseprite"
VALUE "LegalCopyright", "Copyright (C) 2001-2020 Igara Studio S.A." VALUE "LegalCopyright", "Copyright (C) 2001-2021 Igara Studio S.A."
VALUE "OriginalFilename", "aseprite.exe" VALUE "OriginalFilename", "aseprite.exe"
VALUE "ProductName", "ASEPRITE" VALUE "ProductName", "ASEPRITE"
VALUE "ProductVersion", "1,3,0,0" VALUE "ProductVersion", "1,3,0,0"

View File

@ -1,5 +1,5 @@
// Aseprite UI Library // Aseprite UI Library
// Copyright (C) 2018-2020 Igara Studio S.A. // Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -106,7 +106,11 @@ Widget::~Widget()
void Widget::deferDelete() void Widget::deferDelete()
{ {
manager()->addToGarbage(this); if (auto man = manager())
man->addToGarbage(this);
else {
ASSERT(false);
}
} }
void Widget::initTheme() void Widget::initTheme()
@ -219,7 +223,8 @@ void Widget::setVisible(bool state)
} }
else { else {
if (!hasFlags(HIDDEN)) { if (!hasFlags(HIDDEN)) {
manager()->freeWidget(this); // Free from manager if (auto man = manager())
man->freeWidget(this); // Free from manager
enableFlags(HIDDEN); enableFlags(HIDDEN);
onVisible(false); onVisible(false);
@ -241,7 +246,8 @@ void Widget::setEnabled(bool state)
} }
else { else {
if (!hasFlags(DISABLED)) { if (!hasFlags(DISABLED)) {
manager()->freeWidget(this); // Free from the manager if (auto man = manager())
man->freeWidget(this); // Free from the manager
enableFlags(DISABLED); enableFlags(DISABLED);
invalidate(); invalidate();
@ -541,9 +547,8 @@ void Widget::removeChild(WidgetsList::iterator& it)
m_children.erase(it); m_children.erase(it);
// Free from manager // Free from manager
Manager* manager = this->manager(); if (auto man = manager())
if (manager) man->freeWidget(child);
manager->freeWidget(child);
child->m_parent = nullptr; child->m_parent = nullptr;
} }
@ -617,7 +622,8 @@ void Widget::layout()
void Widget::loadLayout() void Widget::loadLayout()
{ {
if (!m_id.empty()) { if (!m_id.empty()) {
LayoutIO* io = manager()->getLayoutIO(); auto man = manager();
LayoutIO* io = (man ? man->getLayoutIO(): nullptr);
if (io) { if (io) {
std::string layout = io->loadLayout(this); std::string layout = io->loadLayout(this);
if (!layout.empty()) { if (!layout.empty()) {
@ -636,7 +642,8 @@ void Widget::loadLayout()
void Widget::saveLayout() void Widget::saveLayout()
{ {
if (!m_id.empty()) { if (!m_id.empty()) {
LayoutIO* io = manager()->getLayoutIO(); auto man = manager();
LayoutIO* io = (man ? man->getLayoutIO(): nullptr);
if (io) { if (io) {
std::stringstream s; std::stringstream s;
SaveLayoutEvent ev(this, s); SaveLayoutEvent ev(this, s);
@ -690,8 +697,8 @@ void Widget::setBoundsQuietly(const gfx::Rect& rc)
m_bounds = rc; m_bounds = rc;
// Remove all paint messages for this widget. // Remove all paint messages for this widget.
if (Manager* manager = this->manager()) if (Manager* man = manager())
manager->removeMessagesFor(this, kPaintMessage); man->removeMessagesFor(this, kPaintMessage);
} }
// TODO Test moving this inside the if (m_bounds != rc) { ... } // TODO Test moving this inside the if (m_bounds != rc) { ... }
@ -975,6 +982,8 @@ void Widget::flushRedraw()
Manager* manager = this->manager(); Manager* manager = this->manager();
ASSERT(manager); ASSERT(manager);
if (!manager)
return;
while (!processing.empty()) { while (!processing.empty()) {
Widget* widget = processing.front(); Widget* widget = processing.front();
@ -1300,36 +1309,44 @@ void Widget::resetSizeHint()
void Widget::requestFocus() void Widget::requestFocus()
{ {
manager()->setFocus(this); if (auto man = manager())
man->setFocus(this);
} }
void Widget::releaseFocus() void Widget::releaseFocus()
{ {
if (hasFocus()) if (hasFocus()) {
manager()->freeFocus(); if (auto man = manager())
man->freeFocus();
}
} }
// Captures the mouse to send all the future mouse messsages to the // Captures the mouse to send all the future mouse messsages to the
// specified widget (included the kMouseMoveMessage and kSetCursorMessage). // specified widget (included the kMouseMoveMessage and kSetCursorMessage).
void Widget::captureMouse() void Widget::captureMouse()
{ {
if (!manager()->getCapture()) { if (auto man = manager()) {
manager()->setCapture(this); if (!man->getCapture()) {
man->setCapture(this);
}
} }
} }
// Releases the capture of the mouse events. // Releases the capture of the mouse events.
void Widget::releaseMouse() void Widget::releaseMouse()
{ {
if (manager()->getCapture() == this) { if (auto man = manager()) {
manager()->freeCapture(); if (man->getCapture() == this) {
man->freeCapture();
}
} }
} }
bool Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type) bool Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
{ {
if (hasCapture()) { if (hasCapture()) {
Widget* pick = manager()->pick(mouseMsg->position()); auto man = manager();
Widget* pick = (man ? man->pick(mouseMsg->position()): nullptr);
if (pick && pick != this && pick->type() == widget_type) { if (pick && pick != this && pick->type() == widget_type) {
releaseMouse(); releaseMouse();
@ -1340,7 +1357,7 @@ bool Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
mouseMsg->modifiers(), mouseMsg->modifiers(),
mouseMsg->position()); mouseMsg->position());
mouseMsg2->setRecipient(pick); mouseMsg2->setRecipient(pick);
manager()->enqueueMessage(mouseMsg2); man->enqueueMessage(mouseMsg2);
return true; return true;
} }
} }
@ -1601,8 +1618,8 @@ void Widget::offsetWidgets(int dx, int dy)
m_bounds.offset(dx, dy); m_bounds.offset(dx, dy);
// Remove all paint messages for this widget. // Remove all paint messages for this widget.
if (Manager* manager = this->manager()) if (auto man = manager())
manager->removeMessagesFor(this, kPaintMessage); man->removeMessagesFor(this, kPaintMessage);
for (auto child : m_children) for (auto child : m_children)
child->offsetWidgets(dx, dy); child->offsetWidgets(dx, dy);