Add fg/bg tiles selectors in ColorBar

Now fg/bg colors and fg/bg tiles are different widgets, and the
StatusBar can show tiles when the eyedropper is picking tiles.
This commit is contained in:
David Capello 2020-08-21 18:07:37 -03:00
parent fa46aff072
commit 5ee3ebdd71
25 changed files with 611 additions and 60 deletions

View File

@ -212,6 +212,8 @@
<option id="tiles_box_size" type="int" default="16" />
<option id="fg_color" type="app::Color" default="app::Color::fromRgb(255, 255, 255)" />
<option id="bg_color" type="app::Color" default="app::Color::fromRgb(0, 0, 0)" />
<option id="fg_tile" type="doc::tile_t" default="doc::tile_i_notile" />
<option id="bg_tile" type="doc::tile_t" default="doc::tile_i_notile" />
<option id="selector" type="app::ColorBar::ColorSelector" default="app::ColorBar::ColorSelector::TINT_SHADE_TONE" />
<option id="discrete_wheel" type="bool" default="false" />
<option id="wheel_model" type="int" default="0" />

View File

@ -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

View File

@ -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) {

View File

@ -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;
};

View File

@ -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()

View File

@ -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,

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -74,6 +74,14 @@ void Site::range(const DocRange& range)
}
}
doc::Tileset* Site::tileset() const
{
if (m_layer && m_layer->isTilemap())
return static_cast<LayerTilemap*>(m_layer)->tileset();
else
return nullptr;
}
Grid Site::grid() const
{
if (m_layer && m_layer->isTilemap()) {

View File

@ -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;

View File

@ -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<bool> sync(m_fromPref, true, false);
m_fgTile.setTile(Preferences::instance().colorBar.fgTile());
}
void ColorBar::onBgTileChangeFromPreferences()
{
if (m_fromPref)
return;
base::ScopedValue<bool> 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());

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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; }

View File

@ -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();

View File

@ -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;

View File

@ -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; i<m_adapter->size(); ++i) {
if (getPaletteEntryBounds(i).contains(relPos))
return doc::tile(i, 0);
}
return doc::tile_i_notile;
}
void PaletteView::onActiveSiteChange(const Site& site)
{
ASSERT(isTiles());

View File

@ -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;

View File

@ -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<TileIndicator*>(*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);

View File

@ -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);

164
src/app/ui/tile_button.cpp Normal file
View File

@ -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<MouseMessage*>(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<ITileSource*>(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<SkinTheme*>(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

52
src/app/ui/tile_button.h Normal file
View File

@ -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<void(doc::tile_t&)> BeforeChange;
obs::signal<void(doc::tile_t)> 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

24
src/app/ui/tile_source.h Normal file
View File

@ -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