mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-17 08:43:11 +00:00
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
|
||||
remap_palette = Remap Palette
|
||||
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]
|
||||
About = About
|
||||
@ -276,6 +281,7 @@ Copy = Copy
|
||||
CopyCel = Copy Cel
|
||||
CopyColors = Copy Colors
|
||||
CopyMerged = Copy Merged
|
||||
CopyTiles = Copy Tiles
|
||||
CropSprite = Crop Sprite
|
||||
Cut = Cut
|
||||
DeselectMask = Deselect Mask
|
||||
@ -357,6 +363,7 @@ MoveColors = Move Colors
|
||||
MoveMask = Move {0} {1}
|
||||
MoveMask_Boundaries = Selection Boundaries
|
||||
MoveMask_Content = Selection Content
|
||||
MoveTiles = Move Tiles
|
||||
NewBrush = New Brush
|
||||
NewFile = New File
|
||||
NewFile_FromClipboard = New File from Clipboard
|
||||
|
@ -447,7 +447,8 @@ add_library(app-lib
|
||||
cmd/move_layer.cpp
|
||||
cmd/patch_cel.cpp
|
||||
cmd/remap_colors.cpp
|
||||
cmd/remap_tiles.cpp
|
||||
cmd/remap_tilemaps.cpp
|
||||
cmd/remap_tileset.cpp
|
||||
cmd/remove_cel.cpp
|
||||
cmd/remove_frame.cpp
|
||||
cmd/remove_layer.cpp
|
||||
@ -539,6 +540,7 @@ add_library(app-lib
|
||||
commands/filters/filter_worker.cpp
|
||||
commands/move_colors_command.cpp
|
||||
commands/move_thing.cpp
|
||||
commands/move_tiles_command.cpp
|
||||
commands/new_params.cpp
|
||||
commands/quick_command.cpp
|
||||
console.cpp
|
||||
|
@ -65,6 +65,7 @@ void ActiveSiteHandler::getActiveSiteForDoc(Doc* doc, Site* site)
|
||||
site->layer(doc::get<doc::Layer>(data.layer));
|
||||
site->frame(data.frame);
|
||||
site->selectedColors(data.selectedColors);
|
||||
site->selectedTiles(data.selectedTiles);
|
||||
}
|
||||
|
||||
void ActiveSiteHandler::setActiveLayerInDoc(Doc* doc, doc::Layer* layer)
|
||||
@ -85,6 +86,12 @@ void ActiveSiteHandler::setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks
|
||||
data.selectedColors = picks;
|
||||
}
|
||||
|
||||
void ActiveSiteHandler::setSelectedTilesInDoc(Doc* doc, const doc::PalettePicks& picks)
|
||||
{
|
||||
Data& data = getData(doc);
|
||||
data.selectedTiles = picks;
|
||||
}
|
||||
|
||||
void ActiveSiteHandler::onAddLayer(DocEvent& ev)
|
||||
{
|
||||
Data& data = getData(ev.document());
|
||||
|
@ -39,6 +39,7 @@ namespace app {
|
||||
void setActiveLayerInDoc(Doc* doc, doc::Layer* layer);
|
||||
void setActiveFrameInDoc(Doc* doc, doc::frame_t frame);
|
||||
void setSelectedColorsInDoc(Doc* doc, const doc::PalettePicks& picks);
|
||||
void setSelectedTilesInDoc(Doc* doc, const doc::PalettePicks& picks);
|
||||
|
||||
private:
|
||||
// DocObserver impl
|
||||
@ -52,6 +53,7 @@ namespace app {
|
||||
doc::ObjectId layer;
|
||||
doc::frame_t frame;
|
||||
doc::PalettePicks selectedColors;
|
||||
doc::PalettePicks selectedTiles;
|
||||
};
|
||||
|
||||
Data& getData(Doc* doc);
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cmd/remap_tiles.h"
|
||||
#include "app/cmd/remap_tilemaps.h"
|
||||
|
||||
#include "doc/cel.h"
|
||||
#include "doc/cels_range.h"
|
||||
@ -24,14 +24,14 @@ namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
RemapTiles::RemapTiles(Tileset* tileset,
|
||||
const Remap& remap)
|
||||
RemapTilemaps::RemapTilemaps(Tileset* tileset,
|
||||
const Remap& remap)
|
||||
: WithTileset(tileset)
|
||||
, m_remap(remap)
|
||||
{
|
||||
}
|
||||
|
||||
void RemapTiles::onExecute()
|
||||
void RemapTilemaps::onExecute()
|
||||
{
|
||||
Tileset* tileset = this->tileset();
|
||||
Sprite* spr = tileset->sprite();
|
||||
@ -39,7 +39,7 @@ void RemapTiles::onExecute()
|
||||
incrementVersions(tileset);
|
||||
}
|
||||
|
||||
void RemapTiles::onUndo()
|
||||
void RemapTilemaps::onUndo()
|
||||
{
|
||||
Tileset* tileset = this->tileset();
|
||||
Sprite* spr = tileset->sprite();
|
||||
@ -47,7 +47,7 @@ void RemapTiles::onUndo()
|
||||
incrementVersions(tileset);
|
||||
}
|
||||
|
||||
void RemapTiles::incrementVersions(Tileset* tileset)
|
||||
void RemapTilemaps::incrementVersions(Tileset* tileset)
|
||||
{
|
||||
Sprite* spr = tileset->sprite();
|
||||
for (const Cel* cel : spr->uniqueCels()) {
|
@ -4,8 +4,8 @@
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_CMD_REMAP_TILES_H_INCLUDED
|
||||
#define APP_CMD_REMAP_TILES_H_INCLUDED
|
||||
#ifndef APP_CMD_REMAP_TILEMAPS_H_INCLUDED
|
||||
#define APP_CMD_REMAP_TILEMAPS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd.h"
|
||||
@ -16,11 +16,11 @@ namespace app {
|
||||
namespace cmd {
|
||||
using namespace doc;
|
||||
|
||||
class RemapTiles : public Cmd
|
||||
, public WithTileset {
|
||||
class RemapTilemaps : public Cmd
|
||||
, public WithTileset {
|
||||
public:
|
||||
RemapTiles(Tileset* tileset,
|
||||
const Remap& remap);
|
||||
RemapTilemaps(Tileset* tileset,
|
||||
const Remap& remap);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
54
src/app/cmd/remap_tileset.cpp
Normal file
54
src/app/cmd/remap_tileset.cpp
Normal 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
|
41
src/app/cmd/remap_tileset.h
Normal file
41
src/app/cmd/remap_tileset.h
Normal 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
|
@ -16,6 +16,7 @@ FOR_EACH_COMMAND(ColorCurve)
|
||||
FOR_EACH_COMMAND(ColorQuantization)
|
||||
FOR_EACH_COMMAND(ConvolutionMatrix)
|
||||
FOR_EACH_COMMAND(CopyColors)
|
||||
FOR_EACH_COMMAND(CopyTiles)
|
||||
FOR_EACH_COMMAND(CropSprite)
|
||||
FOR_EACH_COMMAND(Despeckle)
|
||||
FOR_EACH_COMMAND(ExportSpriteSheet)
|
||||
@ -26,6 +27,7 @@ FOR_EACH_COMMAND(LayerFromBackground)
|
||||
FOR_EACH_COMMAND(LoadPalette)
|
||||
FOR_EACH_COMMAND(MergeDownLayer)
|
||||
FOR_EACH_COMMAND(MoveColors)
|
||||
FOR_EACH_COMMAND(MoveTiles)
|
||||
FOR_EACH_COMMAND(NewFile)
|
||||
FOR_EACH_COMMAND(NewFrame)
|
||||
FOR_EACH_COMMAND(NewLayer)
|
||||
|
85
src/app/commands/move_tiles_command.cpp
Normal file
85
src/app/commands/move_tiles_command.cpp
Normal 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
|
@ -99,6 +99,11 @@ void Context::setSelectedColors(const doc::PalettePicks& picks)
|
||||
onSetSelectedColors(picks);
|
||||
}
|
||||
|
||||
void Context::setSelectedTiles(const doc::PalettePicks& picks)
|
||||
{
|
||||
onSetSelectedTiles(picks);
|
||||
}
|
||||
|
||||
bool Context::hasModifiedDocuments() const
|
||||
{
|
||||
for (auto doc : documents())
|
||||
@ -241,6 +246,12 @@ void Context::onSetSelectedColors(const doc::PalettePicks& 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
|
||||
{
|
||||
if (!m_activeSiteHandler)
|
||||
|
@ -89,6 +89,7 @@ namespace app {
|
||||
void setActiveLayer(doc::Layer* layer);
|
||||
void setActiveFrame(doc::frame_t frame);
|
||||
void setSelectedColors(const doc::PalettePicks& picks);
|
||||
void setSelectedTiles(const doc::PalettePicks& picks);
|
||||
bool hasModifiedDocuments() const;
|
||||
void notifyActiveSiteChanged();
|
||||
|
||||
@ -112,6 +113,7 @@ namespace app {
|
||||
virtual void onSetActiveLayer(doc::Layer* layer);
|
||||
virtual void onSetActiveFrame(const doc::frame_t frame);
|
||||
virtual void onSetSelectedColors(const doc::PalettePicks& picks);
|
||||
virtual void onSetSelectedTiles(const doc::PalettePicks& picks);
|
||||
virtual void onCloseDocument(Doc* doc);
|
||||
|
||||
Doc* lastSelectedDoc() { return m_lastSelectedDoc; }
|
||||
|
@ -116,6 +116,9 @@ void ContextFlags::updateFlagsFromSite(const Site& site)
|
||||
|
||||
if (site.selectedColors().picks() > 0)
|
||||
m_flags |= HasSelectedColors;
|
||||
|
||||
if (site.selectedTiles().picks() > 0)
|
||||
m_flags |= HasSelectedTiles;
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -35,6 +35,7 @@ namespace app {
|
||||
ActiveLayerIsReference = 1 << 13,
|
||||
ActiveLayerIsTilemap = 1 << 14,
|
||||
HasSelectedColors = 1 << 15,
|
||||
HasSelectedTiles = 1 << 16,
|
||||
};
|
||||
|
||||
ContextFlags();
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "doc/layer.h"
|
||||
#include "doc/object_ids.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
@ -37,6 +38,7 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
|
||||
std::vector<frame_t> frames;
|
||||
std::set<ObjectId> cels;
|
||||
std::vector<color_t> colors;
|
||||
std::vector<tile_index> tiles;
|
||||
|
||||
RangeObj(Site& site) {
|
||||
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)
|
||||
colors = site.selectedColors().toVectorOfIndexes();
|
||||
|
||||
if (site.selectedTiles().picks() > 0)
|
||||
tiles = site.selectedTiles().toVectorOfIndexes();
|
||||
}
|
||||
RangeObj(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 {
|
||||
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)
|
||||
@ -128,11 +136,19 @@ int Range_contains(lua_State* L)
|
||||
int Range_containsColor(lua_State* L)
|
||||
{
|
||||
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));
|
||||
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)
|
||||
{
|
||||
auto obj = get_obj<RangeObj>(L, 1);
|
||||
@ -212,6 +228,18 @@ int Range_get_colors(lua_State* L)
|
||||
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)
|
||||
{
|
||||
app::Context* ctx = App::instance()->context();
|
||||
@ -230,10 +258,29 @@ int Range_set_colors(lua_State* L)
|
||||
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[] = {
|
||||
{ "__gc", Range_gc },
|
||||
{ "contains", Range_contains },
|
||||
{ "containsColor", Range_containsColor },
|
||||
{ "containsTile", Range_containsTile },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
@ -247,6 +294,7 @@ const Property Range_properties[] = {
|
||||
{ "images", Range_get_images, nullptr },
|
||||
{ "editableImages", Range_get_editableImages, nullptr },
|
||||
{ "colors", Range_get_colors, Range_set_colors },
|
||||
{ "tiles", Range_get_tiles, Range_set_tiles },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -91,6 +91,13 @@ namespace app {
|
||||
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; }
|
||||
doc::SelectedObjects& selectedSlices() { return m_selectedSlices; }
|
||||
void selectedSlices(const doc::SelectedObjects& set) {
|
||||
@ -116,6 +123,7 @@ namespace app {
|
||||
doc::frame_t m_frame;
|
||||
DocRange m_range;
|
||||
doc::PalettePicks m_selectedColors;
|
||||
doc::PalettePicks m_selectedTiles;
|
||||
doc::SelectedObjects m_selectedSlices;
|
||||
TilesetMode m_tilesetMode;
|
||||
};
|
||||
|
@ -5,7 +5,7 @@
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#define COLOR_BAR_TRACE(...)
|
||||
#define COLOR_BAR_TRACE(...) // TRACE
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
@ -16,6 +16,8 @@
|
||||
#include "app/app.h"
|
||||
#include "app/app_menus.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/replace_image.h"
|
||||
#include "app/cmd/set_palette.h"
|
||||
@ -50,6 +52,7 @@
|
||||
#include "app/ui/timeline/timeline.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/cel_ops.h"
|
||||
#include "app/util/clipboard.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/scoped_value.h"
|
||||
@ -64,6 +67,7 @@
|
||||
#include "doc/rgbmap.h"
|
||||
#include "doc/sort_palette.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tileset_hash_table.h"
|
||||
#include "os/surface.h"
|
||||
#include "ui/alert.h"
|
||||
#include "ui/graphics.h"
|
||||
@ -146,7 +150,8 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
|
||||
, m_splitter(Splitter::ByPercentage, VERTICAL)
|
||||
, m_paletteView(true, PaletteView::FgBgColors, 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_tintShadeTone(nullptr)
|
||||
, m_spectrum(nullptr)
|
||||
@ -199,11 +204,13 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
|
||||
m_scrollableTilesView.setExpansive(true);
|
||||
|
||||
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_scrollableTilesView);
|
||||
m_palettePlaceholder.addChild(&m_remapButton);
|
||||
m_palettePlaceholder.addChild(&m_remapPalButton);
|
||||
m_palettePlaceholder.addChild(&m_remapTilesButton);
|
||||
m_splitter.setId("palette_spectrum_splitter");
|
||||
m_splitter.setPosition(80);
|
||||
m_splitter.setExpansive(true);
|
||||
@ -249,7 +256,8 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
|
||||
m_fgColor.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.BeforeChange.connect(&ColorBar::onFgColorButtonBeforeChange, 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_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_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);
|
||||
KeyboardShortcuts::instance()->UserChange.connect(
|
||||
base::Bind<void>(&ColorBar::setupTooltips, this, tooltipManager));
|
||||
@ -542,7 +551,8 @@ void ColorBar::onActiveSiteChange(const Site& site)
|
||||
if (m_lastDocument)
|
||||
m_lastDocument->add_observer(this);
|
||||
|
||||
hideRemap();
|
||||
hideRemapPal();
|
||||
hideRemapTiles();
|
||||
}
|
||||
|
||||
bool isTilemap = false;
|
||||
@ -599,7 +609,7 @@ void ColorBar::onAppPaletteChange()
|
||||
updateWarningIcon(m_bgColor.getColor(), m_bgWarningIcon);
|
||||
}
|
||||
|
||||
void ColorBar::onFocusPaletteView(ui::Message* msg)
|
||||
void ColorBar::onFocusPaletteOrTilesView(ui::Message* msg)
|
||||
{
|
||||
App::instance()->inputChain().prioritize(this, msg);
|
||||
}
|
||||
@ -608,8 +618,9 @@ void ColorBar::onBeforeExecuteCommand(CommandExecutionEvent& ev)
|
||||
{
|
||||
if (ev.command()->id() == CommandId::SetPalette() ||
|
||||
ev.command()->id() == CommandId::LoadPalette() ||
|
||||
ev.command()->id() == CommandId::ColorQuantization())
|
||||
showRemap();
|
||||
ev.command()->id() == CommandId::ColorQuantization()) {
|
||||
showRemapPal();
|
||||
}
|
||||
}
|
||||
|
||||
void ColorBar::onAfterExecuteCommand(CommandExecutionEvent& ev)
|
||||
@ -619,12 +630,19 @@ void ColorBar::onAfterExecuteCommand(CommandExecutionEvent& ev)
|
||||
invalidate();
|
||||
|
||||
// If the sprite isn't Indexed anymore (e.g. because we've just
|
||||
// undone a "RGB -> Indexed" conversion), we hide the "Remap"
|
||||
// button.
|
||||
// undone a "RGB -> Indexed" conversion), we hide the "Remap
|
||||
// Palette" button.
|
||||
Site site = UIContext::instance()->activeSite();
|
||||
if (site.sprite() &&
|
||||
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));
|
||||
}
|
||||
|
||||
void ColorBar::onRemapButtonClick()
|
||||
void ColorBar::onRemapPalButtonClick()
|
||||
{
|
||||
ASSERT(m_oldPalette);
|
||||
|
||||
@ -741,7 +759,59 @@ void ColorBar::onRemapButtonClick()
|
||||
tx.commit();
|
||||
}
|
||||
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) {
|
||||
Console::showException(e);
|
||||
@ -752,10 +822,6 @@ void ColorBar::onPaletteViewIndexChange(int index, ui::MouseButtons buttons)
|
||||
{
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
showRemap();
|
||||
showRemapPal();
|
||||
|
||||
try {
|
||||
ContextWriter writer(UIContext::instance());
|
||||
ContextWriter writer(UIContext::instance(), 500);
|
||||
Sprite* sprite = writer.sprite();
|
||||
frame_t frame = writer.frame();
|
||||
if (sprite &&
|
||||
@ -884,7 +950,7 @@ app::Color ColorBar::onPaletteViewGetBackgroundIndex()
|
||||
return getBgColor();
|
||||
}
|
||||
|
||||
void ColorBar::onPaletteViewClearTiles(const doc::PalettePicks& picks)
|
||||
void ColorBar::onTilesViewClearTiles(const doc::PalettePicks& picks)
|
||||
{
|
||||
try {
|
||||
ContextWriter writer(UIContext::instance(), 500);
|
||||
@ -894,9 +960,13 @@ void ColorBar::onPaletteViewClearTiles(const doc::PalettePicks& picks)
|
||||
auto tileset = m_tilesView.tileset();
|
||||
|
||||
Tx tx(writer.context(), "Clear Tiles", ModifyDocument);
|
||||
for (auto ti : picks)
|
||||
tx(new cmd::RemoveTile(tileset, ti));
|
||||
for (doc::tile_index ti=0; ti<picks.size(); ++ti) {
|
||||
if (picks[ti])
|
||||
tx(new cmd::RemoveTile(tileset, ti));
|
||||
}
|
||||
tx.commit();
|
||||
|
||||
update_screen_for_document(writer.document());
|
||||
}
|
||||
}
|
||||
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()
|
||||
{
|
||||
COLOR_BAR_TRACE("ColorBar::onFgColorChangeFromPreferences() -> %s\n",
|
||||
@ -1132,26 +1274,51 @@ void ColorBar::setAscending(bool ascending)
|
||||
m_ascending = ascending;
|
||||
}
|
||||
|
||||
void ColorBar::showRemap()
|
||||
void ColorBar::showRemapPal()
|
||||
{
|
||||
Site site = UIContext::instance()->activeSite();
|
||||
if (site.sprite() &&
|
||||
site.sprite()->pixelFormat() == IMAGE_INDEXED) {
|
||||
if (!m_oldPalette) {
|
||||
m_oldPalette.reset(new Palette(*get_current_palette()));
|
||||
m_remapButton.setVisible(true);
|
||||
m_remapPalButton.setVisible(true);
|
||||
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)
|
||||
return;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1162,34 +1329,46 @@ void ColorBar::onNewInputPriority(InputChainElement* element,
|
||||
msg && (msg->ctrlPressed() || msg->shiftPressed()))
|
||||
return;
|
||||
|
||||
if (element != this)
|
||||
m_paletteView.deselect();
|
||||
if (element != this) {
|
||||
if (m_tilesMode)
|
||||
m_tilesView.deselect();
|
||||
else
|
||||
m_paletteView.deselect();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return (m_paletteView.getSelectedEntriesCount() > 0);
|
||||
return onCanCut(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)
|
||||
{
|
||||
return (m_paletteView.getSelectedEntriesCount() > 0);
|
||||
return onCanCut(ctx);
|
||||
}
|
||||
|
||||
bool ColorBar::onCut(Context* ctx)
|
||||
{
|
||||
if (m_tilesMode)
|
||||
if (m_tilesMode) {
|
||||
m_tilesView.cutToClipboard();
|
||||
showRemapTiles();
|
||||
}
|
||||
else
|
||||
m_paletteView.cutToClipboard();
|
||||
return true;
|
||||
@ -1206,8 +1385,10 @@ bool ColorBar::onCopy(Context* ctx)
|
||||
|
||||
bool ColorBar::onPaste(Context* ctx)
|
||||
{
|
||||
if (m_tilesMode)
|
||||
if (m_tilesMode) {
|
||||
m_tilesView.pasteFromClipboard();
|
||||
showRemapTiles();
|
||||
}
|
||||
else
|
||||
m_paletteView.pasteFromClipboard();
|
||||
return true;
|
||||
@ -1215,8 +1396,10 @@ bool ColorBar::onPaste(Context* ctx)
|
||||
|
||||
bool ColorBar::onClear(Context* ctx)
|
||||
{
|
||||
if (m_tilesMode)
|
||||
if (m_tilesMode) {
|
||||
m_tilesView.clearSelection();
|
||||
showRemapTiles();
|
||||
}
|
||||
else
|
||||
m_paletteView.clearSelection();
|
||||
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::PRESETS), Strings::color_bar_presets(), 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(
|
||||
m_tilesButton.getItem(0),
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "doc/object_id.h"
|
||||
#include "doc/pixel_format.h"
|
||||
#include "doc/sort_palette.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "obs/connection.h"
|
||||
#include "obs/signal.h"
|
||||
#include "ui/box.h"
|
||||
@ -113,7 +114,7 @@ namespace app {
|
||||
|
||||
protected:
|
||||
void onAppPaletteChange();
|
||||
void onFocusPaletteView(ui::Message* msg);
|
||||
void onFocusPaletteOrTilesView(ui::Message* msg);
|
||||
void onBeforeExecuteCommand(CommandExecutionEvent& ev);
|
||||
void onAfterExecuteCommand(CommandExecutionEvent& ev);
|
||||
void onSwitchPalEditMode();
|
||||
@ -121,7 +122,8 @@ namespace app {
|
||||
void onTilesButtonClick();
|
||||
void onTilesetModeButtonClick();
|
||||
void onTilesetOptionsClick();
|
||||
void onRemapButtonClick();
|
||||
void onRemapPalButtonClick();
|
||||
void onRemapTilesButtonClick();
|
||||
void onPaletteIndexChange(PaletteIndexChangeEvent& ev);
|
||||
void onFgColorChangeFromPreferences();
|
||||
void onBgColorChangeFromPreferences();
|
||||
@ -144,11 +146,20 @@ namespace app {
|
||||
void onPaletteViewPasteColors(const Palette* fromPal, const doc::PalettePicks& from, const doc::PalettePicks& to) override;
|
||||
app::Color onPaletteViewGetForegroundIndex() 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:
|
||||
void showRemap();
|
||||
void hideRemap();
|
||||
void showRemapPal();
|
||||
void showRemapTiles();
|
||||
void hideRemapPal();
|
||||
void hideRemapTiles();
|
||||
void setPalette(const doc::Palette* newPalette, const std::string& actionText);
|
||||
void setTransparentIndex(int index);
|
||||
void updateWarningIcon(const app::Color& color, ui::Button* warningIcon);
|
||||
@ -184,7 +195,8 @@ namespace app {
|
||||
ScrollableView m_scrollableTilesView;
|
||||
PaletteView m_paletteView;
|
||||
PaletteView m_tilesView;
|
||||
ui::Button m_remapButton;
|
||||
ui::Button m_remapPalButton;
|
||||
ui::Button m_remapTilesButton;
|
||||
ColorSelector m_selector;
|
||||
ColorTintShadeTone* m_tintShadeTone;
|
||||
ColorSpectrum* m_spectrum;
|
||||
@ -206,6 +218,7 @@ namespace app {
|
||||
bool m_fromBgButton;
|
||||
|
||||
std::unique_ptr<doc::Palette> m_oldPalette;
|
||||
std::unique_ptr<doc::Tileset> m_oldTileset;
|
||||
Doc* m_lastDocument;
|
||||
doc::ObjectId m_lastTilesetId;
|
||||
bool m_ascending;
|
||||
|
@ -5,6 +5,8 @@
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#define PAL_TRACE(...) // TRACEARGS
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
@ -52,6 +54,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <set>
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -67,6 +70,15 @@ public:
|
||||
virtual void activeSiteChange(const Site& site, doc::PalettePicks& picks) = 0;
|
||||
virtual void clearSelection(PaletteView* paletteView,
|
||||
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 showDragInfoInStatusBar(StatusBar* statusBar, bool copy, int destIndex, int newSize) = 0;
|
||||
virtual void showResizeInfoInStatusBar(StatusBar* statusBar, int newSize) = 0;
|
||||
@ -103,7 +115,39 @@ public:
|
||||
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 {
|
||||
statusBar->showColor(
|
||||
@ -175,7 +219,32 @@ public:
|
||||
}
|
||||
void clearSelection(PaletteView* paletteView,
|
||||
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 {
|
||||
statusBar->setStatusText(
|
||||
@ -554,9 +623,7 @@ bool PaletteView::onProcessMessage(Message* msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Emit signal
|
||||
if (m_delegate)
|
||||
m_delegate->onPaletteViewIndexChange(idx, buttons);
|
||||
m_adapter->selectIndex(this, idx, buttons);
|
||||
}
|
||||
}
|
||||
|
||||
@ -590,11 +657,8 @@ bool PaletteView::onProcessMessage(Message* msg)
|
||||
case State::RESIZING_PALETTE:
|
||||
if (m_hot.part == Hit::COLOR ||
|
||||
m_hot.part == Hit::POSSIBLE_COLOR) {
|
||||
int newPalSize = MAX(1, m_hot.color);
|
||||
Palette newPalette(*currentPalette());
|
||||
newPalette.resize(newPalSize);
|
||||
setNewPalette(currentPalette(), &newPalette,
|
||||
PaletteViewModification::RESIZE);
|
||||
int newSize = std::max(1, m_hot.color);
|
||||
m_adapter->resizePalette(this, newSize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -988,17 +1052,11 @@ PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos)
|
||||
|
||||
void PaletteView::dropColors(int beforeIndex)
|
||||
{
|
||||
Palette palette(*currentPalette());
|
||||
Palette newPalette(palette);
|
||||
move_or_copy_palette_colors(
|
||||
palette,
|
||||
newPalette,
|
||||
m_selectedEntries,
|
||||
m_currentEntry,
|
||||
beforeIndex,
|
||||
m_copy);
|
||||
setNewPalette(&palette, &newPalette,
|
||||
PaletteViewModification::DRAGANDDROP);
|
||||
m_adapter->dropColors(this,
|
||||
m_selectedEntries,
|
||||
m_currentEntry,
|
||||
beforeIndex,
|
||||
m_copy);
|
||||
}
|
||||
|
||||
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) { }
|
||||
virtual app::Color onPaletteViewGetForegroundIndex() { 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 PaletteViewAdapter;
|
||||
class TilesetViewAdapter;
|
||||
|
||||
class PaletteView : public ui::Widget
|
||||
, public MarchingAnts
|
||||
, public IColorSource
|
||||
, public ContextObserver {
|
||||
friend class PaletteViewAdapter;
|
||||
friend class TilesetViewAdapter;
|
||||
public:
|
||||
enum PaletteViewStyle {
|
||||
SelectOneColor,
|
||||
|
@ -15,7 +15,8 @@
|
||||
#include "app/cmd/clear_cel.h"
|
||||
#include "app/cmd/clear_mask.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/replace_image.h"
|
||||
#include "app/cmd/set_cel_position.h"
|
||||
@ -46,7 +47,7 @@
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
#define OPS_TRACE(...)
|
||||
#define OPS_TRACE(...) // TRACE
|
||||
|
||||
namespace app {
|
||||
|
||||
@ -640,8 +641,46 @@ void remove_unused_tiles_from_tileset(
|
||||
OPS_TRACE(" - remap tile[%d] -> %d\n", ti, remap[ti]);
|
||||
}
|
||||
#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
|
||||
|
@ -23,6 +23,7 @@ namespace doc {
|
||||
class Cel;
|
||||
class Layer;
|
||||
class LayerTilemap;
|
||||
class PalettePicks;
|
||||
class Sprite;
|
||||
class Tileset;
|
||||
}
|
||||
@ -77,6 +78,20 @@ namespace app {
|
||||
doc::Tileset* tileset,
|
||||
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
|
||||
|
||||
#endif
|
||||
|
@ -99,6 +99,7 @@ namespace clipboard {
|
||||
using namespace doc;
|
||||
|
||||
static std::shared_ptr<Palette> clipboard_palette;
|
||||
static std::shared_ptr<Tileset> clipboard_tiles;
|
||||
static PalettePicks clipboard_picks;
|
||||
static ImageRef clipboard_image;
|
||||
static std::shared_ptr<Mask> clipboard_mask;
|
||||
@ -226,6 +227,8 @@ ClipboardFormat get_current_format()
|
||||
return ClipboardDocRange;
|
||||
else if (clipboard_palette && clipboard_picks.picks())
|
||||
return ClipboardPaletteEntries;
|
||||
else if (clipboard_tiles && clipboard_picks.picks())
|
||||
return ClipboardTiles;
|
||||
else
|
||||
return ClipboardNone;
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ namespace app {
|
||||
ClipboardImage,
|
||||
ClipboardDocRange,
|
||||
ClipboardPaletteEntries,
|
||||
ClipboardTiles,
|
||||
};
|
||||
|
||||
// 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");
|
||||
|
||||
if (columns != NULL) {
|
||||
widget = new Grid(strtol(columns, NULL, 10),
|
||||
same_width_columns);
|
||||
widget = new ui::Grid(strtol(columns, NULL, 10),
|
||||
same_width_columns);
|
||||
}
|
||||
}
|
||||
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 vspan = cell_vspan ? strtol(cell_vspan, NULL, 10): 1;
|
||||
int align = cell_align ? convert_align_value_to_flags(cell_align): 0;
|
||||
Grid* grid = dynamic_cast<Grid*>(widget);
|
||||
ASSERT(grid != NULL);
|
||||
auto grid = dynamic_cast<ui::Grid*>(widget);
|
||||
ASSERT(grid != nullptr);
|
||||
|
||||
grid->addChildInCell(child, hspan, vspan, align);
|
||||
}
|
||||
// Attach the child in the view
|
||||
else if (widget->type() == kComboBoxWidget &&
|
||||
child->type() == kListItemWidget) {
|
||||
ComboBox* combo = dynamic_cast<ComboBox*>(widget);
|
||||
ASSERT(combo != NULL);
|
||||
auto combo = dynamic_cast<ComboBox*>(widget);
|
||||
ASSERT(combo != nullptr);
|
||||
|
||||
combo->addItem(dynamic_cast<ListItem*>(child));
|
||||
}
|
||||
|
@ -10,6 +10,11 @@
|
||||
|
||||
#include "doc/tileset.h"
|
||||
|
||||
#include "doc/remap.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace doc {
|
||||
|
||||
Tileset::Tileset(Sprite* sprite,
|
||||
@ -20,6 +25,42 @@ Tileset::Tileset(Sprite* sprite,
|
||||
, m_grid(grid)
|
||||
, 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)
|
||||
@ -31,15 +72,39 @@ int Tileset::getMemSize() const
|
||||
{
|
||||
int size = sizeof(Tileset) + m_name.size();
|
||||
for (auto& img : const_cast<Tileset*>(this)->m_tiles) {
|
||||
if (img)
|
||||
size += img->getMemSize();
|
||||
ASSERT(img);
|
||||
size += img->getMemSize();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void Tileset::resize(const tile_index ntiles)
|
||||
{
|
||||
int oldSize = m_tiles.size();
|
||||
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,
|
||||
|
@ -18,17 +18,22 @@
|
||||
|
||||
namespace doc {
|
||||
|
||||
class Remap;
|
||||
class Sprite;
|
||||
|
||||
class Tileset : public Object {
|
||||
public:
|
||||
typedef std::vector<ImageRef> Tiles;
|
||||
typedef Tiles::iterator iterator;
|
||||
typedef Tiles::const_iterator const_iterator;
|
||||
|
||||
Tileset(Sprite* sprite,
|
||||
const Grid& grid,
|
||||
const tileset_index ntiles);
|
||||
|
||||
static Tileset* MakeCopyWithSameImages(const Tileset* tileset);
|
||||
static Tileset* MakeCopyCopyingImages(const Tileset* tileset);
|
||||
|
||||
Sprite* sprite() const { return m_sprite; }
|
||||
const Grid& grid() const { return m_grid; }
|
||||
void setOrigin(const gfx::Point& pt);
|
||||
@ -40,8 +45,11 @@ namespace doc {
|
||||
|
||||
iterator begin() { return m_tiles.begin(); }
|
||||
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()); }
|
||||
void resize(const tile_index ntiles);
|
||||
void remap(const Remap& remap);
|
||||
|
||||
ImageRef get(const tile_index ti) const {
|
||||
if (ti < size())
|
||||
@ -77,6 +85,20 @@ namespace doc {
|
||||
const std::string& externalFilename() const { return m_external.filename; }
|
||||
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:
|
||||
Sprite* m_sprite;
|
||||
Grid m_grid;
|
||||
|
Loading…
x
Reference in New Issue
Block a user