Add possibility to move tiles w/drag and drop + remap tiles

This commit is contained in:
David Capello 2019-11-16 12:09:33 -03:00
parent 8db5b3fb4e
commit e792d4e078
27 changed files with 773 additions and 91 deletions

View File

@ -226,6 +226,11 @@ generate and stack new tiles automatically
END END
remap_palette = Remap Palette remap_palette = Remap Palette
remap_palette_tooltip = Matches old indexes with new indexes remap_palette_tooltip = Matches old indexes with new indexes
remap_tiles = Remap Tiles
remap_tiles_tooltip = Matches old tiles with new tiles
clear_tiles = Clear Tiles
resize_tiles = Resize Tiles
drag_and_drop_tiles = Drag And Drop Tiles
[commands] [commands]
About = About About = About
@ -276,6 +281,7 @@ Copy = Copy
CopyCel = Copy Cel CopyCel = Copy Cel
CopyColors = Copy Colors CopyColors = Copy Colors
CopyMerged = Copy Merged CopyMerged = Copy Merged
CopyTiles = Copy Tiles
CropSprite = Crop Sprite CropSprite = Crop Sprite
Cut = Cut Cut = Cut
DeselectMask = Deselect Mask DeselectMask = Deselect Mask
@ -357,6 +363,7 @@ MoveColors = Move Colors
MoveMask = Move {0} {1} MoveMask = Move {0} {1}
MoveMask_Boundaries = Selection Boundaries MoveMask_Boundaries = Selection Boundaries
MoveMask_Content = Selection Content MoveMask_Content = Selection Content
MoveTiles = Move Tiles
NewBrush = New Brush NewBrush = New Brush
NewFile = New File NewFile = New File
NewFile_FromClipboard = New File from Clipboard NewFile_FromClipboard = New File from Clipboard

View File

@ -447,7 +447,8 @@ add_library(app-lib
cmd/move_layer.cpp cmd/move_layer.cpp
cmd/patch_cel.cpp cmd/patch_cel.cpp
cmd/remap_colors.cpp cmd/remap_colors.cpp
cmd/remap_tiles.cpp cmd/remap_tilemaps.cpp
cmd/remap_tileset.cpp
cmd/remove_cel.cpp cmd/remove_cel.cpp
cmd/remove_frame.cpp cmd/remove_frame.cpp
cmd/remove_layer.cpp cmd/remove_layer.cpp
@ -539,6 +540,7 @@ add_library(app-lib
commands/filters/filter_worker.cpp commands/filters/filter_worker.cpp
commands/move_colors_command.cpp commands/move_colors_command.cpp
commands/move_thing.cpp commands/move_thing.cpp
commands/move_tiles_command.cpp
commands/new_params.cpp commands/new_params.cpp
commands/quick_command.cpp commands/quick_command.cpp
console.cpp console.cpp

View File

@ -65,6 +65,7 @@ void ActiveSiteHandler::getActiveSiteForDoc(Doc* doc, Site* site)
site->layer(doc::get<doc::Layer>(data.layer)); site->layer(doc::get<doc::Layer>(data.layer));
site->frame(data.frame); site->frame(data.frame);
site->selectedColors(data.selectedColors); site->selectedColors(data.selectedColors);
site->selectedTiles(data.selectedTiles);
} }
void ActiveSiteHandler::setActiveLayerInDoc(Doc* doc, doc::Layer* layer) void ActiveSiteHandler::setActiveLayerInDoc(Doc* doc, doc::Layer* layer)
@ -85,6 +86,12 @@ void ActiveSiteHandler::setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks
data.selectedColors = picks; data.selectedColors = picks;
} }
void ActiveSiteHandler::setSelectedTilesInDoc(Doc* doc, const doc::PalettePicks& picks)
{
Data& data = getData(doc);
data.selectedTiles = picks;
}
void ActiveSiteHandler::onAddLayer(DocEvent& ev) void ActiveSiteHandler::onAddLayer(DocEvent& ev)
{ {
Data& data = getData(ev.document()); Data& data = getData(ev.document());

View File

@ -39,6 +39,7 @@ namespace app {
void setActiveLayerInDoc(Doc* doc, doc::Layer* layer); void setActiveLayerInDoc(Doc* doc, doc::Layer* layer);
void setActiveFrameInDoc(Doc* doc, doc::frame_t frame); void setActiveFrameInDoc(Doc* doc, doc::frame_t frame);
void setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks& picks); void setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks& picks);
void setSelectedTilesInDoc(Doc* doc, const doc::PalettePicks& picks);
private: private:
// DocObserver impl // DocObserver impl
@ -52,6 +53,7 @@ namespace app {
doc::ObjectId layer; doc::ObjectId layer;
doc::frame_t frame; doc::frame_t frame;
doc::PalettePicks selectedColors; doc::PalettePicks selectedColors;
doc::PalettePicks selectedTiles;
}; };
Data& getData(Doc* doc); Data& getData(Doc* doc);

View File

@ -8,7 +8,7 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/cmd/remap_tiles.h" #include "app/cmd/remap_tilemaps.h"
#include "doc/cel.h" #include "doc/cel.h"
#include "doc/cels_range.h" #include "doc/cels_range.h"
@ -24,14 +24,14 @@ namespace cmd {
using namespace doc; using namespace doc;
RemapTiles::RemapTiles(Tileset* tileset, RemapTilemaps::RemapTilemaps(Tileset* tileset,
const Remap& remap) const Remap& remap)
: WithTileset(tileset) : WithTileset(tileset)
, m_remap(remap) , m_remap(remap)
{ {
} }
void RemapTiles::onExecute() void RemapTilemaps::onExecute()
{ {
Tileset* tileset = this->tileset(); Tileset* tileset = this->tileset();
Sprite* spr = tileset->sprite(); Sprite* spr = tileset->sprite();
@ -39,7 +39,7 @@ void RemapTiles::onExecute()
incrementVersions(tileset); incrementVersions(tileset);
} }
void RemapTiles::onUndo() void RemapTilemaps::onUndo()
{ {
Tileset* tileset = this->tileset(); Tileset* tileset = this->tileset();
Sprite* spr = tileset->sprite(); Sprite* spr = tileset->sprite();
@ -47,7 +47,7 @@ void RemapTiles::onUndo()
incrementVersions(tileset); incrementVersions(tileset);
} }
void RemapTiles::incrementVersions(Tileset* tileset) void RemapTilemaps::incrementVersions(Tileset* tileset)
{ {
Sprite* spr = tileset->sprite(); Sprite* spr = tileset->sprite();
for (const Cel* cel : spr->uniqueCels()) { for (const Cel* cel : spr->uniqueCels()) {

View File

@ -4,8 +4,8 @@
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
#ifndef APP_CMD_REMAP_TILES_H_INCLUDED #ifndef APP_CMD_REMAP_TILEMAPS_H_INCLUDED
#define APP_CMD_REMAP_TILES_H_INCLUDED #define APP_CMD_REMAP_TILEMAPS_H_INCLUDED
#pragma once #pragma once
#include "app/cmd.h" #include "app/cmd.h"
@ -16,11 +16,11 @@ namespace app {
namespace cmd { namespace cmd {
using namespace doc; using namespace doc;
class RemapTiles : public Cmd class RemapTilemaps : public Cmd
, public WithTileset { , public WithTileset {
public: public:
RemapTiles(Tileset* tileset, RemapTilemaps(Tileset* tileset,
const Remap& remap); const Remap& remap);
protected: protected:
void onExecute() override; void onExecute() override;

View File

@ -0,0 +1,54 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cmd/remap_tileset.h"
#include "doc/cel.h"
#include "doc/cels_range.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/remap.h"
#include "doc/sprite.h"
#include "doc/tileset.h"
namespace app {
namespace cmd {
using namespace doc;
RemapTileset::RemapTileset(Tileset* tileset,
const Remap& remap)
: WithTileset(tileset)
, m_remap(remap)
{
}
void RemapTileset::onExecute()
{
Tileset* tileset = this->tileset();
applyRemap(tileset, m_remap);
}
void RemapTileset::onUndo()
{
Tileset* tileset = this->tileset();
applyRemap(tileset, m_remap.invert());
}
void RemapTileset::applyRemap(Tileset* tileset, const Remap& remap)
{
tileset->remap(remap);
tileset->incrementVersion();
tileset->sprite()->incrementVersion();
}
} // namespace cmd
} // namespace app

View File

@ -0,0 +1,41 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CMD_REMAP_TILESET_H_INCLUDED
#define APP_CMD_REMAP_TILESET_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "app/cmd/with_tileset.h"
#include "doc/remap.h"
namespace app {
namespace cmd {
using namespace doc;
class RemapTileset : public Cmd
, public WithTileset {
public:
RemapTileset(Tileset* tileset,
const Remap& remap);
protected:
void onExecute() override;
void onUndo() override;
size_t onMemSize() const override {
return sizeof(*this) + m_remap.getMemSize();
}
private:
void applyRemap(Tileset* tileset, const Remap& remap);
Remap m_remap;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -16,6 +16,7 @@ FOR_EACH_COMMAND(ColorCurve)
FOR_EACH_COMMAND(ColorQuantization) FOR_EACH_COMMAND(ColorQuantization)
FOR_EACH_COMMAND(ConvolutionMatrix) FOR_EACH_COMMAND(ConvolutionMatrix)
FOR_EACH_COMMAND(CopyColors) FOR_EACH_COMMAND(CopyColors)
FOR_EACH_COMMAND(CopyTiles)
FOR_EACH_COMMAND(CropSprite) FOR_EACH_COMMAND(CropSprite)
FOR_EACH_COMMAND(Despeckle) FOR_EACH_COMMAND(Despeckle)
FOR_EACH_COMMAND(ExportSpriteSheet) FOR_EACH_COMMAND(ExportSpriteSheet)
@ -26,6 +27,7 @@ FOR_EACH_COMMAND(LayerFromBackground)
FOR_EACH_COMMAND(LoadPalette) FOR_EACH_COMMAND(LoadPalette)
FOR_EACH_COMMAND(MergeDownLayer) FOR_EACH_COMMAND(MergeDownLayer)
FOR_EACH_COMMAND(MoveColors) FOR_EACH_COMMAND(MoveColors)
FOR_EACH_COMMAND(MoveTiles)
FOR_EACH_COMMAND(NewFile) FOR_EACH_COMMAND(NewFile)
FOR_EACH_COMMAND(NewFrame) FOR_EACH_COMMAND(NewFrame)
FOR_EACH_COMMAND(NewLayer) FOR_EACH_COMMAND(NewLayer)

View File

@ -0,0 +1,85 @@
// Aseprite
// Copyright (c) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/app.h"
#include "app/commands/new_params.h"
#include "app/context.h"
#include "app/context_access.h"
#include "app/tx.h"
#include "app/util/cel_ops.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/remap.h"
#include "doc/tileset.h"
namespace app {
using namespace ui;
struct MoveTilesParams : public NewParams {
Param<int> before { this, 0, "before" };
};
class MoveTilesCommand : public CommandWithNewParams<MoveTilesParams> {
public:
MoveTilesCommand(const bool copy)
: CommandWithNewParams<MoveTilesParams>(CommandId::MoveTiles(),
CmdRecordableFlag)
, m_copy(copy) { }
protected:
bool onEnabled(Context* ctx) override {
return ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasActiveLayer |
ContextFlags::ActiveLayerIsTilemap);
}
void onExecute(Context* ctx) override {
ContextWriter writer(ctx);
doc::Layer* layer = writer.layer();
if (!layer || !layer->isTilemap())
return;
doc::Tileset* tileset = static_cast<LayerTilemap*>(layer)->tileset();
ASSERT(tileset);
if (!tileset)
return;
PalettePicks picks = writer.site()->selectedTiles();
if (picks.picks() == 0)
return;
Tx tx(writer.context(), onGetFriendlyName(), ModifyDocument);
const int beforeIndex = params().before();
int currentEntry = picks.firstPick();
if (m_copy)
copy_tiles_in_tileset(tx, tileset, picks, currentEntry, beforeIndex);
else
move_tiles_in_tileset(tx, tileset, picks, currentEntry, beforeIndex);
tx.commit();
}
private:
bool m_copy;
};
Command* CommandFactory::createMoveTilesCommand()
{
return new MoveTilesCommand(false);
}
Command* CommandFactory::createCopyTilesCommand()
{
return new MoveTilesCommand(true);
}
} // namespace app

View File

@ -99,6 +99,11 @@ void Context::setSelectedColors(const doc::PalettePicks& picks)
onSetSelectedColors(picks); onSetSelectedColors(picks);
} }
void Context::setSelectedTiles(const doc::PalettePicks& picks)
{
onSetSelectedTiles(picks);
}
bool Context::hasModifiedDocuments() const bool Context::hasModifiedDocuments() const
{ {
for (auto doc : documents()) for (auto doc : documents())
@ -241,6 +246,12 @@ void Context::onSetSelectedColors(const doc::PalettePicks& picks)
activeSiteHandler()->setSelectedColorsInDoc(m_lastSelectedDoc, picks); activeSiteHandler()->setSelectedColorsInDoc(m_lastSelectedDoc, picks);
} }
void Context::onSetSelectedTiles(const doc::PalettePicks& picks)
{
if (m_lastSelectedDoc)
activeSiteHandler()->setSelectedTilesInDoc(m_lastSelectedDoc, picks);
}
ActiveSiteHandler* Context::activeSiteHandler() const ActiveSiteHandler* Context::activeSiteHandler() const
{ {
if (!m_activeSiteHandler) if (!m_activeSiteHandler)

View File

@ -89,6 +89,7 @@ namespace app {
void setActiveLayer(doc::Layer* layer); void setActiveLayer(doc::Layer* layer);
void setActiveFrame(doc::frame_t frame); void setActiveFrame(doc::frame_t frame);
void setSelectedColors(const doc::PalettePicks& picks); void setSelectedColors(const doc::PalettePicks& picks);
void setSelectedTiles(const doc::PalettePicks& picks);
bool hasModifiedDocuments() const; bool hasModifiedDocuments() const;
void notifyActiveSiteChanged(); void notifyActiveSiteChanged();
@ -112,6 +113,7 @@ namespace app {
virtual void onSetActiveLayer(doc::Layer* layer); virtual void onSetActiveLayer(doc::Layer* layer);
virtual void onSetActiveFrame(const doc::frame_t frame); virtual void onSetActiveFrame(const doc::frame_t frame);
virtual void onSetSelectedColors(const doc::PalettePicks& picks); virtual void onSetSelectedColors(const doc::PalettePicks& picks);
virtual void onSetSelectedTiles(const doc::PalettePicks& picks);
virtual void onCloseDocument(Doc* doc); virtual void onCloseDocument(Doc* doc);
Doc* lastSelectedDoc() { return m_lastSelectedDoc; } Doc* lastSelectedDoc() { return m_lastSelectedDoc; }

View File

@ -116,6 +116,9 @@ void ContextFlags::updateFlagsFromSite(const Site& site)
if (site.selectedColors().picks() > 0) if (site.selectedColors().picks() > 0)
m_flags |= HasSelectedColors; m_flags |= HasSelectedColors;
if (site.selectedTiles().picks() > 0)
m_flags |= HasSelectedTiles;
} }
} // namespace app } // namespace app

View File

@ -35,6 +35,7 @@ namespace app {
ActiveLayerIsReference = 1 << 13, ActiveLayerIsReference = 1 << 13,
ActiveLayerIsTilemap = 1 << 14, ActiveLayerIsTilemap = 1 << 14,
HasSelectedColors = 1 << 15, HasSelectedColors = 1 << 15,
HasSelectedTiles = 1 << 16,
}; };
ContextFlags(); ContextFlags();

View File

@ -20,6 +20,7 @@
#include "doc/layer.h" #include "doc/layer.h"
#include "doc/object_ids.h" #include "doc/object_ids.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "doc/tile.h"
#include <algorithm> #include <algorithm>
#include <set> #include <set>
@ -37,6 +38,7 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
std::vector<frame_t> frames; std::vector<frame_t> frames;
std::set<ObjectId> cels; std::set<ObjectId> cels;
std::vector<color_t> colors; std::vector<color_t> colors;
std::vector<tile_index> tiles;
RangeObj(Site& site) { RangeObj(Site& site) {
const DocRange& docRange = site.range(); const DocRange& docRange = site.range();
@ -67,6 +69,9 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
if (site.selectedColors().picks() > 0) if (site.selectedColors().picks() > 0)
colors = site.selectedColors().toVectorOfIndexes(); colors = site.selectedColors().toVectorOfIndexes();
if (site.selectedTiles().picks() > 0)
tiles = site.selectedTiles().toVectorOfIndexes();
} }
RangeObj(const RangeObj&) = delete; RangeObj(const RangeObj&) = delete;
RangeObj& operator=(const RangeObj&) = delete; RangeObj& operator=(const RangeObj&) = delete;
@ -85,6 +90,9 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
bool containsColor(const color_t color) const { bool containsColor(const color_t color) const {
return (std::find(colors.begin(), colors.end(), color) != colors.end()); return (std::find(colors.begin(), colors.end(), color) != colors.end());
} }
bool containsTile(const tile_t tile) const {
return (std::find(tiles.begin(), tiles.end(), tile) != tiles.end());
}
}; };
int Range_gc(lua_State* L) int Range_gc(lua_State* L)
@ -128,11 +136,19 @@ int Range_contains(lua_State* L)
int Range_containsColor(lua_State* L) int Range_containsColor(lua_State* L)
{ {
auto obj = get_obj<RangeObj>(L, 1); auto obj = get_obj<RangeObj>(L, 1);
color_t color = lua_tointeger(L, 2); const color_t color = lua_tointeger(L, 2);
lua_pushboolean(L, obj->containsColor(color)); lua_pushboolean(L, obj->containsColor(color));
return 1; return 1;
} }
int Range_containsTile(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
const tile_index tile = lua_tointeger(L, 2);
lua_pushboolean(L, obj->containsTile(tile));
return 1;
}
int Range_get_isEmpty(lua_State* L) int Range_get_isEmpty(lua_State* L)
{ {
auto obj = get_obj<RangeObj>(L, 1); auto obj = get_obj<RangeObj>(L, 1);
@ -212,6 +228,18 @@ int Range_get_colors(lua_State* L)
return 1; return 1;
} }
int Range_get_tiles(lua_State* L)
{
auto obj = get_obj<RangeObj>(L, 1);
lua_newtable(L);
int j = 1;
for (tile_index i : obj->tiles) {
lua_pushinteger(L, i);
lua_rawseti(L, -2, j++);
}
return 1;
}
int Range_set_colors(lua_State* L) int Range_set_colors(lua_State* L)
{ {
app::Context* ctx = App::instance()->context(); app::Context* ctx = App::instance()->context();
@ -230,10 +258,29 @@ int Range_set_colors(lua_State* L)
return 0; return 0;
} }
int Range_set_tiles(lua_State* L)
{
app::Context* ctx = App::instance()->context();
doc::PalettePicks picks;
if (lua_istable(L, 2)) {
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
int i = lua_tointeger(L, -1);
if (i >= picks.size())
picks.resize(i+1);
picks[i] = true;
lua_pop(L, 1);
}
}
ctx->setSelectedTiles(picks);
return 0;
}
const luaL_Reg Range_methods[] = { const luaL_Reg Range_methods[] = {
{ "__gc", Range_gc }, { "__gc", Range_gc },
{ "contains", Range_contains }, { "contains", Range_contains },
{ "containsColor", Range_containsColor }, { "containsColor", Range_containsColor },
{ "containsTile", Range_containsTile },
{ nullptr, nullptr } { nullptr, nullptr }
}; };
@ -247,6 +294,7 @@ const Property Range_properties[] = {
{ "images", Range_get_images, nullptr }, { "images", Range_get_images, nullptr },
{ "editableImages", Range_get_editableImages, nullptr }, { "editableImages", Range_get_editableImages, nullptr },
{ "colors", Range_get_colors, Range_set_colors }, { "colors", Range_get_colors, Range_set_colors },
{ "tiles", Range_get_tiles, Range_set_tiles },
{ nullptr, nullptr, nullptr } { nullptr, nullptr, nullptr }
}; };

View File

@ -91,6 +91,13 @@ namespace app {
m_selectedColors = colors; m_selectedColors = colors;
} }
// Selected tiles selected in the ColorBar
const doc::PalettePicks& selectedTiles() const { return m_selectedTiles; }
doc::PalettePicks& selectedTiles() { return m_selectedTiles; }
void selectedTiles(const doc::PalettePicks& tiles) {
m_selectedTiles = tiles;
}
const doc::SelectedObjects& selectedSlices() const { return m_selectedSlices; } const doc::SelectedObjects& selectedSlices() const { return m_selectedSlices; }
doc::SelectedObjects& selectedSlices() { return m_selectedSlices; } doc::SelectedObjects& selectedSlices() { return m_selectedSlices; }
void selectedSlices(const doc::SelectedObjects& set) { void selectedSlices(const doc::SelectedObjects& set) {
@ -116,6 +123,7 @@ namespace app {
doc::frame_t m_frame; doc::frame_t m_frame;
DocRange m_range; DocRange m_range;
doc::PalettePicks m_selectedColors; doc::PalettePicks m_selectedColors;
doc::PalettePicks m_selectedTiles;
doc::SelectedObjects m_selectedSlices; doc::SelectedObjects m_selectedSlices;
TilesetMode m_tilesetMode; TilesetMode m_tilesetMode;
}; };

View File

@ -5,7 +5,7 @@
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
#define COLOR_BAR_TRACE(...) #define COLOR_BAR_TRACE(...) // TRACE
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
@ -16,6 +16,8 @@
#include "app/app.h" #include "app/app.h"
#include "app/app_menus.h" #include "app/app_menus.h"
#include "app/cmd/remap_colors.h" #include "app/cmd/remap_colors.h"
#include "app/cmd/remap_tilemaps.h"
#include "app/cmd/remap_tileset.h"
#include "app/cmd/remove_tile.h" #include "app/cmd/remove_tile.h"
#include "app/cmd/replace_image.h" #include "app/cmd/replace_image.h"
#include "app/cmd/set_palette.h" #include "app/cmd/set_palette.h"
@ -50,6 +52,7 @@
#include "app/ui/timeline/timeline.h" #include "app/ui/timeline/timeline.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "app/util/cel_ops.h"
#include "app/util/clipboard.h" #include "app/util/clipboard.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/scoped_value.h" #include "base/scoped_value.h"
@ -64,6 +67,7 @@
#include "doc/rgbmap.h" #include "doc/rgbmap.h"
#include "doc/sort_palette.h" #include "doc/sort_palette.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "doc/tileset_hash_table.h"
#include "os/surface.h" #include "os/surface.h"
#include "ui/alert.h" #include "ui/alert.h"
#include "ui/graphics.h" #include "ui/graphics.h"
@ -146,7 +150,8 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
, m_splitter(Splitter::ByPercentage, VERTICAL) , m_splitter(Splitter::ByPercentage, VERTICAL)
, m_paletteView(true, PaletteView::FgBgColors, this, 16) , m_paletteView(true, PaletteView::FgBgColors, this, 16)
, m_tilesView(true, PaletteView::FgBgTiles, this, 16) , m_tilesView(true, PaletteView::FgBgTiles, this, 16)
, m_remapButton("Remap") , m_remapPalButton(Strings::color_bar_remap_palette())
, m_remapTilesButton(Strings::color_bar_remap_tiles())
, m_selector(ColorSelector::NONE) , m_selector(ColorSelector::NONE)
, m_tintShadeTone(nullptr) , m_tintShadeTone(nullptr)
, m_spectrum(nullptr) , m_spectrum(nullptr)
@ -199,11 +204,13 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
m_scrollableTilesView.setExpansive(true); m_scrollableTilesView.setExpansive(true);
m_scrollableTilesView.setVisible(false); m_scrollableTilesView.setVisible(false);
m_remapButton.setVisible(false); m_remapPalButton.setVisible(false);
m_remapTilesButton.setVisible(false);
m_palettePlaceholder.addChild(&m_scrollablePalView); m_palettePlaceholder.addChild(&m_scrollablePalView);
m_palettePlaceholder.addChild(&m_scrollableTilesView); m_palettePlaceholder.addChild(&m_scrollableTilesView);
m_palettePlaceholder.addChild(&m_remapButton); m_palettePlaceholder.addChild(&m_remapPalButton);
m_palettePlaceholder.addChild(&m_remapTilesButton);
m_splitter.setId("palette_spectrum_splitter"); m_splitter.setId("palette_spectrum_splitter");
m_splitter.setPosition(80); m_splitter.setPosition(80);
m_splitter.setExpansive(true); m_splitter.setExpansive(true);
@ -249,7 +256,8 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
m_fgColor.setExpansive(true); m_fgColor.setExpansive(true);
m_bgColor.setExpansive(true); m_bgColor.setExpansive(true);
m_remapButton.Click.connect(base::Bind<void>(&ColorBar::onRemapButtonClick, this)); m_remapPalButton.Click.connect(base::Bind<void>(&ColorBar::onRemapPalButtonClick, this));
m_remapTilesButton.Click.connect(base::Bind<void>(&ColorBar::onRemapTilesButtonClick, this));
m_fgColor.Change.connect(&ColorBar::onFgColorButtonChange, this); m_fgColor.Change.connect(&ColorBar::onFgColorButtonChange, this);
m_fgColor.BeforeChange.connect(&ColorBar::onFgColorButtonBeforeChange, this); m_fgColor.BeforeChange.connect(&ColorBar::onFgColorButtonBeforeChange, this);
m_bgColor.Change.connect(&ColorBar::onBgColorButtonChange, this); m_bgColor.Change.connect(&ColorBar::onBgColorButtonChange, this);
@ -328,7 +336,8 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
m_fgConn = Preferences::instance().colorBar.fgColor.AfterChange.connect(base::Bind<void>(&ColorBar::onFgColorChangeFromPreferences, this)); m_fgConn = Preferences::instance().colorBar.fgColor.AfterChange.connect(base::Bind<void>(&ColorBar::onFgColorChangeFromPreferences, this));
m_bgConn = Preferences::instance().colorBar.bgColor.AfterChange.connect(base::Bind<void>(&ColorBar::onBgColorChangeFromPreferences, this)); m_bgConn = Preferences::instance().colorBar.bgColor.AfterChange.connect(base::Bind<void>(&ColorBar::onBgColorChangeFromPreferences, this));
m_sepConn = Preferences::instance().colorBar.entriesSeparator.AfterChange.connect(base::Bind<void>(&ColorBar::invalidate, this)); m_sepConn = Preferences::instance().colorBar.entriesSeparator.AfterChange.connect(base::Bind<void>(&ColorBar::invalidate, this));
m_paletteView.FocusOrClick.connect(&ColorBar::onFocusPaletteView, this); m_paletteView.FocusOrClick.connect(&ColorBar::onFocusPaletteOrTilesView, this);
m_tilesView.FocusOrClick.connect(&ColorBar::onFocusPaletteOrTilesView, this);
m_appPalChangeConn = App::instance()->PaletteChange.connect(&ColorBar::onAppPaletteChange, this); m_appPalChangeConn = App::instance()->PaletteChange.connect(&ColorBar::onAppPaletteChange, this);
KeyboardShortcuts::instance()->UserChange.connect( KeyboardShortcuts::instance()->UserChange.connect(
base::Bind<void>(&ColorBar::setupTooltips, this, tooltipManager)); base::Bind<void>(&ColorBar::setupTooltips, this, tooltipManager));
@ -542,7 +551,8 @@ void ColorBar::onActiveSiteChange(const Site& site)
if (m_lastDocument) if (m_lastDocument)
m_lastDocument->add_observer(this); m_lastDocument->add_observer(this);
hideRemap(); hideRemapPal();
hideRemapTiles();
} }
bool isTilemap = false; bool isTilemap = false;
@ -599,7 +609,7 @@ void ColorBar::onAppPaletteChange()
updateWarningIcon(m_bgColor.getColor(), m_bgWarningIcon); updateWarningIcon(m_bgColor.getColor(), m_bgWarningIcon);
} }
void ColorBar::onFocusPaletteView(ui::Message* msg) void ColorBar::onFocusPaletteOrTilesView(ui::Message* msg)
{ {
App::instance()->inputChain().prioritize(this, msg); App::instance()->inputChain().prioritize(this, msg);
} }
@ -608,8 +618,9 @@ void ColorBar::onBeforeExecuteCommand(CommandExecutionEvent& ev)
{ {
if (ev.command()->id() == CommandId::SetPalette() || if (ev.command()->id() == CommandId::SetPalette() ||
ev.command()->id() == CommandId::LoadPalette() || ev.command()->id() == CommandId::LoadPalette() ||
ev.command()->id() == CommandId::ColorQuantization()) ev.command()->id() == CommandId::ColorQuantization()) {
showRemap(); showRemapPal();
}
} }
void ColorBar::onAfterExecuteCommand(CommandExecutionEvent& ev) void ColorBar::onAfterExecuteCommand(CommandExecutionEvent& ev)
@ -619,12 +630,19 @@ void ColorBar::onAfterExecuteCommand(CommandExecutionEvent& ev)
invalidate(); invalidate();
// If the sprite isn't Indexed anymore (e.g. because we've just // If the sprite isn't Indexed anymore (e.g. because we've just
// undone a "RGB -> Indexed" conversion), we hide the "Remap" // undone a "RGB -> Indexed" conversion), we hide the "Remap
// button. // Palette" button.
Site site = UIContext::instance()->activeSite(); Site site = UIContext::instance()->activeSite();
if (site.sprite() && if (site.sprite() &&
site.sprite()->pixelFormat() != IMAGE_INDEXED) { site.sprite()->pixelFormat() != IMAGE_INDEXED) {
hideRemap(); hideRemapPal();
}
// If the layer isn't a tilemap anymore, we hide the "Remap Tiles"
// button.
if (site.layer() &&
!site.layer()->isTilemap()) {
hideRemapTiles();
} }
} }
@ -670,7 +688,7 @@ void ColorBar::onTilesetModeButtonClick()
setTilesetMode(static_cast<TilesetMode>(item)); setTilesetMode(static_cast<TilesetMode>(item));
} }
void ColorBar::onRemapButtonClick() void ColorBar::onRemapPalButtonClick()
{ {
ASSERT(m_oldPalette); ASSERT(m_oldPalette);
@ -741,7 +759,59 @@ void ColorBar::onRemapButtonClick()
tx.commit(); tx.commit();
} }
update_screen_for_document(writer.document()); update_screen_for_document(writer.document());
hideRemap(); hideRemapPal();
}
catch (base::Exception& e) {
Console::showException(e);
}
}
void ColorBar::onRemapTilesButtonClick()
{
COLOR_BAR_TRACE("remapTiles\n");
try {
ContextWriter writer(UIContext::instance(), 500);
Sprite* sprite = writer.sprite();
if (!sprite)
return;
auto tileset = m_tilesView.tileset();
doc::TilesetHashTable hash;
{
doc::tile_index i = 0;
for (const auto& image : *tileset) {
ASSERT(image);
hash[image] = i++;
}
}
// Remap all tiles in the same order as in newTileset
Remap remap(tileset->size());
for (tile_index ti=0; ti<remap.size(); ++ti) {
auto img = m_oldTileset->get(ti);
if (img && hash.find(img) != hash.end()) {
auto destTi = hash[img];
COLOR_BAR_TRACE(" - Remap tile %d -> %d\n", ti, destTi);
remap.map(ti, destTi);
}
else {
remap.map(ti, ti);
}
}
// Nothing to remap
if (remap.isIdentity()) {
COLOR_BAR_TRACE(" - Nothing to remap\n");
return;
}
Tx tx(writer.context(), Strings::color_bar_remap_tiles(), ModifyDocument);
tx(new cmd::RemapTilemaps(tileset, remap));
tx.commit();
hideRemapTiles();
// TODO this should be automatic in last ~Tx() destruction
manager()->invalidate();
} }
catch (base::Exception& e) { catch (base::Exception& e) {
Console::showException(e); Console::showException(e);
@ -752,10 +822,6 @@ void ColorBar::onPaletteViewIndexChange(int index, ui::MouseButtons buttons)
{ {
COLOR_BAR_TRACE("ColorBar::onPaletteViewIndexChange(%d)\n", index); COLOR_BAR_TRACE("ColorBar::onPaletteViewIndexChange(%d)\n", index);
// TODO select tiles to stamp
if (inTilesMode())
return;
base::ScopedValue<bool> lock(m_fromPalView, true, m_fromPalView); base::ScopedValue<bool> lock(m_fromPalView, true, m_fromPalView);
app::Color color = app::Color::fromIndex(index); app::Color color = app::Color::fromIndex(index);
@ -784,10 +850,10 @@ void ColorBar::onPaletteViewModification(const Palette* newPalette,
void ColorBar::setPalette(const doc::Palette* newPalette, const std::string& actionText) void ColorBar::setPalette(const doc::Palette* newPalette, const std::string& actionText)
{ {
showRemap(); showRemapPal();
try { try {
ContextWriter writer(UIContext::instance()); ContextWriter writer(UIContext::instance(), 500);
Sprite* sprite = writer.sprite(); Sprite* sprite = writer.sprite();
frame_t frame = writer.frame(); frame_t frame = writer.frame();
if (sprite && if (sprite &&
@ -884,7 +950,7 @@ app::Color ColorBar::onPaletteViewGetBackgroundIndex()
return getBgColor(); return getBgColor();
} }
void ColorBar::onPaletteViewClearTiles(const doc::PalettePicks& picks) void ColorBar::onTilesViewClearTiles(const doc::PalettePicks& picks)
{ {
try { try {
ContextWriter writer(UIContext::instance(), 500); ContextWriter writer(UIContext::instance(), 500);
@ -894,9 +960,13 @@ void ColorBar::onPaletteViewClearTiles(const doc::PalettePicks& picks)
auto tileset = m_tilesView.tileset(); auto tileset = m_tilesView.tileset();
Tx tx(writer.context(), "Clear Tiles", ModifyDocument); Tx tx(writer.context(), "Clear Tiles", ModifyDocument);
for (auto ti : picks) for (doc::tile_index ti=0; ti<picks.size(); ++ti) {
tx(new cmd::RemoveTile(tileset, ti)); if (picks[ti])
tx(new cmd::RemoveTile(tileset, ti));
}
tx.commit(); tx.commit();
update_screen_for_document(writer.document());
} }
} }
catch (base::Exception& e) { catch (base::Exception& e) {
@ -904,6 +974,78 @@ void ColorBar::onPaletteViewClearTiles(const doc::PalettePicks& picks)
} }
} }
void ColorBar::onTilesViewResize(const int newSize)
{
auto tileset = m_tilesView.tileset();
if (!tileset || tileset->size() == newSize)
return;
try {
ContextWriter writer(UIContext::instance(), 500);
Sprite* sprite = writer.sprite();
ASSERT(writer.layer()->isTilemap());
if (sprite) {
auto tileset = m_tilesView.tileset();
Tx tx(writer.context(), Strings::color_bar_resize_tiles(), ModifyDocument);
if (tileset->size() < newSize) {
for (doc::tile_index ti=tileset->size(); ti<newSize; ++ti) {
ImageRef img = tileset->makeEmptyTile();
tx(new cmd::AddTile(tileset, img));
}
}
else {
for (doc::tile_index ti=tileset->size()-1;
ti!=(doc::tile_index)newSize-1; --ti) {
tx(new cmd::RemoveTile(tileset, ti));
}
}
tx.commit();
// TODO this should be automatic (when tileset is changed after a transaction)
m_scrollableTilesView.updateView();
update_screen_for_document(writer.document());
}
}
catch (base::Exception& e) {
Console::showException(e);
}
}
void ColorBar::onTilesViewDragAndDrop(doc::Tileset* tileset,
doc::PalettePicks& picks,
int& currentEntry,
const int beforeIndex,
const bool isCopy)
{
COLOR_BAR_TRACE("ColorBar::onTilesViewDragAndDrop() -> beforeIndex=%d\n",
beforeIndex);
showRemapTiles();
try {
ContextWriter writer(UIContext::instance(), 500);
Tx tx(writer.context(), Strings::color_bar_drag_and_drop_tiles(), ModifyDocument);
if (isCopy)
copy_tiles_in_tileset(tx, tileset, picks, currentEntry, beforeIndex);
else
move_tiles_in_tileset(tx, tileset, picks, currentEntry, beforeIndex);
tx.commit();
m_scrollableTilesView.updateView();
update_screen_for_document(writer.document());
}
catch (base::Exception& e) {
Console::showException(e);
}
}
void ColorBar::onTilesViewIndexChange(int index, ui::MouseButtons buttons)
{
// TODO show tools to stamp/draw/pick tiles
}
void ColorBar::onFgColorChangeFromPreferences() void ColorBar::onFgColorChangeFromPreferences()
{ {
COLOR_BAR_TRACE("ColorBar::onFgColorChangeFromPreferences() -> %s\n", COLOR_BAR_TRACE("ColorBar::onFgColorChangeFromPreferences() -> %s\n",
@ -1132,26 +1274,51 @@ void ColorBar::setAscending(bool ascending)
m_ascending = ascending; m_ascending = ascending;
} }
void ColorBar::showRemap() void ColorBar::showRemapPal()
{ {
Site site = UIContext::instance()->activeSite(); Site site = UIContext::instance()->activeSite();
if (site.sprite() && if (site.sprite() &&
site.sprite()->pixelFormat() == IMAGE_INDEXED) { site.sprite()->pixelFormat() == IMAGE_INDEXED) {
if (!m_oldPalette) { if (!m_oldPalette) {
m_oldPalette.reset(new Palette(*get_current_palette())); m_oldPalette.reset(new Palette(*get_current_palette()));
m_remapButton.setVisible(true); m_remapPalButton.setVisible(true);
layout(); layout();
} }
} }
} }
void ColorBar::hideRemap() void ColorBar::showRemapTiles()
{
Site site = UIContext::instance()->activeSite();
if (site.layer() &&
site.layer()->isTilemap()) {
if (!m_oldTileset) {
m_oldTileset.reset(
Tileset::MakeCopyCopyingImages(
static_cast<LayerTilemap*>(site.layer())->tileset()));
m_remapTilesButton.setVisible(true);
layout();
}
}
}
void ColorBar::hideRemapPal()
{ {
if (!m_oldPalette) if (!m_oldPalette)
return; return;
m_oldPalette.reset(); m_oldPalette.reset();
m_remapButton.setVisible(false); m_remapPalButton.setVisible(false);
layout();
}
void ColorBar::hideRemapTiles()
{
if (!m_oldTileset)
return;
m_oldTileset.reset();
m_remapTilesButton.setVisible(false);
layout(); layout();
} }
@ -1162,34 +1329,46 @@ void ColorBar::onNewInputPriority(InputChainElement* element,
msg && (msg->ctrlPressed() || msg->shiftPressed())) msg && (msg->ctrlPressed() || msg->shiftPressed()))
return; return;
if (element != this) if (element != this) {
m_paletteView.deselect(); if (m_tilesMode)
m_tilesView.deselect();
else
m_paletteView.deselect();
}
} }
bool ColorBar::onCanCut(Context* ctx) bool ColorBar::onCanCut(Context* ctx)
{ {
return (m_paletteView.getSelectedEntriesCount() > 0); if (m_tilesMode)
return (m_tilesView.getSelectedEntriesCount() > 0);
else
return (m_paletteView.getSelectedEntriesCount() > 0);
} }
bool ColorBar::onCanCopy(Context* ctx) bool ColorBar::onCanCopy(Context* ctx)
{ {
return (m_paletteView.getSelectedEntriesCount() > 0); return onCanCut(ctx);
} }
bool ColorBar::onCanPaste(Context* ctx) bool ColorBar::onCanPaste(Context* ctx)
{ {
return (clipboard::get_current_format() == clipboard::ClipboardPaletteEntries); if (m_tilesMode)
return (clipboard::get_current_format() == clipboard::ClipboardTiles);
else
return (clipboard::get_current_format() == clipboard::ClipboardPaletteEntries);
} }
bool ColorBar::onCanClear(Context* ctx) bool ColorBar::onCanClear(Context* ctx)
{ {
return (m_paletteView.getSelectedEntriesCount() > 0); return onCanCut(ctx);
} }
bool ColorBar::onCut(Context* ctx) bool ColorBar::onCut(Context* ctx)
{ {
if (m_tilesMode) if (m_tilesMode) {
m_tilesView.cutToClipboard(); m_tilesView.cutToClipboard();
showRemapTiles();
}
else else
m_paletteView.cutToClipboard(); m_paletteView.cutToClipboard();
return true; return true;
@ -1206,8 +1385,10 @@ bool ColorBar::onCopy(Context* ctx)
bool ColorBar::onPaste(Context* ctx) bool ColorBar::onPaste(Context* ctx)
{ {
if (m_tilesMode) if (m_tilesMode) {
m_tilesView.pasteFromClipboard(); m_tilesView.pasteFromClipboard();
showRemapTiles();
}
else else
m_paletteView.pasteFromClipboard(); m_paletteView.pasteFromClipboard();
return true; return true;
@ -1215,8 +1396,10 @@ bool ColorBar::onPaste(Context* ctx)
bool ColorBar::onClear(Context* ctx) bool ColorBar::onClear(Context* ctx)
{ {
if (m_tilesMode) if (m_tilesMode) {
m_tilesView.clearSelection(); m_tilesView.clearSelection();
showRemapTiles();
}
else else
m_paletteView.clearSelection(); m_paletteView.clearSelection();
return true; return true;
@ -1416,7 +1599,8 @@ void ColorBar::setupTooltips(TooltipManager* tooltipManager)
tooltipManager->addTooltipFor(m_buttons.getItem((int)PalButton::SORT), Strings::color_bar_sort_and_gradients(), BOTTOM); tooltipManager->addTooltipFor(m_buttons.getItem((int)PalButton::SORT), Strings::color_bar_sort_and_gradients(), BOTTOM);
tooltipManager->addTooltipFor(m_buttons.getItem((int)PalButton::PRESETS), Strings::color_bar_presets(), BOTTOM); tooltipManager->addTooltipFor(m_buttons.getItem((int)PalButton::PRESETS), Strings::color_bar_presets(), BOTTOM);
tooltipManager->addTooltipFor(m_buttons.getItem((int)PalButton::OPTIONS), Strings::color_bar_options(), BOTTOM); tooltipManager->addTooltipFor(m_buttons.getItem((int)PalButton::OPTIONS), Strings::color_bar_options(), BOTTOM);
tooltipManager->addTooltipFor(&m_remapButton, Strings::color_bar_remap_palette_tooltip(), BOTTOM); tooltipManager->addTooltipFor(&m_remapPalButton, Strings::color_bar_remap_palette_tooltip(), BOTTOM);
tooltipManager->addTooltipFor(&m_remapTilesButton, Strings::color_bar_remap_tiles_tooltip(), BOTTOM);
tooltipManager->addTooltipFor( tooltipManager->addTooltipFor(
m_tilesButton.getItem(0), m_tilesButton.getItem(0),

View File

@ -21,6 +21,7 @@
#include "doc/object_id.h" #include "doc/object_id.h"
#include "doc/pixel_format.h" #include "doc/pixel_format.h"
#include "doc/sort_palette.h" #include "doc/sort_palette.h"
#include "doc/tileset.h"
#include "obs/connection.h" #include "obs/connection.h"
#include "obs/signal.h" #include "obs/signal.h"
#include "ui/box.h" #include "ui/box.h"
@ -113,7 +114,7 @@ namespace app {
protected: protected:
void onAppPaletteChange(); void onAppPaletteChange();
void onFocusPaletteView(ui::Message* msg); void onFocusPaletteOrTilesView(ui::Message* msg);
void onBeforeExecuteCommand(CommandExecutionEvent& ev); void onBeforeExecuteCommand(CommandExecutionEvent& ev);
void onAfterExecuteCommand(CommandExecutionEvent& ev); void onAfterExecuteCommand(CommandExecutionEvent& ev);
void onSwitchPalEditMode(); void onSwitchPalEditMode();
@ -121,7 +122,8 @@ namespace app {
void onTilesButtonClick(); void onTilesButtonClick();
void onTilesetModeButtonClick(); void onTilesetModeButtonClick();
void onTilesetOptionsClick(); void onTilesetOptionsClick();
void onRemapButtonClick(); void onRemapPalButtonClick();
void onRemapTilesButtonClick();
void onPaletteIndexChange(PaletteIndexChangeEvent& ev); void onPaletteIndexChange(PaletteIndexChangeEvent& ev);
void onFgColorChangeFromPreferences(); void onFgColorChangeFromPreferences();
void onBgColorChangeFromPreferences(); void onBgColorChangeFromPreferences();
@ -144,11 +146,20 @@ namespace app {
void onPaletteViewPasteColors(const Palette* fromPal, const doc::PalettePicks& from, const doc::PalettePicks& to) override; void onPaletteViewPasteColors(const Palette* fromPal, const doc::PalettePicks& from, const doc::PalettePicks& to) override;
app::Color onPaletteViewGetForegroundIndex() override; app::Color onPaletteViewGetForegroundIndex() override;
app::Color onPaletteViewGetBackgroundIndex() override; app::Color onPaletteViewGetBackgroundIndex() override;
void onPaletteViewClearTiles(const doc::PalettePicks& picks) override; void onTilesViewClearTiles(const doc::PalettePicks& picks) override;
void onTilesViewResize(const int newSize) override;
void onTilesViewDragAndDrop(doc::Tileset* tileset,
doc::PalettePicks& picks,
int& currentEntry,
const int beforeIndex,
const bool isCopy) override;
void onTilesViewIndexChange(int index, ui::MouseButtons buttons) override;
private: private:
void showRemap(); void showRemapPal();
void hideRemap(); void showRemapTiles();
void hideRemapPal();
void hideRemapTiles();
void setPalette(const doc::Palette* newPalette, const std::string& actionText); void setPalette(const doc::Palette* newPalette, const std::string& actionText);
void setTransparentIndex(int index); void setTransparentIndex(int index);
void updateWarningIcon(const app::Color& color, ui::Button* warningIcon); void updateWarningIcon(const app::Color& color, ui::Button* warningIcon);
@ -184,7 +195,8 @@ namespace app {
ScrollableView m_scrollableTilesView; ScrollableView m_scrollableTilesView;
PaletteView m_paletteView; PaletteView m_paletteView;
PaletteView m_tilesView; PaletteView m_tilesView;
ui::Button m_remapButton; ui::Button m_remapPalButton;
ui::Button m_remapTilesButton;
ColorSelector m_selector; ColorSelector m_selector;
ColorTintShadeTone* m_tintShadeTone; ColorTintShadeTone* m_tintShadeTone;
ColorSpectrum* m_spectrum; ColorSpectrum* m_spectrum;
@ -206,6 +218,7 @@ namespace app {
bool m_fromBgButton; bool m_fromBgButton;
std::unique_ptr<doc::Palette> m_oldPalette; std::unique_ptr<doc::Palette> m_oldPalette;
std::unique_ptr<doc::Tileset> m_oldTileset;
Doc* m_lastDocument; Doc* m_lastDocument;
doc::ObjectId m_lastTilesetId; doc::ObjectId m_lastTilesetId;
bool m_ascending; bool m_ascending;

View File

@ -5,6 +5,8 @@
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
#define PAL_TRACE(...) // TRACEARGS
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
@ -52,6 +54,7 @@
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <set>
namespace app { namespace app {
@ -67,6 +70,15 @@ public:
virtual void activeSiteChange(const Site& site, doc::PalettePicks& picks) = 0; virtual void activeSiteChange(const Site& site, doc::PalettePicks& picks) = 0;
virtual void clearSelection(PaletteView* paletteView, virtual void clearSelection(PaletteView* paletteView,
doc::PalettePicks& picks) = 0; doc::PalettePicks& picks) = 0;
virtual void selectIndex(PaletteView* paletteView,
int index, ui::MouseButtons buttons) = 0;
virtual void resizePalette(PaletteView* paletteView,
int newSize) = 0;
virtual void dropColors(PaletteView* paletteView,
doc::PalettePicks& picks,
int& currentEntry,
const int beforeIndex,
const bool isCopy) = 0;
virtual void showEntryInStatusBar(StatusBar* statusBar, int index) = 0; virtual void showEntryInStatusBar(StatusBar* statusBar, int index) = 0;
virtual void showDragInfoInStatusBar(StatusBar* statusBar, bool copy, int destIndex, int newSize) = 0; virtual void showDragInfoInStatusBar(StatusBar* statusBar, bool copy, int destIndex, int newSize) = 0;
virtual void showResizeInfoInStatusBar(StatusBar* statusBar, int newSize) = 0; virtual void showResizeInfoInStatusBar(StatusBar* statusBar, int newSize) = 0;
@ -103,7 +115,39 @@ public:
newPalette.setEntry(remap[i], palette.getEntry(i)); newPalette.setEntry(remap[i], palette.getEntry(i));
} }
paletteView->setNewPalette(&palette, &newPalette, PaletteViewModification::CLEAR); paletteView->setNewPalette(&palette, &newPalette,
PaletteViewModification::CLEAR);
}
void selectIndex(PaletteView* paletteView,
int index, ui::MouseButtons buttons) override {
// Emit signal
if (paletteView->delegate())
paletteView->delegate()->onPaletteViewIndexChange(index, buttons);
}
void resizePalette(PaletteView* paletteView,
int newSize) override {
Palette newPalette(*paletteView->currentPalette());
newPalette.resize(newSize);
paletteView->setNewPalette(paletteView->currentPalette(),
&newPalette,
PaletteViewModification::RESIZE);
}
void dropColors(PaletteView* paletteView,
doc::PalettePicks& picks,
int& currentEntry,
const int beforeIndex,
const bool isCopy) override {
Palette palette(*paletteView->currentPalette());
Palette newPalette(palette);
move_or_copy_palette_colors(
palette,
newPalette,
picks,
currentEntry,
beforeIndex,
isCopy);
paletteView->setNewPalette(&palette, &newPalette,
PaletteViewModification::DRAGANDDROP);
} }
void showEntryInStatusBar(StatusBar* statusBar, int index) override { void showEntryInStatusBar(StatusBar* statusBar, int index) override {
statusBar->showColor( statusBar->showColor(
@ -175,7 +219,32 @@ public:
} }
void clearSelection(PaletteView* paletteView, void clearSelection(PaletteView* paletteView,
doc::PalettePicks& picks) override { doc::PalettePicks& picks) override {
paletteView->delegate()->onPaletteViewClearTiles(picks); paletteView->delegate()->onTilesViewClearTiles(picks);
}
void selectIndex(PaletteView* paletteView,
int index, ui::MouseButtons buttons) override {
// Emit signal
if (paletteView->delegate())
paletteView->delegate()->onTilesViewIndexChange(index, buttons);
}
void resizePalette(PaletteView* paletteView,
int newSize) override {
paletteView->delegate()->onTilesViewResize(newSize);
}
void dropColors(PaletteView* paletteView,
doc::PalettePicks& picks,
int& currentEntry,
const int beforeIndex,
const bool isCopy) override {
PAL_TRACE("dropColors");
doc::Tileset* tileset = this->tileset();
ASSERT(tileset);
if (!tileset)
return;
paletteView->delegate()->onTilesViewDragAndDrop(
tileset, picks, currentEntry, beforeIndex, isCopy);
} }
void showEntryInStatusBar(StatusBar* statusBar, int index) override { void showEntryInStatusBar(StatusBar* statusBar, int index) override {
statusBar->setStatusText( statusBar->setStatusText(
@ -554,9 +623,7 @@ bool PaletteView::onProcessMessage(Message* msg)
} }
} }
// Emit signal m_adapter->selectIndex(this, idx, buttons);
if (m_delegate)
m_delegate->onPaletteViewIndexChange(idx, buttons);
} }
} }
@ -590,11 +657,8 @@ bool PaletteView::onProcessMessage(Message* msg)
case State::RESIZING_PALETTE: case State::RESIZING_PALETTE:
if (m_hot.part == Hit::COLOR || if (m_hot.part == Hit::COLOR ||
m_hot.part == Hit::POSSIBLE_COLOR) { m_hot.part == Hit::POSSIBLE_COLOR) {
int newPalSize = MAX(1, m_hot.color); int newSize = std::max(1, m_hot.color);
Palette newPalette(*currentPalette()); m_adapter->resizePalette(this, newSize);
newPalette.resize(newPalSize);
setNewPalette(currentPalette(), &newPalette,
PaletteViewModification::RESIZE);
} }
break; break;
} }
@ -988,17 +1052,11 @@ PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos)
void PaletteView::dropColors(int beforeIndex) void PaletteView::dropColors(int beforeIndex)
{ {
Palette palette(*currentPalette()); m_adapter->dropColors(this,
Palette newPalette(palette); m_selectedEntries,
move_or_copy_palette_colors( m_currentEntry,
palette, beforeIndex,
newPalette, m_copy);
m_selectedEntries,
m_currentEntry,
beforeIndex,
m_copy);
setNewPalette(&palette, &newPalette,
PaletteViewModification::DRAGANDDROP);
} }
void PaletteView::getEntryBoundsAndClip(int i, const PalettePicks& entries, void PaletteView::getEntryBoundsAndClip(int i, const PalettePicks& entries,

View File

@ -47,17 +47,26 @@ namespace app {
const doc::Palette* fromPal, const doc::PalettePicks& from, const doc::PalettePicks& to) { } const doc::Palette* fromPal, const doc::PalettePicks& from, const doc::PalettePicks& to) { }
virtual app::Color onPaletteViewGetForegroundIndex() { return app::Color::fromMask(); } virtual app::Color onPaletteViewGetForegroundIndex() { return app::Color::fromMask(); }
virtual app::Color onPaletteViewGetBackgroundIndex() { return app::Color::fromMask(); } virtual app::Color onPaletteViewGetBackgroundIndex() { return app::Color::fromMask(); }
virtual void onPaletteViewClearTiles(const doc::PalettePicks& tiles) { } virtual void onTilesViewClearTiles(const doc::PalettePicks& tiles) { }
virtual void onTilesViewResize(const int newSize) { }
virtual void onTilesViewDragAndDrop(doc::Tileset* tileset,
doc::PalettePicks& picks,
int& currentEntry,
const int beforeIndex,
const bool isCopy) { }
virtual void onTilesViewIndexChange(int index, ui::MouseButtons buttons) { }
}; };
class AbstractPaletteViewAdapter; class AbstractPaletteViewAdapter;
class PaletteViewAdapter; class PaletteViewAdapter;
class TilesetViewAdapter;
class PaletteView : public ui::Widget class PaletteView : public ui::Widget
, public MarchingAnts , public MarchingAnts
, public IColorSource , public IColorSource
, public ContextObserver { , public ContextObserver {
friend class PaletteViewAdapter; friend class PaletteViewAdapter;
friend class TilesetViewAdapter;
public: public:
enum PaletteViewStyle { enum PaletteViewStyle {
SelectOneColor, SelectOneColor,

View File

@ -15,7 +15,8 @@
#include "app/cmd/clear_cel.h" #include "app/cmd/clear_cel.h"
#include "app/cmd/clear_mask.h" #include "app/cmd/clear_mask.h"
#include "app/cmd/copy_region.h" #include "app/cmd/copy_region.h"
#include "app/cmd/remap_tiles.h" #include "app/cmd/remap_tilemaps.h"
#include "app/cmd/remap_tileset.h"
#include "app/cmd/remove_tile.h" #include "app/cmd/remove_tile.h"
#include "app/cmd/replace_image.h" #include "app/cmd/replace_image.h"
#include "app/cmd/set_cel_position.h" #include "app/cmd/set_cel_position.h"
@ -46,7 +47,7 @@
#include <cmath> #include <cmath>
#include <memory> #include <memory>
#define OPS_TRACE(...) #define OPS_TRACE(...) // TRACE
namespace app { namespace app {
@ -640,8 +641,46 @@ void remove_unused_tiles_from_tileset(
OPS_TRACE(" - remap tile[%d] -> %d\n", ti, remap[ti]); OPS_TRACE(" - remap tile[%d] -> %d\n", ti, remap[ti]);
} }
#endif #endif
cmds->executeAndAdd(new cmd::RemapTiles(tileset, remap)); cmds->executeAndAdd(new cmd::RemapTilemaps(tileset, remap));
} }
} }
void move_tiles_in_tileset(
CmdSequence* cmds,
doc::Tileset* tileset,
doc::PalettePicks& picks,
int& currentEntry,
int beforeIndex)
{
OPS_TRACE("move_tiles_in_tileset\n");
int n = beforeIndex - tileset->size();
if (n > 0) {
picks.resize(picks.size()+n);
while (n-- > 0)
cmds->executeAndAdd(new cmd::AddTile(tileset, tileset->makeEmptyTile()));
}
Remap remap = create_remap_to_move_picks(picks, beforeIndex);
cmds->executeAndAdd(new cmd::RemapTileset(tileset, remap));
// New selection
auto oldPicks = picks;
for (int i=0; i<picks.size(); ++i)
picks[remap[i]] = oldPicks[i];
currentEntry = remap[currentEntry];
}
void copy_tiles_in_tileset(
CmdSequence* cmds,
doc::Tileset* tileset,
doc::PalettePicks& picks,
int& currentEntry,
int beforeIndex)
{
OPS_TRACE("copy_tiles_in_tileset\n");
// TODO copy tiles
}
} // namespace app } // namespace app

View File

@ -23,6 +23,7 @@ namespace doc {
class Cel; class Cel;
class Layer; class Layer;
class LayerTilemap; class LayerTilemap;
class PalettePicks;
class Sprite; class Sprite;
class Tileset; class Tileset;
} }
@ -77,6 +78,20 @@ namespace app {
doc::Tileset* tileset, doc::Tileset* tileset,
std::vector<bool>& unusedTiles); std::vector<bool>& unusedTiles);
void move_tiles_in_tileset(
CmdSequence* cmds,
doc::Tileset* tileset,
doc::PalettePicks& picks,
int& currentEntry,
int beforeIndex);
void copy_tiles_in_tileset(
CmdSequence* cmds,
doc::Tileset* tileset,
doc::PalettePicks& picks,
int& currentEntry,
int beforeIndex);
} // namespace app } // namespace app
#endif #endif

View File

@ -99,6 +99,7 @@ namespace clipboard {
using namespace doc; using namespace doc;
static std::shared_ptr<Palette> clipboard_palette; static std::shared_ptr<Palette> clipboard_palette;
static std::shared_ptr<Tileset> clipboard_tiles;
static PalettePicks clipboard_picks; static PalettePicks clipboard_picks;
static ImageRef clipboard_image; static ImageRef clipboard_image;
static std::shared_ptr<Mask> clipboard_mask; static std::shared_ptr<Mask> clipboard_mask;
@ -226,6 +227,8 @@ ClipboardFormat get_current_format()
return ClipboardDocRange; return ClipboardDocRange;
else if (clipboard_palette && clipboard_picks.picks()) else if (clipboard_palette && clipboard_picks.picks())
return ClipboardPaletteEntries; return ClipboardPaletteEntries;
else if (clipboard_tiles && clipboard_picks.picks())
return ClipboardTiles;
else else
return ClipboardNone; return ClipboardNone;
} }

View File

@ -38,6 +38,7 @@ namespace app {
ClipboardImage, ClipboardImage,
ClipboardDocRange, ClipboardDocRange,
ClipboardPaletteEntries, ClipboardPaletteEntries,
ClipboardTiles,
}; };
// TODO Horrible API: refactor it (maybe a merge with os::clipboard). // TODO Horrible API: refactor it (maybe a merge with os::clipboard).

View File

@ -259,8 +259,8 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
bool same_width_columns = bool_attr_is_true(elem, "same_width_columns"); bool same_width_columns = bool_attr_is_true(elem, "same_width_columns");
if (columns != NULL) { if (columns != NULL) {
widget = new Grid(strtol(columns, NULL, 10), widget = new ui::Grid(strtol(columns, NULL, 10),
same_width_columns); same_width_columns);
} }
} }
else if (elem_name == "label") { else if (elem_name == "label") {
@ -677,16 +677,16 @@ void WidgetLoader::fillWidgetWithXmlElementAttributesWithChildren(const TiXmlEle
int hspan = cell_hspan ? strtol(cell_hspan, NULL, 10): 1; int hspan = cell_hspan ? strtol(cell_hspan, NULL, 10): 1;
int vspan = cell_vspan ? strtol(cell_vspan, NULL, 10): 1; int vspan = cell_vspan ? strtol(cell_vspan, NULL, 10): 1;
int align = cell_align ? convert_align_value_to_flags(cell_align): 0; int align = cell_align ? convert_align_value_to_flags(cell_align): 0;
Grid* grid = dynamic_cast<Grid*>(widget); auto grid = dynamic_cast<ui::Grid*>(widget);
ASSERT(grid != NULL); ASSERT(grid != nullptr);
grid->addChildInCell(child, hspan, vspan, align); grid->addChildInCell(child, hspan, vspan, align);
} }
// Attach the child in the view // Attach the child in the view
else if (widget->type() == kComboBoxWidget && else if (widget->type() == kComboBoxWidget &&
child->type() == kListItemWidget) { child->type() == kListItemWidget) {
ComboBox* combo = dynamic_cast<ComboBox*>(widget); auto combo = dynamic_cast<ComboBox*>(widget);
ASSERT(combo != NULL); ASSERT(combo != nullptr);
combo->addItem(dynamic_cast<ListItem*>(child)); combo->addItem(dynamic_cast<ListItem*>(child));
} }

View File

@ -10,6 +10,11 @@
#include "doc/tileset.h" #include "doc/tileset.h"
#include "doc/remap.h"
#include "doc/sprite.h"
#include <memory>
namespace doc { namespace doc {
Tileset::Tileset(Sprite* sprite, Tileset::Tileset(Sprite* sprite,
@ -20,6 +25,42 @@ Tileset::Tileset(Sprite* sprite,
, m_grid(grid) , m_grid(grid)
, m_tiles(ntiles) , m_tiles(ntiles)
{ {
ASSERT(sprite);
for (tile_index ti=0; ti<ntiles; ++ti)
m_tiles[ti] = makeEmptyTile();
}
// static
Tileset* Tileset::MakeCopyWithSameImages(const Tileset* tileset)
{
std::unique_ptr<Tileset> copy(
new Tileset(tileset->sprite(),
tileset->grid(),
tileset->size()));
copy->setName(tileset->name());
for (tile_index ti=0; ti<copy->size(); ++ti) {
ImageRef image = tileset->get(ti);
ASSERT(image);
copy->set(ti, image);
}
return copy.release();
}
// static
Tileset* Tileset::MakeCopyCopyingImages(const Tileset* tileset)
{
std::unique_ptr<Tileset> copy(
new Tileset(tileset->sprite(),
tileset->grid(),
tileset->size()));
copy->setName(tileset->name());
for (tile_index ti=0; ti<copy->size(); ++ti) {
ImageRef image = tileset->get(ti);
ASSERT(image);
// TODO can we avoid making a copy of this image
copy->set(ti, ImageRef(Image::createCopy(image.get())));
}
return copy.release();
} }
void Tileset::setOrigin(const gfx::Point& pt) void Tileset::setOrigin(const gfx::Point& pt)
@ -31,15 +72,39 @@ int Tileset::getMemSize() const
{ {
int size = sizeof(Tileset) + m_name.size(); int size = sizeof(Tileset) + m_name.size();
for (auto& img : const_cast<Tileset*>(this)->m_tiles) { for (auto& img : const_cast<Tileset*>(this)->m_tiles) {
if (img) ASSERT(img);
size += img->getMemSize(); size += img->getMemSize();
} }
return size; return size;
} }
void Tileset::resize(const tile_index ntiles) void Tileset::resize(const tile_index ntiles)
{ {
int oldSize = m_tiles.size();
m_tiles.resize(ntiles); m_tiles.resize(ntiles);
for (tile_index ti=oldSize; ti<ntiles; ++ti)
m_tiles[ti] = makeEmptyTile();
}
void Tileset::remap(const Remap& remap)
{
Tiles tmp = m_tiles;
for (tile_index ti=0; ti<size(); ++ti) {
TRACE("m_tiles[%d] = tmp[%d]\n", remap[ti], ti);
ASSERT(remap[ti] >= 0);
ASSERT(remap[ti] < m_tiles.size());
if (remap[ti] >= 0 &&
remap[ti] < m_tiles.size()) {
m_tiles[remap[ti]] = tmp[ti];
}
}
}
ImageRef Tileset::makeEmptyTile()
{
ImageSpec spec = m_sprite->spec();
spec.setSize(m_grid.tileSize());
return ImageRef(Image::create(spec));
} }
void Tileset::setExternal(const std::string& filename, void Tileset::setExternal(const std::string& filename,

View File

@ -18,17 +18,22 @@
namespace doc { namespace doc {
class Remap;
class Sprite; class Sprite;
class Tileset : public Object { class Tileset : public Object {
public: public:
typedef std::vector<ImageRef> Tiles; typedef std::vector<ImageRef> Tiles;
typedef Tiles::iterator iterator; typedef Tiles::iterator iterator;
typedef Tiles::const_iterator const_iterator;
Tileset(Sprite* sprite, Tileset(Sprite* sprite,
const Grid& grid, const Grid& grid,
const tileset_index ntiles); const tileset_index ntiles);
static Tileset* MakeCopyWithSameImages(const Tileset* tileset);
static Tileset* MakeCopyCopyingImages(const Tileset* tileset);
Sprite* sprite() const { return m_sprite; } Sprite* sprite() const { return m_sprite; }
const Grid& grid() const { return m_grid; } const Grid& grid() const { return m_grid; }
void setOrigin(const gfx::Point& pt); void setOrigin(const gfx::Point& pt);
@ -40,8 +45,11 @@ namespace doc {
iterator begin() { return m_tiles.begin(); } iterator begin() { return m_tiles.begin(); }
iterator end() { return m_tiles.end(); } iterator end() { return m_tiles.end(); }
const_iterator begin() const { return m_tiles.begin(); }
const_iterator end() const { return m_tiles.end(); }
tile_index size() const { return tile_index(m_tiles.size()); } tile_index size() const { return tile_index(m_tiles.size()); }
void resize(const tile_index ntiles); void resize(const tile_index ntiles);
void remap(const Remap& remap);
ImageRef get(const tile_index ti) const { ImageRef get(const tile_index ti) const {
if (ti < size()) if (ti < size())
@ -77,6 +85,20 @@ namespace doc {
const std::string& externalFilename() const { return m_external.filename; } const std::string& externalFilename() const { return m_external.filename; }
tileset_index externalTileset() const { return m_external.tileset; } tileset_index externalTileset() const { return m_external.tileset; }
bool operator==(const Tileset& other) const {
// TODO compare the all grid members
return (m_grid.tileSize() == other.m_grid.tileSize() &&
m_tiles == other.m_tiles &&
m_name == other.m_name);
}
bool operator!=(const Tileset& other) const {
return !operator==(other);
}
// Returns a new empty tile with the tileset specs.
ImageRef makeEmptyTile();
private: private:
Sprite* m_sprite; Sprite* m_sprite;
Grid m_grid; Grid m_grid;