diff --git a/data/pref.xml b/data/pref.xml
index e702e603e..1c7c7ed7b 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -177,6 +177,9 @@
+
diff --git a/data/widgets/sprite_properties.xml b/data/widgets/sprite_properties.xml
index 29214bd28..3c4efc5d3 100644
--- a/data/widgets/sprite_properties.xml
+++ b/data/widgets/sprite_properties.xml
@@ -1,45 +1,47 @@
-
+
-
+
+
-
+
-
+
-
+
+
+
-
+
-
-
-
+
+
+
-
+
-
+
-
diff --git a/src/app/commands/cmd_sprite_properties.cpp b/src/app/commands/cmd_sprite_properties.cpp
index 975d16f5d..16695d9eb 100644
--- a/src/app/commands/cmd_sprite_properties.cpp
+++ b/src/app/commands/cmd_sprite_properties.cpp
@@ -12,6 +12,7 @@
#include "app/cmd/assign_color_profile.h"
#include "app/cmd/convert_color_profile.h"
#include "app/cmd/set_pixel_ratio.h"
+#include "app/cmd/set_user_data.h"
#include "app/color.h"
#include "app/commands/command.h"
#include "app/context_access.h"
@@ -21,11 +22,13 @@
#include "app/pref/preferences.h"
#include "app/tx.h"
#include "app/ui/color_button.h"
+#include "app/ui/user_data_view.h"
#include "app/util/pixel_ratio.h"
#include "base/mem_utils.h"
#include "doc/image.h"
#include "doc/palette.h"
#include "doc/sprite.h"
+#include "doc/user_data.h"
#include "fmt/format.h"
#include "os/color_space.h"
#include "os/system.h"
@@ -37,6 +40,37 @@ namespace app {
using namespace ui;
+class SpritePropertiesWindow : public app::gen::SpriteProperties {
+public:
+ SpritePropertiesWindow(Sprite* sprite)
+ : SpriteProperties()
+ , m_sprite(sprite)
+ , m_userDataView(new gen::UserData(), &Preferences::instance().sprite.userDataVisibility)
+ {
+ userData()->Click.connect([this]{ onToggleUserData(); });
+
+ ui::Grid* mainGrid = propertiesGrid();
+ m_userDataView.configureAndSet(m_sprite->userData(), mainGrid);
+
+ remapWindow();
+ centerWindow();
+ load_window_pos(this, "SpriteProperties");
+ manager()->invalidate();
+ }
+
+ const UserData& getUserData() const { return m_userDataView.userData(); }
+
+private:
+ void onToggleUserData() {
+ m_userDataView.toggleVisibility();
+ remapWindow();
+ manager()->invalidate();
+ }
+
+ Sprite* m_sprite;
+ UserDataView m_userDataView;
+};
+
class SpritePropertiesCommand : public Command {
public:
SpritePropertiesCommand();
@@ -67,7 +101,8 @@ void SpritePropertiesCommand::onExecute(Context* context)
os::instance()->listColorSpaces(colorSpaces);
// Load the window widget
- app::gen::SpriteProperties window;
+ SpritePropertiesWindow window(context->activeDocument()->sprite());
+
int selectedColorProfile = -1;
auto updateButtons =
@@ -208,8 +243,11 @@ void SpritePropertiesCommand::onExecute(Context* context)
PixelRatio pixelRatio =
base::convert_to(window.pixelRatio()->getValue());
+ const UserData newUserData = window.getUserData();
+
if (index != sprite->transparentColor() ||
- pixelRatio != sprite->pixelRatio()) {
+ pixelRatio != sprite->pixelRatio() ||
+ newUserData != sprite->userData()) {
Tx tx(writer.context(), "Change Sprite Properties");
DocApi api = writer.document()->getApi(tx);
@@ -219,6 +257,9 @@ void SpritePropertiesCommand::onExecute(Context* context)
if (pixelRatio != sprite->pixelRatio())
tx(new cmd::SetPixelRatio(sprite, pixelRatio));
+ if (newUserData != sprite->userData())
+ tx(new cmd::SetUserData(sprite, newUserData, static_cast(sprite->document())));
+
tx.commit();
update_screen_for_document(writer.document());
diff --git a/src/app/file/ase_format.cpp b/src/app/file/ase_format.cpp
index 7b4f280ff..db18a3a75 100644
--- a/src/app/file/ase_format.cpp
+++ b/src/app/file/ase_format.cpp
@@ -378,6 +378,10 @@ bool AseFormat::onSave(FileOp* fop)
// Write extra chunks in the first frame
if (frame == fop->roi().fromFrame()) {
+ // Write sprite user data only if needed
+ if (!sprite->userData().isEmpty())
+ ase_file_write_user_data_chunk(f, &frame_header, &sprite->userData());
+
// Write tilesets
ase_file_write_tileset_chunks(f, fop, &frame_header, ext_files,
sprite->tilesets());
diff --git a/src/app/ui/browser_view.cpp b/src/app/ui/browser_view.cpp
index 360dd12c0..616ae5e28 100644
--- a/src/app/ui/browser_view.cpp
+++ b/src/app/ui/browser_view.cpp
@@ -560,6 +560,11 @@ TabIcon BrowserView::getTabIcon()
return TabIcon::NONE;
}
+gfx::Color BrowserView::getTabColor()
+{
+ return gfx::ColorNone;
+}
+
WorkspaceView* BrowserView::cloneWorkspaceView()
{
return new BrowserView();
diff --git a/src/app/ui/browser_view.h b/src/app/ui/browser_view.h
index 849492740..caad3a8e8 100644
--- a/src/app/ui/browser_view.h
+++ b/src/app/ui/browser_view.h
@@ -25,6 +25,7 @@ namespace app {
// TabView implementation
std::string getTabText() override;
TabIcon getTabIcon() override;
+ gfx::Color getTabColor() override;
// WorkspaceView implementation
ui::Widget* getContentWidget() override { return this; }
diff --git a/src/app/ui/data_recovery_view.cpp b/src/app/ui/data_recovery_view.cpp
index ae66e22d8..72122a129 100644
--- a/src/app/ui/data_recovery_view.cpp
+++ b/src/app/ui/data_recovery_view.cpp
@@ -378,6 +378,11 @@ TabIcon DataRecoveryView::getTabIcon()
return TabIcon::NONE;
}
+gfx::Color DataRecoveryView::getTabColor()
+{
+ return gfx::ColorNone;
+}
+
void DataRecoveryView::onWorkspaceViewSelected()
{
// Do nothing
diff --git a/src/app/ui/data_recovery_view.h b/src/app/ui/data_recovery_view.h
index c28c2ceb8..e5773da92 100644
--- a/src/app/ui/data_recovery_view.h
+++ b/src/app/ui/data_recovery_view.h
@@ -37,6 +37,7 @@ namespace app {
// TabView implementation
std::string getTabText() override;
TabIcon getTabIcon() override;
+ gfx::Color getTabColor() override;
// WorkspaceView implementation
ui::Widget* getContentWidget() override { return this; }
diff --git a/src/app/ui/devconsole_view.cpp b/src/app/ui/devconsole_view.cpp
index d5626947a..128e16fe3 100644
--- a/src/app/ui/devconsole_view.cpp
+++ b/src/app/ui/devconsole_view.cpp
@@ -113,6 +113,11 @@ TabIcon DevConsoleView::getTabIcon()
return TabIcon::NONE;
}
+gfx::Color DevConsoleView::getTabColor()
+{
+ return gfx::ColorNone;
+}
+
WorkspaceView* DevConsoleView::cloneWorkspaceView()
{
return new DevConsoleView();
diff --git a/src/app/ui/devconsole_view.h b/src/app/ui/devconsole_view.h
index c26eaf751..a368e2fa4 100644
--- a/src/app/ui/devconsole_view.h
+++ b/src/app/ui/devconsole_view.h
@@ -32,6 +32,7 @@ namespace app {
// TabView implementation
std::string getTabText() override;
TabIcon getTabIcon() override;
+ gfx::Color getTabColor() override;
// WorkspaceView implementation
ui::Widget* getContentWidget() override { return this; }
diff --git a/src/app/ui/doc_view.cpp b/src/app/ui/doc_view.cpp
index c06c647c2..e13eefb2f 100644
--- a/src/app/ui/doc_view.cpp
+++ b/src/app/ui/doc_view.cpp
@@ -38,6 +38,7 @@
#include "app/util/clipboard.h"
#include "app/util/range_utils.h"
#include "base/fs.h"
+#include "doc/color.h"
#include "doc/layer.h"
#include "doc/sprite.h"
#include "fmt/format.h"
@@ -234,6 +235,12 @@ TabIcon DocView::getTabIcon()
return TabIcon::NONE;
}
+gfx::Color DocView::getTabColor()
+{
+ color_t c = m_editor->sprite()->userData().color();
+ return gfx::rgba(doc::rgba_getr(c), doc::rgba_getg(c), doc::rgba_getb(c), doc::rgba_geta(c));
+}
+
WorkspaceView* DocView::cloneWorkspaceView()
{
return new DocView(m_document, Normal, m_previewDelegate);
diff --git a/src/app/ui/doc_view.h b/src/app/ui/doc_view.h
index 5a8449d25..1dce2ff8a 100644
--- a/src/app/ui/doc_view.h
+++ b/src/app/ui/doc_view.h
@@ -58,6 +58,7 @@ namespace app {
// TabView implementation
std::string getTabText() override;
TabIcon getTabIcon() override;
+ gfx::Color getTabColor() override;
// WorkspaceView implementation
ui::Widget* getContentWidget() override { return this; }
diff --git a/src/app/ui/home_view.cpp b/src/app/ui/home_view.cpp
index f8b8e82cc..3d6a1b012 100644
--- a/src/app/ui/home_view.cpp
+++ b/src/app/ui/home_view.cpp
@@ -111,6 +111,11 @@ TabIcon HomeView::getTabIcon()
return TabIcon::HOME;
}
+gfx::Color HomeView::getTabColor()
+{
+ return gfx::ColorNone;
+}
+
bool HomeView::onCloseView(Workspace* workspace, bool quitting)
{
workspace->removeView(this);
diff --git a/src/app/ui/home_view.h b/src/app/ui/home_view.h
index a6f071a86..53793e4cd 100644
--- a/src/app/ui/home_view.h
+++ b/src/app/ui/home_view.h
@@ -49,6 +49,7 @@ namespace app {
// TabView implementation
std::string getTabText() override;
TabIcon getTabIcon() override;
+ gfx::Color getTabColor() override;
// WorkspaceView implementation
ui::Widget* getContentWidget() override { return this; }
diff --git a/src/app/ui/tabs.cpp b/src/app/ui/tabs.cpp
index 26e793b9d..d14eeed69 100644
--- a/src/app/ui/tabs.cpp
+++ b/src/app/ui/tabs.cpp
@@ -174,6 +174,7 @@ void Tabs::updateTabs()
tab->text = tab->view->getTabText();
tab->icon = tab->view->getTabIcon();
+ tab->color = tab->view->getTabColor();
tab->x = int(x);
tab->width = int(x+tabWidth) - int(x);
x += tabWidth;
@@ -626,6 +627,10 @@ void Tabs::drawTab(Graphics* g, const gfx::Rect& _box,
// Tab with text + clipping the close button
if (box.w > 8*ui::guiscale()) {
info.text = &tab->text;
+ if (tab->color != gfx::ColorNone) {
+ g->fillRect(tab->color, gfx::Rect(box.x+dx+2, box.y+dy+3, box.w-dx-2, box.h-3));
+ g->fillRect(tab->color, gfx::Rect(box.x+dx+3, box.y+dy+2, box.w-dx-3, 1));
+ }
theme->paintWidgetPart(
g, theme->styles.tabText(),
gfx::Rect(box.x+dx, box.y+dy, box.w-dx, box.h),
@@ -656,6 +661,11 @@ void Tabs::drawTab(Graphics* g, const gfx::Rect& _box,
}
}
+ if (tab->color != gfx::ColorNone) {
+ g->fillRect(tab->color, gfx::Rect(closeBox.x, closeBox.y+3, closeBox.w-3, closeBox.h-3));
+ g->fillRect(tab->color, gfx::Rect(closeBox.x, closeBox.y+2, closeBox.w-4, 1));
+ }
+
info.styleFlags = 0;
if (selected)
info.styleFlags |= ui::Style::Layer::kFocus;
diff --git a/src/app/ui/tabs.h b/src/app/ui/tabs.h
index 4ca1bec5c..c0bdb9c80 100644
--- a/src/app/ui/tabs.h
+++ b/src/app/ui/tabs.h
@@ -41,6 +41,9 @@ namespace app {
// Returns the icon to be shown in the tab
virtual TabIcon getTabIcon() = 0;
+
+ // Returns the tab background color
+ virtual gfx::Color getTabColor() = 0;
};
enum class DropTabResult {
@@ -116,6 +119,7 @@ namespace app {
TabView* view;
std::string text;
TabIcon icon;
+ gfx::Color color;
int x, width;
int oldX, oldWidth;
bool modified;
@@ -124,6 +128,7 @@ namespace app {
ASSERT(view);
text = view->getTabText();
icon = view->getTabIcon();
+ color = view->getTabColor();
x = width = oldX = oldWidth =
#if _DEBUG
diff --git a/src/dio/aseprite_decoder.cpp b/src/dio/aseprite_decoder.cpp
index 5337dc3fc..39099ab04 100644
--- a/src/dio/aseprite_decoder.cpp
+++ b/src/dio/aseprite_decoder.cpp
@@ -82,7 +82,7 @@ bool AsepriteDecoder::decode()
// Prepare variables for layer chunks
doc::Layer* last_layer = sprite->root();
- doc::WithUserData* last_object_with_user_data = nullptr;
+ doc::WithUserData* last_object_with_user_data = sprite.get();
doc::Cel* last_cel = nullptr;
auto tag_it = sprite->tags().begin();
auto tag_end = sprite->tags().end();
diff --git a/src/doc/sprite.cpp b/src/doc/sprite.cpp
index 30296ea17..20c46bb14 100644
--- a/src/doc/sprite.cpp
+++ b/src/doc/sprite.cpp
@@ -66,7 +66,7 @@ void Sprite::SetDefaultRgbMapAlgorithm(const RgbMapAlgorithm mapAlgo)
Sprite::Sprite(const ImageSpec& spec,
int ncolors)
- : Object(ObjectType::Sprite)
+ : WithUserData(ObjectType::Sprite)
, m_document(nullptr)
, m_spec(spec)
, m_pixelRatio(1, 1)
diff --git a/src/doc/sprite.h b/src/doc/sprite.h
index 90f1ca1c7..74c490f1e 100644
--- a/src/doc/sprite.h
+++ b/src/doc/sprite.h
@@ -24,6 +24,7 @@
#include "doc/rgbmap_algorithm.h"
#include "doc/slices.h"
#include "doc/tags.h"
+#include "doc/with_user_data.h"
#include "gfx/rect.h"
#include
@@ -52,7 +53,7 @@ namespace doc {
typedef std::vector PalettesList;
// The main structure used in the whole program to handle a sprite.
- class Sprite : public Object {
+ class Sprite : public WithUserData {
public:
enum class RgbMapFor {
OpaqueLayer,