mirror of https://github.com/aseprite/aseprite.git
				
				
				
			Add possibility to move tiles w/drag and drop + remap tiles
This commit is contained in:
		
							parent
							
								
									8db5b3fb4e
								
							
						
					
					
						commit
						e792d4e078
					
				|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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()); | ||||||
|  |  | ||||||
|  | @ -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); | ||||||
|  |  | ||||||
|  | @ -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()) { | ||||||
|  | @ -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; | ||||||
|  | @ -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
 | ||||||
|  | @ -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 | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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
 | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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; } | ||||||
|  |  | ||||||
|  | @ -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
 | ||||||
|  |  | ||||||
|  | @ -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(); | ||||||
|  |  | ||||||
|  | @ -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 } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  | @ -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), | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -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, | ||||||
|  |  | ||||||
|  | @ -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, | ||||||
|  |  | ||||||
|  | @ -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
 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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).
 | ||||||
|  |  | ||||||
|  | @ -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)); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|  | @ -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, | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue