diff --git a/data/pref.xml b/data/pref.xml
index 58d079500..de179659b 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -212,6 +212,8 @@
+
+
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 1910efa42..4c901cf73 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -388,6 +388,7 @@ if(ENABLE_UI)
ui/tabs.cpp
ui/tag_window.cpp
ui/task_widget.cpp
+ ui/tile_button.cpp
ui/tileset_selector.cpp
ui/timeline/ani_controls.cpp
ui/timeline/timeline.cpp
diff --git a/src/app/color_picker.cpp b/src/app/color_picker.cpp
index effc9825f..a5ce62e47 100644
--- a/src/app/color_picker.cpp
+++ b/src/app/color_picker.cpp
@@ -96,8 +96,9 @@ bool get_cel_pixel(const Cel* cel,
}
ColorPicker::ColorPicker()
- : m_alpha(0)
- , m_layer(NULL)
+ : m_tile(doc::tile_i_notile)
+ , m_alpha(0)
+ , m_layer(nullptr)
{
}
@@ -136,8 +137,8 @@ void ColorPicker::pickColor(const Site& site,
if (!cels.empty()) {
const gfx::Point tilePos = site.grid().canvasToTile(gfx::Point(pos));
if (cels.front()->image()->bounds().contains(tilePos)) {
- m_color = app::Color::fromIndex(
- doc::get_pixel(cels.front()->image(), tilePos.x, tilePos.y));
+ m_tile = doc::get_pixel(cels.front()->image(), tilePos.x, tilePos.y);
+ m_color = app::Color::fromIndex(m_tile);
}
}
}
@@ -160,8 +161,8 @@ void ColorPicker::pickColor(const Site& site,
if (site.tilemapMode() == TilemapMode::Tiles) {
const gfx::Point tilePos = site.grid().canvasToTile(gfx::Point(pos));
if (cel->image()->bounds().contains(tilePos)) {
- m_color = app::Color::fromIndex(
- doc::get_pixel(cel->image(), tilePos.x, tilePos.y));
+ m_tile = doc::get_pixel(cel->image(), tilePos.x, tilePos.y);
+ m_color = app::Color::fromIndex(m_tile);
}
}
else if (site.tilemapMode() == TilemapMode::Pixels) {
diff --git a/src/app/color_picker.h b/src/app/color_picker.h
index 4fdb34fdc..5103f12a0 100644
--- a/src/app/color_picker.h
+++ b/src/app/color_picker.h
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -10,6 +11,7 @@
#include "app/color.h"
#include "doc/layer.h"
+#include "doc/tile.h"
#include "gfx/point.h"
namespace render {
@@ -35,11 +37,13 @@ namespace app {
const Mode mode);
app::Color color() const { return m_color; }
+ doc::tile_t tile() const { return m_tile; }
int alpha() const { return m_alpha; }
doc::Layer* layer() const { return m_layer; }
private:
app::Color m_color;
+ doc::tile_t m_tile;
int m_alpha;
doc::Layer* m_layer;
};
diff --git a/src/app/commands/cmd_eyedropper.cpp b/src/app/commands/cmd_eyedropper.cpp
index 42cb908e7..287590e35 100644
--- a/src/app/commands/cmd_eyedropper.cpp
+++ b/src/app/commands/cmd_eyedropper.cpp
@@ -37,7 +37,8 @@ EyedropperCommand::EyedropperCommand()
void EyedropperCommand::pickSample(const Site& site,
const gfx::PointF& pixelPos,
const render::Projection& proj,
- app::Color& color)
+ app::Color& color,
+ doc::tile_t& tile)
{
// Check if we've to grab alpha channel or the merged color.
Preferences& pref = Preferences::instance();
@@ -60,13 +61,12 @@ void EyedropperCommand::pickSample(const Site& site,
app::gen::EyedropperChannel channel =
pref.eyedropper.channel();
- app::Color picked = picker.color();
-
if (site.tilemapMode() == TilemapMode::Tiles) {
- color = app::Color::fromIndex(picked.getIndex());
+ tile = picker.tile();
return;
}
+ app::Color picked = picker.color();
switch (channel) {
case app::gen::EyedropperChannel::COLOR_ALPHA:
color = picked;
@@ -218,18 +218,30 @@ void EyedropperCommand::executeOnMousePos(Context* context,
DisableColorBarEditMode disable;
Preferences& pref = Preferences::instance();
app::Color color =
- foreground ? pref.colorBar.fgColor():
- pref.colorBar.bgColor();
+ (foreground ? pref.colorBar.fgColor():
+ pref.colorBar.bgColor());
+ doc::tile_t tile =
+ (foreground ? pref.colorBar.fgTile():
+ pref.colorBar.bgTile());
- pickSample(editor->getSite(),
+ Site site = editor->getSite();
+ pickSample(site,
pixelPos,
editor->projection(),
- color);
+ color, tile);
- if (foreground)
- pref.colorBar.fgColor(color);
- else
- pref.colorBar.bgColor(color);
+ if (site.tilemapMode() == TilemapMode::Tiles) {
+ if (foreground)
+ pref.colorBar.fgTile(tile);
+ else
+ pref.colorBar.bgTile(tile);
+ }
+ else {
+ if (foreground)
+ pref.colorBar.fgColor(color);
+ else
+ pref.colorBar.bgColor(color);
+ }
}
Command* CommandFactory::createEyedropperCommand()
diff --git a/src/app/commands/cmd_eyedropper.h b/src/app/commands/cmd_eyedropper.h
index d25201671..2d1990946 100644
--- a/src/app/commands/cmd_eyedropper.h
+++ b/src/app/commands/cmd_eyedropper.h
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -28,7 +29,8 @@ namespace app {
void pickSample(const Site& site,
const gfx::PointF& pixelPos,
const render::Projection& proj,
- app::Color& color);
+ app::Color& color,
+ doc::tile_t& tile);
void executeOnMousePos(Context* context,
Editor* editor,
diff --git a/src/app/modules/gfx.cpp b/src/app/modules/gfx.cpp
index 50d346bbb..685e978e3 100644
--- a/src/app/modules/gfx.cpp
+++ b/src/app/modules/gfx.cpp
@@ -17,8 +17,10 @@
#include "app/console.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
+#include "app/site.h"
#include "app/ui/editor/editor.h"
#include "app/ui/skin/skin_theme.h"
+#include "app/util/conversion_to_surface.h"
#include "doc/blend_funcs.h"
#include "doc/image.h"
#include "doc/palette.h"
@@ -26,6 +28,7 @@
#include "gfx/point.h"
#include "gfx/rect.h"
#include "os/surface.h"
+#include "os/system.h"
#include "ui/intern.h"
#include "ui/system.h"
#include "ui/theme.h"
@@ -181,6 +184,80 @@ void draw_color_button(ui::Graphics* g,
}
}
+void draw_tile(ui::Graphics* g,
+ const Rect& rc,
+ const Site& site,
+ doc::tile_t tile)
+{
+ if (rc.w < 1 || rc.h < 1)
+ return;
+
+ if (rc.w == rc.h)
+ draw_checked_grid(g, rc, gfx::Size(rc.w/2, rc.h/2));
+ else
+ draw_checked_grid(g, rc, gfx::Size(rc.w/4, rc.h/2));
+
+ if (tile == doc::tile_i_notile)
+ return;
+
+ doc::Tileset* ts = site.tileset();
+ if (!ts)
+ return;
+
+ doc::tile_index ti = doc::tile_geti(tile);
+ if (ti < 0 || ti >= ts->size())
+ return;
+
+ doc::ImageRef tileImage = ts->get(ti);
+ if (!tileImage)
+ return;
+
+ const int w = tileImage->width();
+ const int h = tileImage->height();
+ os::SurfaceRef surface = os::instance()->makeRgbaSurface(w, h);
+ convert_image_to_surface(tileImage.get(), get_current_palette(),
+ surface.get(), 0, 0, 0, 0, w, h);
+ g->drawRgbaSurface(surface.get(), gfx::Rect(0, 0, w, h), rc);
+}
+
+void draw_tile_button(ui::Graphics* g,
+ const gfx::Rect& rc,
+ const Site& site,
+ doc::tile_t tile,
+ const bool hot,
+ const bool drag)
+{
+ SkinTheme* theme = SkinTheme::instance();
+ int scale = ui::guiscale();
+
+ // Draw background (the tile)
+ draw_tile(g,
+ Rect(rc.x+1*scale,
+ rc.y+1*scale,
+ rc.w-2*scale,
+ rc.h-2*scale),
+ site, tile);
+
+ // Draw opaque border
+ theme->drawRect(
+ g, rc,
+ theme->parts.colorbar0()->bitmapNW(),
+ theme->parts.colorbar0()->bitmapN(),
+ theme->parts.colorbar1()->bitmapNE(),
+ theme->parts.colorbar1()->bitmapE(),
+ theme->parts.colorbar3()->bitmapSE(),
+ theme->parts.colorbar2()->bitmapS(),
+ theme->parts.colorbar2()->bitmapSW(),
+ theme->parts.colorbar0()->bitmapW());
+
+ // Draw hot
+ if (hot) {
+ theme->drawRect(
+ g, gfx::Rect(rc.x, rc.y, rc.w, rc.h-1 - 1*scale),
+ theme->parts.colorbarSelection().get());
+ }
+}
+
void draw_alpha_slider(ui::Graphics* g,
const gfx::Rect& rc,
const app::Color& color)
diff --git a/src/app/modules/gfx.h b/src/app/modules/gfx.h
index 52df2bd5b..f45a47685 100644
--- a/src/app/modules/gfx.h
+++ b/src/app/modules/gfx.h
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2019 Igara Studio S.A.
+// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -26,6 +26,7 @@ namespace ui {
}
namespace app {
+ class Site;
void draw_checked_grid(ui::Graphics* g,
const gfx::Rect& rc,
@@ -48,6 +49,18 @@ namespace app {
const bool hot,
const bool drag);
+ void draw_tile(ui::Graphics* g,
+ const gfx::Rect& rc,
+ const Site& site,
+ const doc::tile_t tile);
+
+ void draw_tile_button(ui::Graphics* g,
+ const gfx::Rect& rc,
+ const Site& site,
+ doc::tile_t tile,
+ const bool hot,
+ const bool drag);
+
void draw_alpha_slider(ui::Graphics* g,
const gfx::Rect& rc,
const app::Color& color);
diff --git a/src/app/script/values.cpp b/src/app/script/values.cpp
index a90890102..dd9e843d2 100644
--- a/src/app/script/values.cpp
+++ b/src/app/script/values.cpp
@@ -143,6 +143,19 @@ app::tools::InkType get_value_from_lua(lua_State* L, int index) {
return (app::tools::InkType)lua_tointeger(L, index);
}
+// ----------------------------------------------------------------------
+// doc::tile_t
+
+template<>
+void push_value_to_lua(lua_State* L, const doc::tile_t& value) {
+ lua_pushinteger(L, value);
+}
+
+template<>
+doc::tile_t get_value_from_lua(lua_State* L, int index) {
+ return lua_tointeger(L, index);
+}
+
// ----------------------------------------------------------------------
// enums
diff --git a/src/app/site.cpp b/src/app/site.cpp
index 81f4b49f9..bd7decbeb 100644
--- a/src/app/site.cpp
+++ b/src/app/site.cpp
@@ -74,6 +74,14 @@ void Site::range(const DocRange& range)
}
}
+doc::Tileset* Site::tileset() const
+{
+ if (m_layer && m_layer->isTilemap())
+ return static_cast(m_layer)->tileset();
+ else
+ return nullptr;
+}
+
Grid Site::grid() const
{
if (m_layer && m_layer->isTilemap()) {
diff --git a/src/app/site.h b/src/app/site.h
index db741279e..19bd44232 100644
--- a/src/app/site.h
+++ b/src/app/site.h
@@ -25,6 +25,7 @@ namespace doc {
class Palette;
class RgbMap;
class Sprite;
+ class Tileset;
} // namespace doc
namespace app {
@@ -106,6 +107,7 @@ namespace app {
doc::Palette* palette() const;
doc::RgbMap* rgbMap() const;
+ doc::Tileset* tileset() const;
doc::Grid grid() const;
gfx::Rect gridBounds() const;
diff --git a/src/app/ui/color_bar.cpp b/src/app/ui/color_bar.cpp
index c6efee5dc..fc1ac39e0 100644
--- a/src/app/ui/color_bar.cpp
+++ b/src/app/ui/color_bar.cpp
@@ -205,6 +205,7 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
m_scrollableTilesView.setExpansive(true);
m_scrollableTilesView.setVisible(false);
+ m_tilesHelpers.setVisible(false);
m_remapPalButton.setVisible(false);
m_remapTilesButton.setVisible(false);
@@ -243,14 +244,22 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
addChild(&m_tilesHBox);
addChild(&m_splitter);
+ addChild(&m_colorHelpers);
+ addChild(&m_tilesHelpers);
+
HBox* fgBox = new HBox;
HBox* bgBox = new HBox;
fgBox->addChild(&m_fgColor);
fgBox->addChild(m_fgWarningIcon);
bgBox->addChild(&m_bgColor);
bgBox->addChild(m_bgWarningIcon);
- addChild(fgBox);
- addChild(bgBox);
+ m_colorHelpers.addChild(fgBox);
+ m_colorHelpers.addChild(bgBox);
+
+ m_tilesHelpers.addChild(new BoxFiller);
+ m_tilesHelpers.addChild(&m_fgTile);
+ m_tilesHelpers.addChild(&m_bgTile);
+ m_tilesHelpers.addChild(new BoxFiller);
m_fgColor.setId("fg_color");
m_bgColor.setId("bg_color");
@@ -336,6 +345,8 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
m_afterCmdConn = UIContext::instance()->AfterCommandExecution.connect(&ColorBar::onAfterExecuteCommand, this);
m_fgConn = Preferences::instance().colorBar.fgColor.AfterChange.connect([this]{ onFgColorChangeFromPreferences(); });
m_bgConn = Preferences::instance().colorBar.bgColor.AfterChange.connect([this]{ onBgColorChangeFromPreferences(); });
+ m_fgTileConn = Preferences::instance().colorBar.fgTile.AfterChange.connect([this]{ onFgTileChangeFromPreferences(); });
+ m_bgTileConn = Preferences::instance().colorBar.bgTile.AfterChange.connect([this]{ onBgTileChangeFromPreferences(); });
m_sepConn = Preferences::instance().colorBar.entriesSeparator.AfterChange.connect([this]{ invalidate(); });
m_paletteView.FocusOrClick.connect(&ColorBar::onFocusPaletteOrTilesView, this);
m_tilesView.FocusOrClick.connect(&ColorBar::onFocusPaletteOrTilesView, this);
@@ -390,12 +401,12 @@ void ColorBar::setBgColor(const app::Color& color)
doc::tile_index ColorBar::getFgTile() const
{
- return m_fgColor.getColor().getIndex(); // TODO
+ return m_fgTile.getTile();
}
doc::tile_index ColorBar::getBgTile() const
{
- return m_bgColor.getColor().getIndex(); // TODO
+ return m_bgTile.getTile();
}
ColorBar::ColorSelector ColorBar::getColorSelector() const
@@ -518,11 +529,17 @@ void ColorBar::setTilemapMode(const TilemapMode mode)
manager()->freeWidget(&m_paletteView);
m_scrollablePalView.setVisible(false);
m_scrollableTilesView.setVisible(true);
+ m_selectorPlaceholder.setVisible(false);
+ m_colorHelpers.setVisible(false);
+ m_tilesHelpers.setVisible(true);
}
else {
manager()->freeWidget(&m_tilesView);
m_scrollablePalView.setVisible(true);
m_scrollableTilesView.setVisible(false);
+ m_selectorPlaceholder.setVisible(true);
+ m_colorHelpers.setVisible(true);
+ m_tilesHelpers.setVisible(false);
}
layout();
@@ -1049,12 +1066,11 @@ void ColorBar::onTilesViewDragAndDrop(doc::Tileset* tileset,
void ColorBar::onTilesViewIndexChange(int index, ui::MouseButton button)
{
- // TODO show tools to stamp/draw/pick tiles
-
+ auto& pref = Preferences::instance();
if (button == kButtonRight)
- setBgColor(app::Color::fromIndex(index));
+ pref.colorBar.bgTile(doc::tile(index, 0));
else if (button == kButtonLeft)
- setFgColor(app::Color::fromIndex(index));
+ pref.colorBar.fgTile(doc::tile(index, 0));
else if (button == kButtonMiddle) {
// TODO ?
}
@@ -1091,6 +1107,24 @@ void ColorBar::onBgColorChangeFromPreferences()
}
}
+void ColorBar::onFgTileChangeFromPreferences()
+{
+ if (m_fromPref)
+ return;
+
+ base::ScopedValue sync(m_fromPref, true, false);
+ m_fgTile.setTile(Preferences::instance().colorBar.fgTile());
+}
+
+void ColorBar::onBgTileChangeFromPreferences()
+{
+ if (m_fromPref)
+ return;
+
+ base::ScopedValue sync(m_fromPref, true, false);
+ m_bgTile.setTile(Preferences::instance().colorBar.bgTile());
+}
+
void ColorBar::onFgColorButtonBeforeChange(app::Color& color)
{
COLOR_BAR_TRACE("ColorBar::onFgColorButtonBeforeChange(%s)\n", color.toString().c_str());
diff --git a/src/app/ui/color_bar.h b/src/app/ui/color_bar.h
index a018b7484..1bcc257ce 100644
--- a/src/app/ui/color_bar.h
+++ b/src/app/ui/color_bar.h
@@ -19,6 +19,7 @@
#include "app/ui/color_button.h"
#include "app/ui/input_chain_element.h"
#include "app/ui/palette_view.h"
+#include "app/ui/tile_button.h"
#include "doc/object_id.h"
#include "doc/palette_gradient_type.h"
#include "doc/pixel_format.h"
@@ -133,6 +134,8 @@ namespace app {
void onPaletteIndexChange(PaletteIndexChangeEvent& ev);
void onFgColorChangeFromPreferences();
void onBgColorChangeFromPreferences();
+ void onFgTileChangeFromPreferences();
+ void onBgTileChangeFromPreferences();
void onFgColorButtonBeforeChange(app::Color& color);
void onFgColorButtonChange(const app::Color& color);
void onBgColorButtonChange(const app::Color& color);
@@ -203,6 +206,8 @@ namespace app {
PaletteView m_tilesView;
ui::Button m_remapPalButton;
ui::Button m_remapTilesButton;
+ ui::VBox m_colorHelpers;
+ ui::HBox m_tilesHelpers;
ColorSelector m_selector;
ColorTintShadeTone* m_tintShadeTone;
ColorSpectrum* m_spectrum;
@@ -211,6 +216,8 @@ namespace app {
ColorButton m_bgColor;
WarningIcon* m_fgWarningIcon;
WarningIcon* m_bgWarningIcon;
+ TileButton m_fgTile;
+ TileButton m_bgTile;
// True when the user clicks the PaletteView so we're changing the
// color from the palette view.
@@ -232,6 +239,8 @@ namespace app {
obs::scoped_connection m_afterCmdConn;
obs::scoped_connection m_fgConn;
obs::scoped_connection m_bgConn;
+ obs::scoped_connection m_fgTileConn;
+ obs::scoped_connection m_bgTileConn;
obs::scoped_connection m_sepConn;
obs::scoped_connection m_appPalChangeConn;
ui::MouseButton m_lastButton;
diff --git a/src/app/ui/color_button.h b/src/app/ui/color_button.h
index f1be82063..f55b7ab31 100644
--- a/src/app/ui/color_button.h
+++ b/src/app/ui/color_button.h
@@ -61,9 +61,11 @@ namespace app {
void onSaveLayout(ui::SaveLayoutEvent& ev) override;
private:
+ // ContextObserver impl
+ void onActiveSiteChange(const Site& site) override;
+
void onWindowClose(ui::CloseEvent& ev);
void onWindowColorChange(const app::Color& color);
- void onActiveSiteChange(const Site& site) override;
bool canPin() const { return m_options.canPinSelector; }
app::Color m_color;
diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp
index d19952199..4084c4514 100644
--- a/src/app/ui/editor/editor.cpp
+++ b/src/app/ui/editor/editor.cpp
@@ -1663,6 +1663,7 @@ app::Color Editor::getColorByPosition(const gfx::Point& mousePos)
gfx::PointF editorPos = screenToEditorF(mousePos);
ColorPicker picker;
+ site.tilemapMode(TilemapMode::Pixels);
picker.pickColor(site, editorPos, m_proj,
ColorPicker::FromComposition);
return picker.color();
@@ -1671,6 +1672,23 @@ app::Color Editor::getColorByPosition(const gfx::Point& mousePos)
return app::Color::fromMask();
}
+doc::tile_t Editor::getTileByPosition(const gfx::Point& mousePos)
+{
+ Site site = getSite();
+ if (site.sprite()) {
+ gfx::PointF editorPos = screenToEditorF(mousePos);
+
+ ColorPicker picker;
+ site.tilemapMode(TilemapMode::Tiles);
+ picker.pickColor(site, editorPos, m_proj,
+ ColorPicker::FromComposition);
+
+ return picker.tile();
+ }
+ else
+ return doc::tile_i_notile;
+}
+
bool Editor::startStraightLineWithFreehandTool(const ui::MouseMessage* msg)
{
tools::Tool* tool = App::instance()->activeToolManager()->selectedTool();
diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h
index 33fc7acfb..b7e255368 100644
--- a/src/app/ui/editor/editor.h
+++ b/src/app/ui/editor/editor.h
@@ -21,6 +21,7 @@
#include "app/ui/editor/editor_observers.h"
#include "app/ui/editor/editor_state.h"
#include "app/ui/editor/editor_states_history.h"
+#include "app/ui/tile_source.h"
#include "doc/algorithm/flip_type.h"
#include "doc/frame.h"
#include "doc/image_buffer.h"
@@ -73,6 +74,7 @@ namespace app {
class Editor : public ui::Widget,
public app::DocObserver,
public IColorSource,
+ public ITileSource,
public tools::ActiveToolObserver {
public:
enum EditorFlags {
@@ -282,6 +284,9 @@ namespace app {
// IColorSource
app::Color getColorByPosition(const gfx::Point& pos) override;
+ // ITileSource
+ doc::tile_t getTileByPosition(const gfx::Point& pos) override;
+
void setTagFocusBand(int value) { m_tagFocusBand = value; }
int tagFocusBand() const { return m_tagFocusBand; }
diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp
index fd52f343a..cf3204d13 100644
--- a/src/app/ui/editor/standby_state.cpp
+++ b/src/app/ui/editor/standby_state.cpp
@@ -542,19 +542,23 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
}
// For eye-dropper
else if (ink->isEyedropper()) {
+ Site site = editor->getSite();
EyedropperCommand cmd;
app::Color color = Preferences::instance().colorBar.fgColor();
- cmd.pickSample(editor->getSite(),
+ doc::tile_t tile = Preferences::instance().colorBar.fgTile();
+ cmd.pickSample(site,
spritePos,
editor->projection(),
- color);
+ color, tile);
- char buf[256];
- sprintf(buf, " :pos: %d %d",
- int(std::floor(spritePos.x)),
- int(std::floor(spritePos.y)));
+ auto buf = fmt::format(" :pos: {} {}",
+ int(std::floor(spritePos.x)),
+ int(std::floor(spritePos.y)));
- StatusBar::instance()->showColor(0, buf, color);
+ if (site.tilemapMode() == TilemapMode::Tiles)
+ StatusBar::instance()->showTile(0, buf.c_str(), tile);
+ else
+ StatusBar::instance()->showColor(0, buf.c_str(), color);
}
else {
Site site = editor->getSite();
diff --git a/src/app/ui/editor/tool_loop_impl.cpp b/src/app/ui/editor/tool_loop_impl.cpp
index 195159b7d..b4d9ff07f 100644
--- a/src/app/ui/editor/tool_loop_impl.cpp
+++ b/src/app/ui/editor/tool_loop_impl.cpp
@@ -805,16 +805,14 @@ tools::ToolLoop* create_tool_loop(
else {
params.fg = colorbar->getFgColor();
params.bg = colorbar->getBgColor();
- }
-
- if (site.tilemapMode() == TilemapMode::Pixels &&
- (!params.fg.isValid() ||
- !params.bg.isValid())) {
- if (Preferences::instance().colorBar.showInvalidFgBgColorAlert()) {
- OptionalAlert::show(
- Preferences::instance().colorBar.showInvalidFgBgColorAlert,
- 1, Strings::alerts_invalid_fg_or_bg_colors());
- return nullptr;
+ if (!params.fg.isValid() ||
+ !params.bg.isValid()) {
+ if (Preferences::instance().colorBar.showInvalidFgBgColorAlert()) {
+ OptionalAlert::show(
+ Preferences::instance().colorBar.showInvalidFgBgColorAlert,
+ 1, Strings::alerts_invalid_fg_or_bg_colors());
+ return nullptr;
+ }
}
}
@@ -990,16 +988,14 @@ tools::ToolLoop* create_tool_loop_preview(
if (site.tilemapMode() == TilemapMode::Tiles) {
params.fg = app::Color::fromIndex(colorbar->getFgTile()); // TODO Color::fromTileIndex?
params.bg = app::Color::fromIndex(colorbar->getBgTile());
- if (!params.fg.isValid() || !params.bg.isValid())
- return nullptr;
}
else {
params.fg = colorbar->getFgColor();
params.bg = colorbar->getBgColor();
+ if (!params.fg.isValid() ||
+ !params.bg.isValid())
+ return nullptr;
}
- if (!params.fg.isValid() ||
- !params.bg.isValid())
- return nullptr;
params.brush = brush;
params.button = tools::ToolLoop::Left;
diff --git a/src/app/ui/palette_view.cpp b/src/app/ui/palette_view.cpp
index 611460309..91e4c330b 100644
--- a/src/app/ui/palette_view.cpp
+++ b/src/app/ui/palette_view.cpp
@@ -151,8 +151,7 @@ public:
PaletteViewModification::DRAGANDDROP);
}
void showEntryInStatusBar(StatusBar* statusBar, int index) override {
- statusBar->showColor(
- 0, "", app::Color::fromIndex(index));
+ statusBar->showColor(0, "", app::Color::fromIndex(index));
}
void showDragInfoInStatusBar(StatusBar* statusBar, bool copy, int destIndex, int newSize) override {
statusBar->setStatusText(
@@ -259,8 +258,7 @@ public:
picks = newPicks;
}
void showEntryInStatusBar(StatusBar* statusBar, int index) override {
- statusBar->setStatusText(
- 0, fmt::format("Tile {}", index));
+ statusBar->showTile(0, "", doc::tile(index, 0));
}
void showDragInfoInStatusBar(StatusBar* statusBar, bool copy, int destIndex, int newSize) override {
statusBar->setStatusText(
@@ -483,6 +481,16 @@ app::Color PaletteView::getColorByPosition(const gfx::Point& pos)
return app::Color::fromMask();
}
+doc::tile_t PaletteView::getTileByPosition(const gfx::Point& pos)
+{
+ gfx::Point relPos = pos - bounds().origin();
+ for (int i=0; isize(); ++i) {
+ if (getPaletteEntryBounds(i).contains(relPos))
+ return doc::tile(i, 0);
+ }
+ return doc::tile_i_notile;
+}
+
void PaletteView::onActiveSiteChange(const Site& site)
{
ASSERT(isTiles());
diff --git a/src/app/ui/palette_view.h b/src/app/ui/palette_view.h
index d009709e1..8302f703a 100644
--- a/src/app/ui/palette_view.h
+++ b/src/app/ui/palette_view.h
@@ -13,6 +13,7 @@
#include "app/context_observer.h"
#include "app/ui/color_source.h"
#include "app/ui/marching_ants.h"
+#include "app/ui/tile_source.h"
#include "doc/palette_picks.h"
#include "doc/tile.h"
#include "obs/connection.h"
@@ -64,6 +65,7 @@ namespace app {
class PaletteView : public ui::Widget
, public MarchingAnts
, public IColorSource
+ , public ITileSource
, public ContextObserver {
friend class PaletteViewAdapter;
friend class TilesetViewAdapter;
@@ -102,6 +104,9 @@ namespace app {
// IColorSource
app::Color getColorByPosition(const gfx::Point& pos) override;
+ // ITileSource
+ doc::tile_t getTileByPosition(const gfx::Point& pos) override;
+
// ContextObserver impl
void onActiveSiteChange(const Site& site) override;
diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp
index 564d5f0cd..a5c6e62d5 100644
--- a/src/app/ui/status_bar.cpp
+++ b/src/app/ui/status_bar.cpp
@@ -68,7 +68,8 @@ class StatusBar::Indicators : public HBox {
enum IndicatorType {
kText,
kIcon,
- kColor
+ kColor,
+ kTile
};
Indicator(IndicatorType type) : m_type(type) { }
IndicatorType indicatorType() const { return m_type; }
@@ -195,6 +196,41 @@ class StatusBar::Indicators : public HBox {
app::Color m_color;
};
+ class TileIndicator : public Indicator {
+ public:
+ TileIndicator(doc::tile_t tile)
+ : Indicator(kTile)
+ , m_tile(doc::tile_i_notile) {
+ updateIndicator(tile, true);
+ }
+
+ bool updateIndicator(doc::tile_t tile, bool first = false) {
+ if (m_tile == tile && !first)
+ return false;
+
+ m_tile = tile;
+ setMinSize(minSize().createUnion(Size(32*guiscale(), 1)));
+ return true;
+ }
+
+ private:
+ void onPaint(ui::PaintEvent& ev) override {
+ Rect rc = clientBounds();
+ Graphics* g = ev.graphics();
+
+ // TODO could the site came from the Indicators or StatusBar itself
+ Site site = UIContext::instance()->activeSite();
+
+ g->fillRect(bgColor(), rc);
+ draw_tile_button(
+ g, Rect(rc.x, rc.y, 32*guiscale(), rc.h),
+ site, m_tile,
+ false, false);
+ }
+
+ doc::tile_t m_tile;
+ };
+
public:
Indicators()
@@ -282,6 +318,25 @@ public:
m_redraw = true;
}
+ void addTileIndicator(doc::tile_t tile) {
+ if (m_iterator != m_indicators.end()) {
+ if ((*m_iterator)->indicatorType() == Indicator::kTile) {
+ m_redraw |= static_cast(*m_iterator)
+ ->updateIndicator(tile);
+ ++m_iterator;
+ return;
+ }
+ else
+ removeAllNextIndicators();
+ }
+
+ auto indicator = new TileIndicator(tile);
+ m_indicators.push_back(indicator);
+ m_iterator = m_indicators.end();
+ m_leftArea.addChild(indicator);
+ m_redraw = true;
+ }
+
void showBackupIcon(BackupIcon icon) {
m_backupIcon = icon;
if (m_backupIcon != BackupIcon::None) {
@@ -394,10 +449,36 @@ public:
std::string str = color.toHumanReadableString(
app_get_current_pixel_format(),
app::Color::LongHumanReadableString);
- if (color.getAlpha() < 255) {
- char buf[256];
- sprintf(buf, " A%d", color.getAlpha());
- str += buf;
+ if (color.getAlpha() < 255)
+ str += fmt::format(" A{}", color.getAlpha());
+ m_indicators->addTextIndicator(str.c_str());
+
+ return *this;
+ }
+
+ IndicatorsGeneration& add(doc::tile_t tile) {
+ auto theme = SkinTheme::instance();
+
+ // Eyedropper icon
+ add(theme->getToolPart("eyedropper"), false);
+
+ // Color
+ m_indicators->addTileIndicator(tile);
+
+ // Color description
+ std::string str;
+ if (tile == doc::tile_i_notile) {
+ str += "Empty";
+ }
+ else {
+ doc::tile_index ti = doc::tile_geti(tile);
+ doc::tile_flags tf = doc::tile_getf(tile);
+ str += fmt::format("{}", ti);
+ if (tf) {
+ if (tf & doc::tile_f_flipx) str += " FlipX";
+ if (tf & doc::tile_f_flipy) str += " FlipY";
+ if (tf & doc::tile_f_90cw) str += " Rot90CW";
+ }
}
m_indicators->addTextIndicator(str.c_str());
@@ -712,6 +793,18 @@ void StatusBar::showColor(int msecs, const char* text, const app::Color& color)
}
}
+void StatusBar::showTile(int msecs, const char* text, doc::tile_t tile)
+{
+ if ((base::current_tick() > m_timeout) || (msecs > 0)) {
+ IndicatorsGeneration gen(m_indicators);
+ gen.add(tile);
+ if (text)
+ gen.add(text);
+
+ m_timeout = base::current_tick() + msecs;
+ }
+}
+
void StatusBar::showTool(int msecs, tools::Tool* tool)
{
ASSERT(tool != NULL);
diff --git a/src/app/ui/status_bar.h b/src/app/ui/status_bar.h
index 3982bf2df..41c4d6877 100644
--- a/src/app/ui/status_bar.h
+++ b/src/app/ui/status_bar.h
@@ -14,6 +14,7 @@
#include "app/tools/active_tool_observer.h"
#include "app/ui/doc_observer_widget.h"
#include "base/time.h"
+#include "doc/tile.h"
#include "ui/base.h"
#include "ui/box.h"
@@ -60,6 +61,7 @@ namespace app {
bool setStatusText(int msecs, const std::string& text);
void showTip(int msecs, const std::string& msg);
void showColor(int msecs, const char* text, const Color& color);
+ void showTile(int msecs, const char* text, doc::tile_t tile);
void showTool(int msecs, tools::Tool* tool);
void showSnapToGridWarning(bool state);
diff --git a/src/app/ui/tile_button.cpp b/src/app/ui/tile_button.cpp
new file mode 100644
index 000000000..34c4d3427
--- /dev/null
+++ b/src/app/ui/tile_button.cpp
@@ -0,0 +1,164 @@
+// Aseprite
+// Copyright (c) 2020 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/ui/tile_button.h"
+
+#include "app/modules/gfx.h"
+#include "app/ui/skin/skin_theme.h"
+#include "app/ui/status_bar.h"
+#include "app/ui_context.h"
+#include "fmt/format.h"
+#include "ui/graphics.h"
+#include "ui/message.h"
+#include "ui/paint_event.h"
+#include "ui/size_hint_event.h"
+#include "ui/system.h"
+
+namespace app {
+
+using namespace app::skin;
+using namespace ui;
+
+static WidgetType tilebutton_type()
+{
+ static WidgetType type = kGenericWidget;
+ if (type == kGenericWidget)
+ type = register_widget_type();
+ return type;
+}
+
+TileButton::TileButton()
+ : ButtonBase("", tilebutton_type(), kButtonWidget, kButtonWidget)
+{
+ setFocusStop(true);
+ initTheme();
+
+ UIContext::instance()->add_observer(this);
+}
+
+TileButton::~TileButton()
+{
+ UIContext::instance()->remove_observer(this);
+}
+
+doc::tile_t TileButton::getTile() const
+{
+ return m_tile;
+}
+
+void TileButton::setTile(doc::tile_t origTile)
+{
+ // Before change (this signal can modify the color)
+ doc::tile_t tile = origTile;
+ BeforeChange(tile);
+ m_tile = tile;
+ Change(tile);
+ invalidate();
+}
+
+doc::tile_t TileButton::getTileByPosition(const gfx::Point& pos)
+{
+ // Ignore the position
+ return m_tile;
+}
+
+void TileButton::onInitTheme(InitThemeEvent& ev)
+{
+ ButtonBase::onInitTheme(ev);
+ setStyle(SkinTheme::instance()->styles.colorButton());
+}
+
+bool TileButton::onProcessMessage(Message* msg)
+{
+ switch (msg->type()) {
+
+ case kMouseEnterMessage:
+ StatusBar::instance()->showTile(0, "", m_tile);
+ break;
+
+ case kMouseLeaveMessage:
+ StatusBar::instance()->showDefaultText();
+ break;
+
+ case kMouseMoveMessage:
+ if (hasCapture()) {
+ gfx::Point mousePos = static_cast(msg)->position();
+ Widget* picked = manager()->pick(mousePos);
+ doc::tile_t tile = m_tile;
+
+ if (picked && picked != this) {
+ // Pick a tile from a ITileSource
+ if (ITileSource* tileSource = dynamic_cast(picked)) {
+ tile = tileSource->getTileByPosition(mousePos);
+ }
+ }
+
+ // Did the tile change?
+ if (tile != m_tile) {
+ setTile(tile);
+ }
+ }
+ break;
+
+ case kSetCursorMessage:
+ if (hasCapture()) {
+ ui::set_mouse_cursor(kCustomCursor, SkinTheme::instance()->cursors.eyedropper());
+ return true;
+ }
+ break;
+
+ }
+
+ return ButtonBase::onProcessMessage(msg);
+}
+
+void TileButton::onSizeHint(SizeHintEvent& ev)
+{
+ ButtonBase::onSizeHint(ev);
+ gfx::Size sz(32*guiscale(),
+ 32*guiscale());
+ ev.setSizeHint(sz);
+}
+
+void TileButton::onPaint(PaintEvent& ev)
+{
+ Graphics* g = ev.graphics();
+ SkinTheme* theme = static_cast(this->theme());
+ gfx::Rect rc = clientBounds();
+
+ gfx::Color bg = bgColor();
+ if (gfx::is_transparent(bg))
+ bg = theme->colors.face();
+ g->fillRect(bg, rc);
+
+ Site site = UIContext::instance()->activeSite();
+ draw_tile_button(g, rc,
+ site, m_tile,
+ hasMouseOver(), false);
+
+ // Draw text
+ if (m_tile != doc::tile_i_notile) {
+ std::string str = fmt::format("{}", doc::tile_geti(m_tile));
+ setTextQuiet(str.c_str());
+
+ // TODO calc a proper color for the text
+ gfx::Color textcolor = gfx::rgba(0, 0, 0);
+ gfx::Rect textrc;
+ getTextIconInfo(NULL, &textrc);
+ g->drawUIText(text(), textcolor, gfx::ColorNone, textrc.origin(), 0);
+ }
+}
+
+void TileButton::onActiveSiteChange(const Site& site)
+{
+ invalidate();
+}
+
+} // namespace app
diff --git a/src/app/ui/tile_button.h b/src/app/ui/tile_button.h
new file mode 100644
index 000000000..4d8f34899
--- /dev/null
+++ b/src/app/ui/tile_button.h
@@ -0,0 +1,52 @@
+// Aseprite
+// Copyright (c) 2020 Igara Studio S.A.
+//
+// This program is distributed under the terms of
+// the End-User License Agreement for Aseprite.
+
+#ifndef APP_UI_TILE_BUTTON_H_INCLUDED
+#define APP_UI_TILE_BUTTON_H_INCLUDED
+#pragma once
+
+#include "app/color.h"
+#include "app/context_observer.h"
+#include "app/ui/tile_source.h"
+#include "doc/tile.h"
+#include "ui/button.h"
+
+namespace app {
+
+ class TileButton : public ui::ButtonBase,
+ public ContextObserver,
+ public ITileSource {
+ public:
+ TileButton();
+ ~TileButton();
+
+ doc::tile_t getTile() const;
+ void setTile(doc::tile_t tile);
+
+ // ITileSource
+ doc::tile_t getTileByPosition(const gfx::Point& pos) override;
+
+ // Signals
+ obs::signal BeforeChange;
+ obs::signal Change;
+
+ protected:
+ // Events
+ void onInitTheme(ui::InitThemeEvent& ev) override;
+ bool onProcessMessage(ui::Message* msg) override;
+ void onSizeHint(ui::SizeHintEvent& ev) override;
+ void onPaint(ui::PaintEvent& ev) override;
+
+ private:
+ // ContextObserver impl
+ void onActiveSiteChange(const Site& site) override;
+
+ doc::tile_t m_tile = doc::tile_i_notile;
+ };
+
+} // namespace app
+
+#endif
diff --git a/src/app/ui/tile_source.h b/src/app/ui/tile_source.h
new file mode 100644
index 000000000..e34e0c3c5
--- /dev/null
+++ b/src/app/ui/tile_source.h
@@ -0,0 +1,24 @@
+// Aseprite
+// Copyright (c) 2020 Igara Studio S.A.
+//
+// This program is distributed under the terms of
+// the End-User License Agreement for Aseprite.
+
+#ifndef APP_UI_TILE_SOURCE_H_INCLUDED
+#define APP_UI_TILE_SOURCE_H_INCLUDED
+#pragma once
+
+#include "doc/tile.h"
+#include "gfx/point.h"
+
+namespace app {
+
+ class ITileSource {
+ public:
+ virtual ~ITileSource() { }
+ virtual doc::tile_t getTileByPosition(const gfx::Point& pos) = 0;
+ };
+
+} // namespace app
+
+#endif