2015-02-12 23:16:25 +08:00
|
|
|
// Aseprite
|
2024-02-21 02:40:17 +08:00
|
|
|
// Copyright (C) 2018-2024 Igara Studio S.A.
|
2018-06-06 00:11:29 +08:00
|
|
|
// Copyright (C) 2001-2018 David Capello
|
2015-02-12 23:16:25 +08:00
|
|
|
//
|
2016-08-27 04:02:58 +08:00
|
|
|
// This program is distributed under the terms of
|
|
|
|
// the End-User License Agreement for Aseprite.
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2019-11-16 23:09:33 +08:00
|
|
|
#define PAL_TRACE(...) // TRACEARGS
|
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2007-09-19 07:57:02 +08:00
|
|
|
#include "config.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#endif
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/app.h"
|
2010-08-25 11:26:37 +08:00
|
|
|
#include "app/color.h"
|
2015-03-21 01:46:48 +08:00
|
|
|
#include "app/color_utils.h"
|
2015-05-09 23:20:58 +08:00
|
|
|
#include "app/commands/commands.h"
|
2015-07-01 04:36:37 +08:00
|
|
|
#include "app/modules/gfx.h"
|
2014-04-14 03:26:00 +08:00
|
|
|
#include "app/modules/gui.h"
|
|
|
|
#include "app/modules/palettes.h"
|
2019-03-30 02:57:10 +08:00
|
|
|
#include "app/site.h"
|
2015-05-09 23:20:58 +08:00
|
|
|
#include "app/ui/editor/editor.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/ui/palette_view.h"
|
2015-03-20 06:45:12 +08:00
|
|
|
#include "app/ui/skin/skin_theme.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/ui/status_bar.h"
|
2019-03-30 02:57:10 +08:00
|
|
|
#include "app/ui_context.h"
|
2015-05-11 08:36:46 +08:00
|
|
|
#include "app/util/clipboard.h"
|
2020-06-12 22:33:55 +08:00
|
|
|
#include "app/util/conversion_to_surface.h"
|
2019-08-11 01:37:18 +08:00
|
|
|
#include "app/util/pal_ops.h"
|
2015-07-05 06:11:50 +08:00
|
|
|
#include "base/convert_to.h"
|
2014-10-21 09:21:31 +08:00
|
|
|
#include "doc/image.h"
|
2019-03-30 02:57:10 +08:00
|
|
|
#include "doc/layer_tilemap.h"
|
2014-10-21 09:21:31 +08:00
|
|
|
#include "doc/palette.h"
|
2015-03-23 23:25:32 +08:00
|
|
|
#include "doc/remap.h"
|
2019-03-30 02:57:10 +08:00
|
|
|
#include "doc/tileset.h"
|
2020-05-09 04:39:55 +08:00
|
|
|
#include "fmt/format.h"
|
2015-03-20 19:44:39 +08:00
|
|
|
#include "gfx/color.h"
|
2015-03-20 06:45:12 +08:00
|
|
|
#include "gfx/point.h"
|
2018-08-09 23:58:43 +08:00
|
|
|
#include "os/surface.h"
|
2019-03-30 02:57:10 +08:00
|
|
|
#include "os/surface.h"
|
|
|
|
#include "os/system.h"
|
2024-02-21 02:40:17 +08:00
|
|
|
#include "text/font.h"
|
2014-04-14 03:26:00 +08:00
|
|
|
#include "ui/graphics.h"
|
2012-06-18 09:49:58 +08:00
|
|
|
#include "ui/manager.h"
|
|
|
|
#include "ui/message.h"
|
2014-04-14 03:26:00 +08:00
|
|
|
#include "ui/paint_event.h"
|
2015-12-04 08:50:05 +08:00
|
|
|
#include "ui/size_hint_event.h"
|
2012-06-18 09:49:58 +08:00
|
|
|
#include "ui/system.h"
|
|
|
|
#include "ui/theme.h"
|
|
|
|
#include "ui/view.h"
|
|
|
|
#include "ui/widget.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
|
2015-03-23 23:25:32 +08:00
|
|
|
#include <algorithm>
|
2014-06-23 05:53:14 +08:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
2019-11-16 23:09:33 +08:00
|
|
|
#include <set>
|
2014-06-23 05:53:14 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
namespace app {
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-06-18 09:02:54 +08:00
|
|
|
using namespace ui;
|
2015-03-20 06:45:12 +08:00
|
|
|
using namespace app::skin;
|
2012-06-18 09:02:54 +08:00
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
// Interface used to adapt the PaletteView widget to see tilesets too.
|
|
|
|
class AbstractPaletteViewAdapter {
|
|
|
|
public:
|
|
|
|
virtual ~AbstractPaletteViewAdapter() { }
|
|
|
|
virtual int size() const = 0;
|
|
|
|
virtual void paletteChange(doc::PalettePicks& picks) = 0;
|
|
|
|
virtual void activeSiteChange(const Site& site, doc::PalettePicks& picks) = 0;
|
|
|
|
virtual void clearSelection(PaletteView* paletteView,
|
|
|
|
doc::PalettePicks& picks) = 0;
|
2019-11-16 23:09:33 +08:00
|
|
|
virtual void selectIndex(PaletteView* paletteView,
|
2020-05-19 07:24:22 +08:00
|
|
|
int index, ui::MouseButton button) = 0;
|
2019-11-16 23:09:33 +08:00
|
|
|
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;
|
2019-03-30 02:57:10 +08:00
|
|
|
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;
|
|
|
|
virtual void drawEntry(ui::Graphics* g,
|
|
|
|
SkinTheme* theme,
|
|
|
|
const int palIdx,
|
|
|
|
const int offIdx,
|
|
|
|
const int childSpacing,
|
|
|
|
gfx::Rect& box,
|
|
|
|
gfx::Color& negColor) = 0;
|
|
|
|
virtual doc::Tileset* tileset() const { return nullptr; }
|
|
|
|
};
|
|
|
|
|
|
|
|
// This default adapter uses the default behavior to use the
|
|
|
|
// PaletteView as just a doc::Palette view.
|
|
|
|
class PaletteViewAdapter : public AbstractPaletteViewAdapter {
|
|
|
|
public:
|
|
|
|
int size() const override { return palette()->size(); }
|
|
|
|
void paletteChange(doc::PalettePicks& picks) override {
|
|
|
|
picks.resize(palette()->size());
|
|
|
|
}
|
|
|
|
void activeSiteChange(const Site& site, doc::PalettePicks& picks) override {
|
|
|
|
// Do nothing
|
|
|
|
}
|
|
|
|
void clearSelection(PaletteView* paletteView,
|
|
|
|
doc::PalettePicks& picks) override {
|
|
|
|
Palette palette(*this->palette());
|
|
|
|
Palette newPalette(palette);
|
2020-05-19 07:24:22 +08:00
|
|
|
newPalette.resize(std::max(1, newPalette.size() - picks.picks()));
|
2019-03-30 02:57:10 +08:00
|
|
|
|
|
|
|
Remap remap = create_remap_to_move_picks(picks, palette.size());
|
|
|
|
for (int i=0; i<palette.size(); ++i) {
|
|
|
|
if (!picks[i])
|
|
|
|
newPalette.setEntry(remap[i], palette.getEntry(i));
|
|
|
|
}
|
|
|
|
|
2019-11-16 23:09:33 +08:00
|
|
|
paletteView->setNewPalette(&palette, &newPalette,
|
|
|
|
PaletteViewModification::CLEAR);
|
|
|
|
}
|
|
|
|
void selectIndex(PaletteView* paletteView,
|
2020-05-19 07:24:22 +08:00
|
|
|
int index, ui::MouseButton button) override {
|
2019-11-16 23:09:33 +08:00
|
|
|
// Emit signal
|
|
|
|
if (paletteView->delegate())
|
2020-05-19 07:24:22 +08:00
|
|
|
paletteView->delegate()->onPaletteViewIndexChange(index, button);
|
2019-11-16 23:09:33 +08:00
|
|
|
}
|
|
|
|
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);
|
2019-03-30 02:57:10 +08:00
|
|
|
}
|
|
|
|
void showEntryInStatusBar(StatusBar* statusBar, int index) override {
|
2022-07-16 04:30:39 +08:00
|
|
|
statusBar->showColor(0, app::Color::fromIndex(index));
|
2019-03-30 02:57:10 +08:00
|
|
|
}
|
|
|
|
void showDragInfoInStatusBar(StatusBar* statusBar, bool copy, int destIndex, int newSize) override {
|
|
|
|
statusBar->setStatusText(
|
2020-05-19 07:24:22 +08:00
|
|
|
0, fmt::format("{} to {} - New Palette Size {}",
|
|
|
|
(copy ? "Copy": "Move"),
|
|
|
|
destIndex, newSize));
|
2019-03-30 02:57:10 +08:00
|
|
|
}
|
|
|
|
void showResizeInfoInStatusBar(StatusBar* statusBar, int newSize) override {
|
|
|
|
statusBar->setStatusText(
|
2020-05-19 07:24:22 +08:00
|
|
|
0, fmt::format("New Palette Size {}", newSize));
|
2019-03-30 02:57:10 +08:00
|
|
|
}
|
|
|
|
void drawEntry(ui::Graphics* g,
|
|
|
|
SkinTheme* theme,
|
|
|
|
const int palIdx,
|
|
|
|
const int offIdx,
|
|
|
|
const int childSpacing,
|
|
|
|
gfx::Rect& box,
|
|
|
|
gfx::Color& negColor) override {
|
|
|
|
doc::color_t palColor =
|
|
|
|
(palIdx < palette()->size() ? palette()->getEntry(palIdx):
|
|
|
|
rgba(0, 0, 0, 255));
|
|
|
|
app::Color appColor = app::Color::fromRgb(
|
|
|
|
rgba_getr(palColor),
|
|
|
|
rgba_getg(palColor),
|
|
|
|
rgba_getb(palColor),
|
|
|
|
rgba_geta(palColor));
|
|
|
|
|
|
|
|
if (childSpacing > 0) {
|
|
|
|
gfx::Color color = theme->colors.paletteEntriesSeparator();
|
|
|
|
g->fillRect(color, gfx::Rect(box).enlarge(childSpacing));
|
|
|
|
}
|
|
|
|
draw_color(g, box, appColor, doc::ColorMode::RGB);
|
|
|
|
|
|
|
|
const gfx::Color gfxColor = gfx::rgba(
|
|
|
|
rgba_getr(palColor),
|
|
|
|
rgba_getg(palColor),
|
|
|
|
rgba_getb(palColor),
|
|
|
|
rgba_geta(palColor));
|
|
|
|
negColor = color_utils::blackandwhite_neg(gfxColor);
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
doc::Palette* palette() const {
|
|
|
|
return get_current_palette();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// This adapter makes it possible to use a PaletteView to edit a
|
|
|
|
// doc::Tileset.
|
|
|
|
class TilesetViewAdapter : public AbstractPaletteViewAdapter {
|
|
|
|
public:
|
|
|
|
int size() const override {
|
|
|
|
if (auto t = tileset())
|
|
|
|
return t->size();
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
void paletteChange(doc::PalettePicks& picks) override {
|
|
|
|
// Do nothing
|
|
|
|
}
|
|
|
|
void activeSiteChange(const Site& site, doc::PalettePicks& picks) override {
|
|
|
|
if (auto tileset = this->tileset())
|
|
|
|
picks.resize(tileset->size());
|
|
|
|
else
|
|
|
|
picks.clear();
|
|
|
|
}
|
|
|
|
void clearSelection(PaletteView* paletteView,
|
|
|
|
doc::PalettePicks& picks) override {
|
2020-10-31 03:33:34 +08:00
|
|
|
// Cannot delete the empty tile (index 0)
|
|
|
|
int i = picks.firstPick();
|
|
|
|
if (i == doc::notile) {
|
|
|
|
picks[i] = false;
|
|
|
|
if (!picks.picks()) {
|
|
|
|
// Cannot remove empty tile
|
|
|
|
StatusBar::instance()->showTip(1000, "Cannot delete the empty tile");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-16 23:09:33 +08:00
|
|
|
paletteView->delegate()->onTilesViewClearTiles(picks);
|
|
|
|
}
|
|
|
|
void selectIndex(PaletteView* paletteView,
|
2020-05-19 07:24:22 +08:00
|
|
|
int index, ui::MouseButton button) override {
|
2019-11-16 23:09:33 +08:00
|
|
|
// Emit signal
|
|
|
|
if (paletteView->delegate())
|
2020-05-19 07:24:22 +08:00
|
|
|
paletteView->delegate()->onTilesViewIndexChange(index, button);
|
2019-11-16 23:09:33 +08:00
|
|
|
}
|
|
|
|
void resizePalette(PaletteView* paletteView,
|
|
|
|
int newSize) override {
|
|
|
|
paletteView->delegate()->onTilesViewResize(newSize);
|
|
|
|
}
|
|
|
|
void dropColors(PaletteView* paletteView,
|
|
|
|
doc::PalettePicks& picks,
|
|
|
|
int& currentEntry,
|
2020-10-31 03:33:34 +08:00
|
|
|
const int _beforeIndex,
|
2019-11-16 23:09:33 +08:00
|
|
|
const bool isCopy) override {
|
|
|
|
PAL_TRACE("dropColors");
|
|
|
|
|
|
|
|
doc::Tileset* tileset = this->tileset();
|
|
|
|
ASSERT(tileset);
|
|
|
|
if (!tileset)
|
|
|
|
return;
|
|
|
|
|
2020-02-18 21:30:34 +08:00
|
|
|
// Important: we create a copy because if we make the tileset
|
|
|
|
// bigger dropping tiles outside the tileset range, the tileset
|
|
|
|
// will be made bigger (see cmd::AddTile() inside
|
|
|
|
// move_tiles_in_tileset() function), a
|
|
|
|
// Doc::notifyTilesetChanged() will be generated, a
|
|
|
|
// ColorBar::onTilesetChanged() called, and finally we'll receive a
|
|
|
|
// PaletteView::deselect() that will clear the whole picks.
|
|
|
|
auto newPicks = picks;
|
2020-10-31 03:33:34 +08:00
|
|
|
int beforeIndex = _beforeIndex;
|
|
|
|
|
|
|
|
// We cannot move the empty tile (index 0) no any place
|
|
|
|
if (beforeIndex == 0)
|
|
|
|
++beforeIndex;
|
|
|
|
if (!isCopy && newPicks.size() > 0 && newPicks[0])
|
|
|
|
newPicks[0] = false;
|
|
|
|
if (!newPicks.picks()) {
|
|
|
|
// Cannot move empty tile
|
|
|
|
StatusBar::instance()->showTip(1000, "Cannot move the empty tile");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-11-16 23:09:33 +08:00
|
|
|
paletteView->delegate()->onTilesViewDragAndDrop(
|
2020-02-18 21:30:34 +08:00
|
|
|
tileset, newPicks, currentEntry, beforeIndex, isCopy);
|
|
|
|
|
|
|
|
// Copy the new picks
|
|
|
|
picks = newPicks;
|
2019-03-30 02:57:10 +08:00
|
|
|
}
|
|
|
|
void showEntryInStatusBar(StatusBar* statusBar, int index) override {
|
2022-07-16 04:30:39 +08:00
|
|
|
statusBar->showTile(0, doc::tile(index, 0));
|
2019-03-30 02:57:10 +08:00
|
|
|
}
|
|
|
|
void showDragInfoInStatusBar(StatusBar* statusBar, bool copy, int destIndex, int newSize) override {
|
|
|
|
statusBar->setStatusText(
|
2020-05-19 07:24:22 +08:00
|
|
|
0, fmt::format("{} to {} - New Tileset Size {}",
|
|
|
|
(copy ? "Copy": "Move"),
|
|
|
|
destIndex, newSize));
|
2019-03-30 02:57:10 +08:00
|
|
|
}
|
|
|
|
void showResizeInfoInStatusBar(StatusBar* statusBar, int newSize) override {
|
|
|
|
statusBar->setStatusText(
|
2020-05-19 07:24:22 +08:00
|
|
|
0, fmt::format("New Tileset Size {}", newSize));
|
2019-03-30 02:57:10 +08:00
|
|
|
}
|
|
|
|
void drawEntry(ui::Graphics* g,
|
|
|
|
SkinTheme* theme,
|
|
|
|
const int palIdx,
|
|
|
|
const int offIdx,
|
|
|
|
const int childSpacing,
|
|
|
|
gfx::Rect& box,
|
|
|
|
gfx::Color& negColor) override {
|
|
|
|
if (childSpacing > 0) {
|
|
|
|
gfx::Color color = theme->colors.paletteEntriesSeparator();
|
|
|
|
g->fillRect(color, gfx::Rect(box).enlarge(childSpacing));
|
|
|
|
}
|
|
|
|
draw_color(g, box, app::Color::fromMask(), doc::ColorMode::RGB);
|
|
|
|
|
|
|
|
doc::ImageRef tileImage;
|
|
|
|
if (auto t = this->tileset())
|
|
|
|
tileImage = t->get(palIdx);
|
|
|
|
if (tileImage) {
|
|
|
|
int w = tileImage->width();
|
|
|
|
int h = tileImage->height();
|
2024-03-06 05:50:24 +08:00
|
|
|
os::SurfaceRef surface = os::System::instance()->makeRgbaSurface(w, h);
|
2020-06-12 22:33:55 +08:00
|
|
|
convert_image_to_surface(tileImage.get(), get_current_palette(),
|
2020-07-14 04:32:42 +08:00
|
|
|
surface.get(), 0, 0, 0, 0, w, h);
|
2022-05-20 03:55:30 +08:00
|
|
|
|
|
|
|
ui::Paint paint;
|
|
|
|
paint.blendMode(os::BlendMode::SrcOver);
|
|
|
|
|
|
|
|
os::Sampling sampling;
|
|
|
|
if (w > box.w && h > box.h) {
|
|
|
|
sampling = os::Sampling(os::Sampling::Filter::Linear,
|
|
|
|
os::Sampling::Mipmap::Nearest);
|
|
|
|
}
|
|
|
|
|
|
|
|
g->drawSurface(surface.get(), gfx::Rect(0, 0, w, h), box,
|
|
|
|
sampling, &paint);
|
2019-03-30 02:57:10 +08:00
|
|
|
}
|
|
|
|
negColor = gfx::rgba(255, 255, 255);
|
|
|
|
}
|
|
|
|
doc::Tileset* tileset() const override {
|
|
|
|
Site site = App::instance()->context()->activeSite();
|
|
|
|
if (site.layer() &&
|
|
|
|
site.layer()->isTilemap()) {
|
|
|
|
return static_cast<LayerTilemap*>(site.layer())->tileset();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
};
|
|
|
|
|
2015-05-11 21:53:50 +08:00
|
|
|
PaletteView::PaletteView(bool editable, PaletteViewStyle style, PaletteViewDelegate* delegate, int boxsize)
|
2016-03-18 04:16:35 +08:00
|
|
|
: Widget(kGenericWidget)
|
2015-03-21 02:29:44 +08:00
|
|
|
, m_state(State::WAITING)
|
2015-03-20 06:45:12 +08:00
|
|
|
, m_editable(editable)
|
2015-05-11 21:53:50 +08:00
|
|
|
, m_style(style)
|
2015-03-23 23:25:32 +08:00
|
|
|
, m_delegate(delegate)
|
2019-03-30 02:57:10 +08:00
|
|
|
, m_adapter(isTiles() ? (AbstractPaletteViewAdapter*)new TilesetViewAdapter:
|
|
|
|
(AbstractPaletteViewAdapter*)new PaletteViewAdapter)
|
2015-03-20 06:45:12 +08:00
|
|
|
, m_columns(16)
|
2015-03-24 01:39:21 +08:00
|
|
|
, m_boxsize(boxsize)
|
2011-06-27 06:07:19 +08:00
|
|
|
, m_currentEntry(-1)
|
|
|
|
, m_rangeAnchor(-1)
|
2012-07-18 12:10:43 +08:00
|
|
|
, m_isUpdatingColumns(false)
|
2015-03-21 01:46:48 +08:00
|
|
|
, m_hot(Hit::NONE)
|
2015-05-10 02:57:46 +08:00
|
|
|
, m_copy(false)
|
2008-03-15 09:54:45 +08:00
|
|
|
{
|
2014-04-14 03:26:00 +08:00
|
|
|
setFocusStop(true);
|
|
|
|
setDoubleBuffered(true);
|
|
|
|
|
2018-10-19 02:29:16 +08:00
|
|
|
m_palConn = App::instance()->PaletteChange.connect(&PaletteView::onAppPaletteChange, this);
|
|
|
|
m_csConn = App::instance()->ColorSpaceChange.connect(
|
2020-07-04 08:51:46 +08:00
|
|
|
[this]{ invalidate(); });
|
2017-08-15 21:39:06 +08:00
|
|
|
|
2019-03-22 03:58:50 +08:00
|
|
|
{
|
|
|
|
auto& entriesSep = Preferences::instance().colorBar.entriesSeparator;
|
|
|
|
m_withSeparator = entriesSep();
|
|
|
|
m_sepConn = entriesSep.AfterChange.connect(
|
|
|
|
[this, &entriesSep](bool newValue) {
|
|
|
|
m_withSeparator = entriesSep();
|
|
|
|
updateBorderAndChildSpacing();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
if (isTiles())
|
|
|
|
UIContext::instance()->add_observer(this);
|
|
|
|
|
2017-08-15 21:39:06 +08:00
|
|
|
InitTheme.connect(
|
|
|
|
[this]{
|
2019-03-22 03:58:50 +08:00
|
|
|
updateBorderAndChildSpacing();
|
2017-08-15 21:39:06 +08:00
|
|
|
});
|
|
|
|
initTheme();
|
2008-03-15 09:54:45 +08:00
|
|
|
}
|
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
PaletteView::~PaletteView()
|
|
|
|
{
|
|
|
|
if (isTiles())
|
|
|
|
UIContext::instance()->remove_observer(this);
|
|
|
|
}
|
|
|
|
|
2011-01-28 20:28:54 +08:00
|
|
|
void PaletteView::setColumns(int columns)
|
2008-03-15 09:54:45 +08:00
|
|
|
{
|
2010-04-11 04:01:56 +08:00
|
|
|
int old_columns = m_columns;
|
|
|
|
m_columns = columns;
|
2008-03-15 09:54:45 +08:00
|
|
|
|
2010-04-11 04:01:56 +08:00
|
|
|
if (m_columns != old_columns) {
|
2011-02-21 05:35:21 +08:00
|
|
|
View* view = View::getView(this);
|
2007-09-19 07:57:02 +08:00
|
|
|
if (view)
|
2011-02-21 05:35:21 +08:00
|
|
|
view->updateView();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-11 06:23:30 +08:00
|
|
|
void PaletteView::deselect()
|
2011-06-27 06:07:19 +08:00
|
|
|
{
|
2015-05-10 06:55:33 +08:00
|
|
|
bool invalidate = (m_selectedEntries.picks() > 0);
|
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
m_selectedEntries.resize(m_adapter->size());
|
2015-06-18 23:50:33 +08:00
|
|
|
m_selectedEntries.clear();
|
2015-05-10 06:55:33 +08:00
|
|
|
|
|
|
|
if (invalidate)
|
|
|
|
this->invalidate();
|
2011-06-27 06:07:19 +08:00
|
|
|
}
|
|
|
|
|
2015-05-11 20:51:10 +08:00
|
|
|
void PaletteView::selectColor(int index)
|
2008-03-15 09:54:45 +08:00
|
|
|
{
|
2019-03-30 02:57:10 +08:00
|
|
|
if (index < 0 || index >= m_adapter->size())
|
2015-06-18 23:50:33 +08:00
|
|
|
return;
|
2008-03-15 09:54:45 +08:00
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
if (m_currentEntry != index || !m_selectedEntries[index]) {
|
|
|
|
m_currentEntry = index;
|
|
|
|
m_rangeAnchor = index;
|
2015-05-10 06:55:33 +08:00
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
update_scroll(m_currentEntry);
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-11 22:25:53 +08:00
|
|
|
void PaletteView::selectExactMatchColor(const app::Color& color)
|
|
|
|
{
|
|
|
|
int index = findExactIndex(color);
|
|
|
|
if (index >= 0)
|
|
|
|
selectColor(index);
|
|
|
|
}
|
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
void PaletteView::selectRange(int index1, int index2)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2011-06-27 06:07:19 +08:00
|
|
|
m_rangeAnchor = index1;
|
|
|
|
m_currentEntry = index2;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
std::fill(m_selectedEntries.begin()+std::min(index1, index2),
|
2012-01-06 06:45:03 +08:00
|
|
|
m_selectedEntries.begin()+std::max(index1, index2)+1, true);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
update_scroll(index2);
|
2011-01-22 06:45:04 +08:00
|
|
|
invalidate();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
int PaletteView::getSelectedEntry() const
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2011-06-27 06:07:19 +08:00
|
|
|
return m_currentEntry;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
bool PaletteView::getSelectedRange(int& index1, int& index2) const
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2011-06-27 06:07:19 +08:00
|
|
|
int i, i2, j;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
// Find the first selected entry
|
|
|
|
for (i=0; i<(int)m_selectedEntries.size(); ++i)
|
|
|
|
if (m_selectedEntries[i])
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
// Find the first unselected entry after i
|
|
|
|
for (i2=i+1; i2<(int)m_selectedEntries.size(); ++i2)
|
|
|
|
if (!m_selectedEntries[i2])
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
// Find the last selected entry
|
|
|
|
for (j=m_selectedEntries.size()-1; j>=0; --j)
|
|
|
|
if (m_selectedEntries[j])
|
|
|
|
break;
|
2008-03-15 09:54:45 +08:00
|
|
|
|
2011-06-27 06:07:19 +08:00
|
|
|
if (j-i+1 == i2-i) {
|
|
|
|
index1 = i;
|
|
|
|
index2 = j;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
2008-03-15 09:54:45 +08:00
|
|
|
}
|
|
|
|
|
2015-05-09 23:20:58 +08:00
|
|
|
void PaletteView::getSelectedEntries(PalettePicks& entries) const
|
2008-03-15 09:54:45 +08:00
|
|
|
{
|
2011-06-27 06:07:19 +08:00
|
|
|
entries = m_selectedEntries;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2015-05-10 06:55:33 +08:00
|
|
|
int PaletteView::getSelectedEntriesCount() const
|
|
|
|
{
|
|
|
|
return m_selectedEntries.picks();
|
|
|
|
}
|
|
|
|
|
2019-08-11 01:37:18 +08:00
|
|
|
void PaletteView::setSelectedEntries(const doc::PalettePicks& entries)
|
|
|
|
{
|
|
|
|
m_selectedEntries = entries;
|
2020-02-11 22:16:01 +08:00
|
|
|
m_selectedEntries.resize(m_adapter->size());
|
2019-08-11 01:37:18 +08:00
|
|
|
m_currentEntry = m_selectedEntries.firstPick();
|
2021-05-21 05:03:07 +08:00
|
|
|
|
|
|
|
if (entries.picks() > 0)
|
|
|
|
requestFocus();
|
|
|
|
|
2019-08-11 01:37:18 +08:00
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
doc::Tileset* PaletteView::tileset() const
|
|
|
|
{
|
|
|
|
return m_adapter->tileset();
|
|
|
|
}
|
|
|
|
|
2015-03-20 19:44:39 +08:00
|
|
|
app::Color PaletteView::getColorByPosition(const gfx::Point& pos)
|
2011-02-24 06:29:57 +08:00
|
|
|
{
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
gfx::Point relPos = pos - bounds().origin();
|
2019-03-30 02:57:10 +08:00
|
|
|
for (int i=0; i<m_adapter->size(); ++i) {
|
2021-05-24 02:28:39 +08:00
|
|
|
auto box = getPaletteEntryBounds(i);
|
|
|
|
box.inflate(childSpacing());
|
|
|
|
if (box.contains(relPos))
|
2015-03-20 19:44:39 +08:00
|
|
|
return app::Color::fromIndex(i);
|
2011-02-24 06:29:57 +08:00
|
|
|
}
|
2013-01-07 01:45:43 +08:00
|
|
|
return app::Color::fromMask();
|
2011-02-24 06:29:57 +08:00
|
|
|
}
|
|
|
|
|
2020-08-22 05:07:37 +08:00
|
|
|
doc::tile_t PaletteView::getTileByPosition(const gfx::Point& pos)
|
|
|
|
{
|
|
|
|
gfx::Point relPos = pos - bounds().origin();
|
|
|
|
for (int i=0; i<m_adapter->size(); ++i) {
|
2021-05-24 02:28:39 +08:00
|
|
|
auto box = getPaletteEntryBounds(i);
|
|
|
|
box.inflate(childSpacing());
|
|
|
|
if (box.contains(relPos))
|
2020-08-22 05:07:37 +08:00
|
|
|
return doc::tile(i, 0);
|
|
|
|
}
|
2020-10-31 03:33:34 +08:00
|
|
|
return doc::notile;
|
2020-08-22 05:07:37 +08:00
|
|
|
}
|
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
void PaletteView::onActiveSiteChange(const Site& site)
|
|
|
|
{
|
|
|
|
ASSERT(isTiles());
|
|
|
|
m_adapter->activeSiteChange(site, m_selectedEntries);
|
|
|
|
}
|
|
|
|
|
2015-05-09 01:59:52 +08:00
|
|
|
int PaletteView::getBoxSize() const
|
|
|
|
{
|
2017-08-15 21:39:06 +08:00
|
|
|
return int(m_boxsize);
|
2015-05-09 01:59:52 +08:00
|
|
|
}
|
|
|
|
|
2016-11-15 03:12:11 +08:00
|
|
|
void PaletteView::setBoxSize(double boxsize)
|
2015-05-09 01:59:52 +08:00
|
|
|
{
|
2019-03-30 02:57:10 +08:00
|
|
|
if (isTiles())
|
2022-06-10 21:31:13 +08:00
|
|
|
m_boxsize = std::clamp(boxsize, 4.0, 64.0);
|
2019-03-30 02:57:10 +08:00
|
|
|
else
|
2022-06-10 21:31:13 +08:00
|
|
|
m_boxsize = std::clamp(boxsize, 4.0, 32.0);
|
2015-05-09 01:59:52 +08:00
|
|
|
|
|
|
|
if (m_delegate)
|
2021-04-30 22:31:02 +08:00
|
|
|
m_delegate->onPaletteViewChangeSize(this, int(m_boxsize));
|
2015-05-09 01:59:52 +08:00
|
|
|
|
|
|
|
View* view = View::getView(this);
|
|
|
|
if (view)
|
|
|
|
view->layout();
|
|
|
|
}
|
|
|
|
|
2015-06-19 03:20:42 +08:00
|
|
|
void PaletteView::clearSelection()
|
2015-05-09 23:20:58 +08:00
|
|
|
{
|
2015-05-11 08:36:46 +08:00
|
|
|
if (!m_selectedEntries.picks())
|
|
|
|
return;
|
2015-05-09 23:20:58 +08:00
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
m_adapter->clearSelection(this, m_selectedEntries);
|
2015-05-11 08:36:46 +08:00
|
|
|
|
|
|
|
m_currentEntry = m_selectedEntries.firstPick();
|
|
|
|
m_selectedEntries.clear();
|
|
|
|
stopMarchingAnts();
|
2015-06-19 03:20:42 +08:00
|
|
|
}
|
2015-05-11 08:36:46 +08:00
|
|
|
|
2015-06-19 03:20:42 +08:00
|
|
|
void PaletteView::cutToClipboard()
|
|
|
|
{
|
|
|
|
if (!m_selectedEntries.picks())
|
|
|
|
return;
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
Clipboard::instance()->copyPalette(currentPalette(), m_selectedEntries);
|
2015-06-19 03:20:42 +08:00
|
|
|
|
|
|
|
clearSelection();
|
2015-05-11 08:36:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void PaletteView::copyToClipboard()
|
|
|
|
{
|
|
|
|
if (!m_selectedEntries.picks())
|
|
|
|
return;
|
|
|
|
|
2020-09-25 22:13:52 +08:00
|
|
|
Clipboard::instance()->copyPalette(currentPalette(), m_selectedEntries);
|
2015-05-11 08:36:46 +08:00
|
|
|
|
|
|
|
startMarchingAnts();
|
|
|
|
invalidate();
|
2015-05-09 23:20:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void PaletteView::pasteFromClipboard()
|
|
|
|
{
|
2020-09-25 22:13:52 +08:00
|
|
|
auto clipboard = Clipboard::instance();
|
|
|
|
if (clipboard->format() == ClipboardFormat::PaletteEntries) {
|
2015-05-09 23:20:58 +08:00
|
|
|
if (m_delegate)
|
|
|
|
m_delegate->onPaletteViewPasteColors(
|
2020-09-25 22:13:52 +08:00
|
|
|
clipboard->getPalette(),
|
|
|
|
clipboard->getPalettePicks(),
|
2015-05-11 08:36:46 +08:00
|
|
|
m_selectedEntries);
|
2015-05-09 23:20:58 +08:00
|
|
|
|
2015-05-10 06:55:33 +08:00
|
|
|
// We just hide the marching ants, the user can paste multiple
|
|
|
|
// times.
|
2015-05-09 23:20:58 +08:00
|
|
|
stopMarchingAnts();
|
2015-05-10 06:55:33 +08:00
|
|
|
invalidate();
|
2015-05-09 23:20:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-10 06:55:33 +08:00
|
|
|
void PaletteView::discardClipboardSelection()
|
|
|
|
{
|
2015-05-10 07:27:40 +08:00
|
|
|
if (isMarchingAntsRunning()) {
|
2015-05-10 06:55:33 +08:00
|
|
|
stopMarchingAnts();
|
|
|
|
invalidate();
|
2015-05-11 08:36:46 +08:00
|
|
|
}
|
2015-05-09 23:20:58 +08:00
|
|
|
}
|
|
|
|
|
2011-04-03 00:14:07 +08:00
|
|
|
bool PaletteView::onProcessMessage(Message* msg)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2013-07-29 08:17:07 +08:00
|
|
|
switch (msg->type()) {
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2015-05-10 06:55:33 +08:00
|
|
|
case kFocusEnterMessage:
|
2018-06-06 00:11:29 +08:00
|
|
|
FocusOrClick(msg);
|
2015-05-09 23:20:58 +08:00
|
|
|
break;
|
|
|
|
|
2015-05-10 06:55:33 +08:00
|
|
|
case kKeyDownMessage:
|
2015-05-10 02:57:46 +08:00
|
|
|
case kKeyUpMessage:
|
2015-05-10 06:55:33 +08:00
|
|
|
case kMouseEnterMessage:
|
2016-04-26 03:17:30 +08:00
|
|
|
if (hasMouse())
|
|
|
|
updateCopyFlag(msg);
|
2015-05-10 02:57:46 +08:00
|
|
|
break;
|
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseDownMessage:
|
2015-03-21 02:29:44 +08:00
|
|
|
switch (m_hot.part) {
|
2015-03-23 23:25:32 +08:00
|
|
|
|
2015-03-21 02:29:44 +08:00
|
|
|
case Hit::COLOR:
|
2022-06-02 21:31:15 +08:00
|
|
|
case Hit::POSSIBLE_COLOR:
|
2022-05-14 02:19:18 +08:00
|
|
|
// Clicking outside the palette range will deselect
|
2022-06-02 21:31:15 +08:00
|
|
|
if (m_hot.color >= m_adapter->size()) {
|
2022-05-14 02:19:18 +08:00
|
|
|
deselect();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-03-21 02:29:44 +08:00
|
|
|
m_state = State::SELECTING_COLOR;
|
2018-06-06 00:11:29 +08:00
|
|
|
|
|
|
|
// As we can ctrl+click color bar + timeline, now we have to
|
|
|
|
// re-prioritize the color bar on each click.
|
|
|
|
FocusOrClick(msg);
|
2015-03-21 02:29:44 +08:00
|
|
|
break;
|
2015-03-23 23:25:32 +08:00
|
|
|
|
2015-03-21 02:29:44 +08:00
|
|
|
case Hit::OUTLINE:
|
|
|
|
m_state = State::DRAGGING_OUTLINE;
|
|
|
|
break;
|
2015-08-20 23:08:08 +08:00
|
|
|
|
|
|
|
case Hit::RESIZE_HANDLE:
|
|
|
|
m_state = State::RESIZING_PALETTE;
|
|
|
|
break;
|
2015-03-21 02:29:44 +08:00
|
|
|
}
|
|
|
|
|
2010-04-25 23:03:25 +08:00
|
|
|
captureMouse();
|
2022-06-30 07:14:03 +08:00
|
|
|
[[fallthrough]];
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseMoveMessage: {
|
2013-07-29 08:17:07 +08:00
|
|
|
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
2011-02-24 06:29:57 +08:00
|
|
|
|
2022-06-02 21:32:15 +08:00
|
|
|
if ((m_state == State::SELECTING_COLOR) &&
|
|
|
|
(m_hot.part == Hit::COLOR ||
|
|
|
|
m_hot.part == Hit::POSSIBLE_COLOR)) {
|
2015-07-05 06:11:50 +08:00
|
|
|
int idx = m_hot.color;
|
2022-06-10 21:31:13 +08:00
|
|
|
idx = std::clamp(idx, 0, std::max(0, m_adapter->size()-1));
|
2015-07-05 06:11:50 +08:00
|
|
|
|
2020-03-28 02:40:43 +08:00
|
|
|
const MouseButton button = mouseMsg->button();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2020-10-24 03:07:54 +08:00
|
|
|
if (hasCapture() &&
|
|
|
|
(idx >= 0 && idx < m_adapter->size()) &&
|
|
|
|
((idx != m_currentEntry) ||
|
|
|
|
(msg->type() == kMouseDownMessage) ||
|
|
|
|
(button == kButtonMiddle))) {
|
2020-03-28 02:40:43 +08:00
|
|
|
if (button != kButtonMiddle) {
|
2018-06-06 00:11:29 +08:00
|
|
|
if (!msg->ctrlPressed() && !msg->shiftPressed())
|
2015-07-05 06:11:50 +08:00
|
|
|
deselect();
|
|
|
|
|
|
|
|
if (msg->type() == kMouseMoveMessage)
|
|
|
|
selectRange(m_rangeAnchor, idx);
|
|
|
|
else {
|
|
|
|
selectColor(idx);
|
|
|
|
m_selectedEntries[idx] = true;
|
|
|
|
}
|
2015-03-21 02:29:44 +08:00
|
|
|
}
|
2015-07-05 06:11:50 +08:00
|
|
|
|
2020-05-19 07:24:22 +08:00
|
|
|
m_adapter->selectIndex(this, idx, button);
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
2011-02-24 06:29:57 +08:00
|
|
|
}
|
2018-08-03 21:14:35 +08:00
|
|
|
|
2018-07-28 04:49:06 +08:00
|
|
|
if (m_state == State::DRAGGING_OUTLINE &&
|
|
|
|
m_hot.part == Hit::COLOR) {
|
|
|
|
update_scroll(m_hot.color);
|
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-02-24 06:29:57 +08:00
|
|
|
if (hasCapture())
|
2012-01-06 06:45:03 +08:00
|
|
|
return true;
|
2011-02-24 06:29:57 +08:00
|
|
|
|
2007-09-19 07:57:02 +08:00
|
|
|
break;
|
2011-02-24 06:29:57 +08:00
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseUpMessage:
|
2015-03-21 02:29:44 +08:00
|
|
|
if (hasCapture()) {
|
2015-03-21 01:46:48 +08:00
|
|
|
releaseMouse();
|
2015-03-21 02:29:44 +08:00
|
|
|
|
2015-08-20 23:08:08 +08:00
|
|
|
switch (m_state) {
|
|
|
|
|
|
|
|
case State::DRAGGING_OUTLINE:
|
2015-08-27 19:09:13 +08:00
|
|
|
if (m_hot.part == Hit::COLOR ||
|
|
|
|
m_hot.part == Hit::POSSIBLE_COLOR) {
|
2015-08-20 23:08:08 +08:00
|
|
|
int i = m_hot.color;
|
|
|
|
if (!m_copy && i > m_selectedEntries.firstPick())
|
|
|
|
i += m_selectedEntries.picks();
|
|
|
|
dropColors(i);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case State::RESIZING_PALETTE:
|
2015-08-27 19:09:13 +08:00
|
|
|
if (m_hot.part == Hit::COLOR ||
|
|
|
|
m_hot.part == Hit::POSSIBLE_COLOR) {
|
2019-11-16 23:09:33 +08:00
|
|
|
int newSize = std::max(1, m_hot.color);
|
|
|
|
m_adapter->resizePalette(this, newSize);
|
2020-02-11 22:16:01 +08:00
|
|
|
m_selectedEntries.resize(newSize);
|
2015-08-20 23:08:08 +08:00
|
|
|
}
|
|
|
|
break;
|
2015-03-23 23:25:32 +08:00
|
|
|
}
|
|
|
|
|
2015-03-21 02:29:44 +08:00
|
|
|
m_state = State::WAITING;
|
2018-07-05 23:39:49 +08:00
|
|
|
setStatusBar();
|
2015-03-21 02:29:44 +08:00
|
|
|
invalidate();
|
|
|
|
}
|
2010-01-31 00:43:13 +08:00
|
|
|
return true;
|
2011-02-20 10:44:17 +08:00
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseWheelMessage: {
|
2011-02-21 05:35:21 +08:00
|
|
|
View* view = View::getView(this);
|
2015-03-24 00:28:16 +08:00
|
|
|
if (!view)
|
|
|
|
break;
|
|
|
|
|
|
|
|
gfx::Point delta = static_cast<MouseMessage*>(msg)->wheelDelta();
|
|
|
|
|
2016-11-22 20:25:23 +08:00
|
|
|
if (msg->onlyCtrlPressed() ||
|
|
|
|
msg->onlyCmdPressed()) {
|
2015-03-24 00:28:16 +08:00
|
|
|
int z = delta.x - delta.y;
|
2015-07-07 03:46:12 +08:00
|
|
|
setBoxSize(m_boxsize + z);
|
2015-03-24 00:28:16 +08:00
|
|
|
}
|
|
|
|
else {
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
gfx::Point scroll = view->viewScroll();
|
2016-11-22 20:25:23 +08:00
|
|
|
|
|
|
|
if (static_cast<MouseMessage*>(msg)->preciseWheel())
|
|
|
|
scroll += delta;
|
|
|
|
else
|
2019-03-22 03:58:50 +08:00
|
|
|
scroll += delta * 3 * boxSizePx();
|
2016-11-22 20:25:23 +08:00
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
view->setViewScroll(scroll);
|
2011-02-20 10:44:17 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-04-05 08:53:29 +08:00
|
|
|
case kMouseLeaveMessage:
|
2015-03-21 01:46:48 +08:00
|
|
|
m_hot = Hit(Hit::NONE);
|
2018-07-05 23:39:49 +08:00
|
|
|
setStatusBar();
|
2015-03-21 01:46:48 +08:00
|
|
|
invalidate();
|
2011-02-24 06:29:57 +08:00
|
|
|
break;
|
|
|
|
|
2015-03-21 01:46:48 +08:00
|
|
|
case kSetCursorMessage: {
|
|
|
|
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
Hit hit = hitTest(mouseMsg->position() - bounds().origin());
|
2015-03-21 01:46:48 +08:00
|
|
|
if (hit != m_hot) {
|
2017-11-03 21:01:51 +08:00
|
|
|
// Redraw only when we put the mouse in other part of the
|
|
|
|
// widget (e.g. if we move from color to color, we don't want
|
2017-11-17 22:26:40 +08:00
|
|
|
// to redraw the whole widget if we're on WAITING state).
|
|
|
|
if ((m_state == State::WAITING && hit.part != m_hot.part) ||
|
|
|
|
(m_state != State::WAITING && hit != m_hot)) {
|
2017-11-03 21:01:51 +08:00
|
|
|
invalidate();
|
2017-11-17 22:26:40 +08:00
|
|
|
}
|
2015-03-21 01:46:48 +08:00
|
|
|
m_hot = hit;
|
2018-07-05 23:39:49 +08:00
|
|
|
setStatusBar();
|
2015-03-21 01:46:48 +08:00
|
|
|
}
|
2015-05-10 02:57:46 +08:00
|
|
|
setCursor();
|
2015-03-21 01:46:48 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-11-15 03:12:11 +08:00
|
|
|
case kTouchMagnifyMessage: {
|
2019-03-22 03:58:50 +08:00
|
|
|
setBoxSize(m_boxsize +
|
|
|
|
m_boxsize * static_cast<ui::TouchMessage*>(msg)->magnification());
|
2016-11-15 03:12:11 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2010-08-03 08:29:56 +08:00
|
|
|
return Widget::onProcessMessage(msg);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2014-04-14 03:26:00 +08:00
|
|
|
void PaletteView::onPaint(ui::PaintEvent& ev)
|
|
|
|
{
|
2022-02-19 06:01:46 +08:00
|
|
|
auto theme = SkinTheme::get(this);
|
2019-03-22 03:58:50 +08:00
|
|
|
const int outlineWidth = theme->dimensions.paletteOutlineWidth();
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
ui::Graphics* g = ev.graphics();
|
|
|
|
gfx::Rect bounds = clientBounds();
|
2015-06-18 23:50:33 +08:00
|
|
|
Palette* palette = currentPalette();
|
2015-05-11 21:53:50 +08:00
|
|
|
int fgIndex = -1;
|
|
|
|
int bgIndex = -1;
|
2015-05-20 22:40:22 +08:00
|
|
|
int transparentIndex = -1;
|
2019-03-21 01:27:19 +08:00
|
|
|
const bool hotColor = (m_hot.part == Hit::COLOR ||
|
|
|
|
m_hot.part == Hit::POSSIBLE_COLOR);
|
|
|
|
const bool dragging = (m_state == State::DRAGGING_OUTLINE && hotColor);
|
|
|
|
const bool resizing = (m_state == State::RESIZING_PALETTE && hotColor);
|
2015-05-11 21:53:50 +08:00
|
|
|
|
2021-05-24 02:28:39 +08:00
|
|
|
if (m_delegate) {
|
|
|
|
switch (m_style) {
|
|
|
|
|
2022-10-20 23:31:22 +08:00
|
|
|
case FgBgColors: {
|
2021-05-24 02:28:39 +08:00
|
|
|
fgIndex = findExactIndex(m_delegate->onPaletteViewGetForegroundIndex());
|
|
|
|
bgIndex = findExactIndex(m_delegate->onPaletteViewGetBackgroundIndex());
|
|
|
|
|
2022-10-20 23:31:22 +08:00
|
|
|
auto editor = Editor::activeEditor();
|
|
|
|
if (editor && editor->sprite()->pixelFormat() == IMAGE_INDEXED)
|
|
|
|
transparentIndex = editor->sprite()->transparentColor();
|
2021-05-24 02:28:39 +08:00
|
|
|
break;
|
2022-10-20 23:31:22 +08:00
|
|
|
}
|
2015-05-20 22:40:22 +08:00
|
|
|
|
2021-05-24 02:28:39 +08:00
|
|
|
case FgBgTiles:
|
|
|
|
fgIndex = m_delegate->onPaletteViewGetForegroundTile();
|
|
|
|
bgIndex = m_delegate->onPaletteViewGetBackgroundTile();
|
|
|
|
break;
|
|
|
|
}
|
2015-05-11 21:53:50 +08:00
|
|
|
}
|
2014-04-14 03:26:00 +08:00
|
|
|
|
2015-06-18 23:50:33 +08:00
|
|
|
g->fillRect(theme->colors.editorFace(), bounds);
|
2014-04-14 03:26:00 +08:00
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
// Draw palette/tileset entries
|
2015-07-05 06:11:50 +08:00
|
|
|
int picksCount = m_selectedEntries.picks();
|
|
|
|
int idxOffset = 0;
|
|
|
|
int boxOffset = 0;
|
2019-03-30 02:57:10 +08:00
|
|
|
int palSize = m_adapter->size();
|
2015-08-20 23:08:08 +08:00
|
|
|
if (dragging && !m_copy) palSize -= picksCount;
|
|
|
|
if (resizing) palSize = m_hot.color;
|
|
|
|
|
|
|
|
for (int i=0; i<palSize; ++i) {
|
|
|
|
if (dragging) {
|
2015-07-05 06:11:50 +08:00
|
|
|
if (!m_copy) {
|
2015-07-07 00:56:53 +08:00
|
|
|
while (i+idxOffset < m_selectedEntries.size() &&
|
|
|
|
m_selectedEntries[i+idxOffset])
|
2015-07-05 06:11:50 +08:00
|
|
|
++idxOffset;
|
|
|
|
}
|
|
|
|
if (!boxOffset && m_hot.color == i) {
|
|
|
|
boxOffset += picksCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gfx::Rect box = getPaletteEntryBounds(i + boxOffset);
|
2019-03-22 03:58:50 +08:00
|
|
|
gfx::Color negColor;
|
2019-03-30 02:57:10 +08:00
|
|
|
m_adapter->drawEntry(g, theme, i + idxOffset, i + boxOffset,
|
|
|
|
childSpacing(), box, negColor);
|
2019-03-22 03:58:50 +08:00
|
|
|
const int boxsize = boxSizePx();
|
2021-05-24 02:28:39 +08:00
|
|
|
const int scale = guiscale();
|
2015-03-21 01:46:48 +08:00
|
|
|
|
2022-01-15 03:44:13 +08:00
|
|
|
switch (m_style) {
|
2015-05-11 21:53:50 +08:00
|
|
|
|
2022-01-15 03:44:13 +08:00
|
|
|
case SelectOneColor:
|
|
|
|
if (m_currentEntry == i)
|
|
|
|
g->fillRect(negColor, gfx::Rect(box.center(), gfx::Size(scale, scale)));
|
|
|
|
break;
|
2015-05-11 21:53:50 +08:00
|
|
|
|
2022-01-15 03:44:13 +08:00
|
|
|
case FgBgColors:
|
|
|
|
case FgBgTiles:
|
|
|
|
if (!m_delegate || m_delegate->onIsPaletteViewActive(this)) {
|
2021-05-24 02:28:39 +08:00
|
|
|
if (fgIndex == i) {
|
|
|
|
for (int i=0; i<int(boxsize/2); i += scale) {
|
|
|
|
g->fillRect(negColor,
|
|
|
|
gfx::Rect(box.x, box.y+i, int(boxsize/2)-i, scale));
|
|
|
|
}
|
|
|
|
}
|
2015-05-11 21:53:50 +08:00
|
|
|
|
2021-05-24 02:28:39 +08:00
|
|
|
if (bgIndex == i) {
|
|
|
|
for (int i=0; i<int(boxsize/4); i += scale) {
|
|
|
|
g->fillRect(negColor,
|
|
|
|
gfx::Rect(box.x+box.w-(i+scale),
|
|
|
|
box.y+box.h-int(boxsize/4)+i,
|
|
|
|
i+scale, scale));
|
|
|
|
}
|
|
|
|
}
|
2015-05-20 22:40:22 +08:00
|
|
|
|
2021-05-24 02:28:39 +08:00
|
|
|
if (transparentIndex == i)
|
|
|
|
g->fillRect(negColor, gfx::Rect(box.center(), gfx::Size(scale, scale)));
|
2022-01-15 03:44:13 +08:00
|
|
|
}
|
|
|
|
break;
|
2015-05-11 21:53:50 +08:00
|
|
|
}
|
2015-03-20 06:45:12 +08:00
|
|
|
}
|
|
|
|
|
2015-08-20 23:08:08 +08:00
|
|
|
// Handle to resize palette
|
|
|
|
|
|
|
|
if (m_editable && !dragging) {
|
2018-08-09 23:58:43 +08:00
|
|
|
os::Surface* handle = theme->parts.palResize()->bitmap(0);
|
2015-08-20 23:08:08 +08:00
|
|
|
gfx::Rect box = getPaletteEntryBounds(palSize);
|
|
|
|
g->drawRgbaSurface(handle,
|
|
|
|
box.x+box.w/2-handle->width()/2,
|
|
|
|
box.y+box.h/2-handle->height()/2);
|
|
|
|
}
|
|
|
|
|
2015-03-20 06:45:12 +08:00
|
|
|
// Draw selected entries
|
2015-07-05 06:11:50 +08:00
|
|
|
|
2017-03-14 05:13:38 +08:00
|
|
|
PaintWidgetPartInfo info;
|
|
|
|
if (m_hot.part == Hit::OUTLINE)
|
|
|
|
info.styleFlags |= ui::Style::Layer::kMouse;
|
2015-03-21 01:46:48 +08:00
|
|
|
|
2015-07-05 06:11:50 +08:00
|
|
|
PalettePicks dragPicks;
|
|
|
|
int j = 0;
|
2015-08-20 23:08:08 +08:00
|
|
|
if (dragging) {
|
2015-07-05 06:11:50 +08:00
|
|
|
dragPicks.resize(m_hot.color+picksCount);
|
|
|
|
std::fill(dragPicks.begin()+m_hot.color, dragPicks.end(), true);
|
|
|
|
}
|
2015-08-20 23:08:08 +08:00
|
|
|
PalettePicks& picks = (dragging ? dragPicks: m_selectedEntries);
|
2015-07-05 06:11:50 +08:00
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
const int size = m_adapter->size();
|
|
|
|
for (int i=0; i<size; ++i) {
|
2019-08-05 19:36:47 +08:00
|
|
|
// TODO why does this fail?
|
|
|
|
//ASSERT(i >= 0 && i < m_selectedEntries.size());
|
|
|
|
if (i >= 0 && i < m_selectedEntries.size() &&
|
|
|
|
!m_selectedEntries[i]) {
|
2015-03-20 19:44:39 +08:00
|
|
|
continue;
|
2019-08-05 19:36:47 +08:00
|
|
|
}
|
2015-03-20 19:44:39 +08:00
|
|
|
|
2019-03-22 03:58:50 +08:00
|
|
|
const int k = (dragging ? m_hot.color+j: i);
|
2015-07-05 06:11:50 +08:00
|
|
|
|
2015-05-09 23:20:58 +08:00
|
|
|
gfx::Rect box, clipR;
|
2019-03-22 03:58:50 +08:00
|
|
|
getEntryBoundsAndClip(k, picks, outlineWidth, box, clipR);
|
2015-03-20 19:44:39 +08:00
|
|
|
|
|
|
|
IntersectClip clip(g, clipR);
|
|
|
|
if (clip) {
|
2015-07-05 06:11:50 +08:00
|
|
|
// Draw color being dragged + label
|
2015-08-20 23:08:08 +08:00
|
|
|
if (dragging) {
|
2015-07-05 06:11:50 +08:00
|
|
|
gfx::Rect box2 = getPaletteEntryBounds(k);
|
2019-03-22 03:58:50 +08:00
|
|
|
gfx::Color negColor;
|
2019-03-30 02:57:10 +08:00
|
|
|
m_adapter->drawEntry(g, theme, i, k, childSpacing(),
|
|
|
|
box2, negColor);
|
2015-07-05 06:11:50 +08:00
|
|
|
|
2024-11-30 04:57:41 +08:00
|
|
|
text::FontRef minifont = theme->getMiniFont();
|
2019-03-22 03:58:50 +08:00
|
|
|
const std::string text = base::convert_to<std::string>(k);
|
2024-11-30 04:57:41 +08:00
|
|
|
g->setFont(minifont);
|
2019-03-30 02:57:10 +08:00
|
|
|
g->drawText(text, negColor, gfx::ColorNone,
|
2017-02-07 04:58:55 +08:00
|
|
|
gfx::Point(box2.x + box2.w/2 - minifont->textLength(text)/2,
|
|
|
|
box2.y + box2.h/2 - minifont->height()/2));
|
2015-07-05 06:11:50 +08:00
|
|
|
}
|
|
|
|
|
2017-03-14 05:13:38 +08:00
|
|
|
// Draw the selection
|
|
|
|
theme->paintWidgetPart(
|
2017-03-14 09:31:57 +08:00
|
|
|
g, theme->styles.colorbarSelection(), box, info);
|
2014-04-14 03:26:00 +08:00
|
|
|
}
|
2015-07-05 06:11:50 +08:00
|
|
|
|
|
|
|
++j;
|
2014-04-14 03:26:00 +08:00
|
|
|
}
|
2015-03-21 02:29:44 +08:00
|
|
|
|
2015-05-09 23:20:58 +08:00
|
|
|
// Draw marching ants
|
2015-07-05 06:11:50 +08:00
|
|
|
if ((m_state == State::WAITING) &&
|
|
|
|
(isMarchingAntsRunning()) &&
|
2020-09-25 22:13:52 +08:00
|
|
|
(Clipboard::instance()->format() == ClipboardFormat::PaletteEntries)) {
|
|
|
|
auto clipboard = Clipboard::instance();
|
|
|
|
Palette* clipboardPalette = clipboard->getPalette();
|
|
|
|
const PalettePicks& clipboardPicks = clipboard->getPalettePicks();
|
2015-05-11 08:36:46 +08:00
|
|
|
|
|
|
|
if (clipboardPalette &&
|
|
|
|
clipboardPalette->countDiff(palette, nullptr, nullptr) == 0) {
|
|
|
|
for (int i=0; i<clipboardPicks.size(); ++i) {
|
|
|
|
if (!clipboardPicks[i])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
gfx::Rect box, clipR;
|
2019-03-22 03:58:50 +08:00
|
|
|
getEntryBoundsAndClip(i, clipboardPicks, 1*guiscale(), box, clipR);
|
2015-05-11 08:36:46 +08:00
|
|
|
|
|
|
|
IntersectClip clip(g, clipR);
|
|
|
|
if (clip) {
|
2022-09-29 02:49:30 +08:00
|
|
|
ui::Paint paint;
|
|
|
|
paint.style(ui::Paint::Stroke);
|
|
|
|
ui::set_checkered_paint_mode(paint, getMarchingAntsOffset(),
|
|
|
|
gfx::rgba(0, 0, 0, 255),
|
|
|
|
gfx::rgba(255, 255, 255, 255));
|
|
|
|
g->drawRect(box, paint);
|
2015-05-11 08:36:46 +08:00
|
|
|
}
|
2015-05-09 23:20:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-04-14 03:26:00 +08:00
|
|
|
}
|
|
|
|
|
2013-05-12 04:56:27 +08:00
|
|
|
void PaletteView::onResize(ui::ResizeEvent& ev)
|
|
|
|
{
|
|
|
|
if (!m_isUpdatingColumns) {
|
|
|
|
m_isUpdatingColumns = true;
|
|
|
|
View* view = View::getView(this);
|
|
|
|
if (view) {
|
|
|
|
int columns =
|
2019-03-22 03:58:50 +08:00
|
|
|
(view->viewportBounds().w
|
|
|
|
-this->border().width()
|
|
|
|
+this->childSpacing())
|
|
|
|
/ (boxSizePx()
|
|
|
|
+this->childSpacing());
|
2020-04-08 23:03:32 +08:00
|
|
|
setColumns(std::max(1, columns));
|
2013-05-12 04:56:27 +08:00
|
|
|
}
|
|
|
|
m_isUpdatingColumns = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Widget::onResize(ev);
|
|
|
|
}
|
|
|
|
|
2015-12-04 08:50:05 +08:00
|
|
|
void PaletteView::onSizeHint(ui::SizeHintEvent& ev)
|
2012-09-27 05:34:52 +08:00
|
|
|
{
|
2019-03-30 02:57:10 +08:00
|
|
|
div_t d = div(m_adapter->size(), m_columns);
|
2015-08-27 19:09:13 +08:00
|
|
|
int cols = m_columns;
|
|
|
|
int rows = d.quot + ((d.rem)? 1: 0);
|
|
|
|
|
|
|
|
if (m_editable) {
|
|
|
|
++rows;
|
|
|
|
}
|
|
|
|
|
2019-03-22 03:58:50 +08:00
|
|
|
const int boxsize = boxSizePx();
|
2017-08-15 21:39:06 +08:00
|
|
|
ev.setSizeHint(
|
|
|
|
gfx::Size(
|
|
|
|
border().width() + cols*boxsize + (cols-1)*childSpacing(),
|
|
|
|
border().height() + rows*boxsize + (rows-1)*childSpacing()));
|
2012-09-27 05:34:52 +08:00
|
|
|
}
|
|
|
|
|
2015-05-09 23:20:58 +08:00
|
|
|
void PaletteView::onDrawMarchingAnts()
|
|
|
|
{
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2011-01-28 20:28:54 +08:00
|
|
|
void PaletteView::update_scroll(int color)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2011-02-21 05:35:21 +08:00
|
|
|
View* view = View::getView(this);
|
|
|
|
if (!view)
|
|
|
|
return;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
gfx::Rect vp = view->viewportBounds();
|
2011-02-21 05:35:21 +08:00
|
|
|
gfx::Point scroll;
|
|
|
|
int x, y, cols;
|
|
|
|
div_t d;
|
2019-03-22 03:58:50 +08:00
|
|
|
const int boxsize = boxSizePx();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
scroll = view->viewScroll();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
d = div(m_adapter->size(), m_columns);
|
2011-02-21 05:35:21 +08:00
|
|
|
cols = m_columns;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2017-08-15 21:39:06 +08:00
|
|
|
y = (boxsize+childSpacing()) * (color / cols);
|
|
|
|
x = (boxsize+childSpacing()) * (color % cols);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-02-21 05:35:21 +08:00
|
|
|
if (scroll.x > x)
|
|
|
|
scroll.x = x;
|
2017-08-15 21:39:06 +08:00
|
|
|
else if (scroll.x+vp.w-boxsize-2 < x)
|
|
|
|
scroll.x = x-vp.w+boxsize+2;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-02-21 05:35:21 +08:00
|
|
|
if (scroll.y > y)
|
|
|
|
scroll.y = y;
|
2017-08-15 21:39:06 +08:00
|
|
|
else if (scroll.y+vp.h-boxsize-2 < y)
|
|
|
|
scroll.y = y-vp.h+boxsize+2;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2011-02-21 05:35:21 +08:00
|
|
|
view->setViewScroll(scroll);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2013-08-06 08:20:19 +08:00
|
|
|
|
2014-06-02 05:09:19 +08:00
|
|
|
void PaletteView::onAppPaletteChange()
|
|
|
|
{
|
2019-03-30 02:57:10 +08:00
|
|
|
m_adapter->paletteChange(m_selectedEntries);
|
2015-06-18 23:50:33 +08:00
|
|
|
|
|
|
|
View* view = View::getView(this);
|
|
|
|
if (view)
|
|
|
|
view->layout();
|
2014-06-02 05:09:19 +08:00
|
|
|
}
|
|
|
|
|
2015-05-09 23:20:58 +08:00
|
|
|
gfx::Rect PaletteView::getPaletteEntryBounds(int index) const
|
2015-03-20 19:44:39 +08:00
|
|
|
{
|
2019-03-22 03:58:50 +08:00
|
|
|
const gfx::Rect bounds = clientChildrenBounds();
|
|
|
|
const int col = index % m_columns;
|
|
|
|
const int row = index / m_columns;
|
|
|
|
const int boxsize = boxSizePx();
|
|
|
|
const int spacing = childSpacing();
|
2015-03-20 19:44:39 +08:00
|
|
|
|
|
|
|
return gfx::Rect(
|
2019-03-22 03:58:50 +08:00
|
|
|
bounds.x + col*boxsize + (col-1)*spacing + (m_withSeparator ? border().left(): 0),
|
|
|
|
bounds.y + row*boxsize + (row-1)*spacing + (m_withSeparator ? border().top(): 0),
|
2017-08-15 21:39:06 +08:00
|
|
|
boxsize, boxsize);
|
2015-03-20 19:44:39 +08:00
|
|
|
}
|
|
|
|
|
2015-03-21 01:46:48 +08:00
|
|
|
PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos)
|
|
|
|
{
|
2022-02-19 06:01:46 +08:00
|
|
|
auto theme = SkinTheme::get(this);
|
2019-03-22 03:58:50 +08:00
|
|
|
const int outlineWidth = theme->dimensions.paletteOutlineWidth();
|
2019-03-30 02:57:10 +08:00
|
|
|
const int size = m_adapter->size();
|
2015-03-21 01:46:48 +08:00
|
|
|
|
2015-03-23 23:25:32 +08:00
|
|
|
if (m_state == State::WAITING && m_editable) {
|
2015-03-21 01:46:48 +08:00
|
|
|
// First check if the mouse is inside the selection outline.
|
2019-03-30 02:57:10 +08:00
|
|
|
for (int i=0; i<size; ++i) {
|
2015-03-21 01:46:48 +08:00
|
|
|
if (!m_selectedEntries[i])
|
|
|
|
continue;
|
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
bool top = (i >= m_columns && i-m_columns >= 0 ? m_selectedEntries[i-m_columns]: false);
|
|
|
|
bool bottom = (i < size-m_columns && i+m_columns < size ? m_selectedEntries[i+m_columns]: false);
|
|
|
|
bool left = ((i%m_columns)>0 && i-1 >= 0 ? m_selectedEntries[i-1]: false);
|
|
|
|
bool right = ((i%m_columns)<m_columns-1 && i+1 < size ? m_selectedEntries[i+1]: false);
|
2015-03-21 01:46:48 +08:00
|
|
|
|
|
|
|
gfx::Rect box = getPaletteEntryBounds(i);
|
|
|
|
box.enlarge(outlineWidth);
|
|
|
|
|
|
|
|
if ((!top && gfx::Rect(box.x, box.y, box.w, outlineWidth).contains(pos)) ||
|
2015-03-21 02:29:44 +08:00
|
|
|
(!bottom && gfx::Rect(box.x, box.y+box.h-outlineWidth, box.w, outlineWidth).contains(pos)) ||
|
|
|
|
(!left && gfx::Rect(box.x, box.y, outlineWidth, box.h).contains(pos)) ||
|
|
|
|
(!right && gfx::Rect(box.x+box.w-outlineWidth, box.y, outlineWidth, box.h).contains(pos)))
|
2015-03-21 01:46:48 +08:00
|
|
|
return Hit(Hit::OUTLINE, i);
|
|
|
|
}
|
2015-08-20 23:08:08 +08:00
|
|
|
|
|
|
|
// Check if we are in the resize handle
|
2019-03-30 02:57:10 +08:00
|
|
|
if (getPaletteEntryBounds(size).contains(pos)) {
|
|
|
|
return Hit(Hit::RESIZE_HANDLE, size);
|
2015-08-20 23:08:08 +08:00
|
|
|
}
|
2015-03-21 01:46:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we are inside a color.
|
2015-06-19 02:50:00 +08:00
|
|
|
View* view = View::getView(this);
|
2019-03-30 02:57:10 +08:00
|
|
|
ASSERT(view);
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
gfx::Rect vp = view->viewportBounds();
|
2015-06-19 02:50:00 +08:00
|
|
|
for (int i=0; ; ++i) {
|
2015-03-21 01:46:48 +08:00
|
|
|
gfx::Rect box = getPaletteEntryBounds(i);
|
2019-03-30 02:57:10 +08:00
|
|
|
if (i >= size && box.y2() > vp.h)
|
2015-06-19 02:50:00 +08:00
|
|
|
break;
|
|
|
|
|
2015-06-24 06:20:49 +08:00
|
|
|
box.w += childSpacing();
|
|
|
|
box.h += childSpacing();
|
2015-08-27 19:09:13 +08:00
|
|
|
if (box.contains(pos))
|
|
|
|
return Hit(Hit::COLOR, i);
|
2015-03-21 01:46:48 +08:00
|
|
|
}
|
|
|
|
|
2015-08-27 19:09:13 +08:00
|
|
|
gfx::Rect box = getPaletteEntryBounds(0);
|
|
|
|
|
|
|
|
int colsLimit = m_columns;
|
|
|
|
if (m_state == State::DRAGGING_OUTLINE)
|
|
|
|
--colsLimit;
|
2022-06-10 05:28:06 +08:00
|
|
|
int i = std::clamp((pos.x-vp.x)/box.w, 0, colsLimit)
|
2020-04-08 06:47:16 +08:00
|
|
|
+ std::max(0, pos.y/box.h)*m_columns;
|
2015-08-27 19:09:13 +08:00
|
|
|
return Hit(Hit::POSSIBLE_COLOR, i);
|
2015-03-21 01:46:48 +08:00
|
|
|
}
|
|
|
|
|
2015-03-23 23:25:32 +08:00
|
|
|
void PaletteView::dropColors(int beforeIndex)
|
|
|
|
{
|
2019-11-16 23:09:33 +08:00
|
|
|
m_adapter->dropColors(this,
|
|
|
|
m_selectedEntries,
|
|
|
|
m_currentEntry,
|
|
|
|
beforeIndex,
|
|
|
|
m_copy);
|
2015-03-23 23:25:32 +08:00
|
|
|
}
|
|
|
|
|
2015-05-09 23:20:58 +08:00
|
|
|
void PaletteView::getEntryBoundsAndClip(int i, const PalettePicks& entries,
|
2019-03-22 03:58:50 +08:00
|
|
|
const int outlineWidth,
|
|
|
|
gfx::Rect& box, gfx::Rect& clip) const
|
2015-05-09 23:20:58 +08:00
|
|
|
{
|
2019-03-22 03:58:50 +08:00
|
|
|
const int childSpacing = this->childSpacing();
|
|
|
|
|
2015-05-09 23:20:58 +08:00
|
|
|
box = clip = getPaletteEntryBounds(i);
|
|
|
|
box.enlarge(outlineWidth);
|
|
|
|
|
2019-03-22 03:58:50 +08:00
|
|
|
gfx::Border boxBorder(0, 0, 0, 0);
|
|
|
|
gfx::Border clipBorder(0, 0, 0, 0);
|
|
|
|
|
2015-05-09 23:20:58 +08:00
|
|
|
// Left
|
2019-03-22 03:58:50 +08:00
|
|
|
if (!pickedXY(entries, i, -1, 0))
|
|
|
|
clipBorder.left(outlineWidth);
|
|
|
|
else {
|
|
|
|
boxBorder.left((childSpacing+1)/2);
|
|
|
|
clipBorder.left((childSpacing+1)/2);
|
2015-05-09 23:20:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Top
|
2019-03-22 03:58:50 +08:00
|
|
|
if (!pickedXY(entries, i, 0, -1))
|
|
|
|
clipBorder.top(outlineWidth);
|
|
|
|
else {
|
|
|
|
boxBorder.top((childSpacing+1)/2);
|
|
|
|
clipBorder.top((childSpacing+1)/2);
|
2015-05-09 23:20:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Right
|
|
|
|
if (!pickedXY(entries, i, +1, 0))
|
2019-03-22 03:58:50 +08:00
|
|
|
clipBorder.right(outlineWidth);
|
2015-05-09 23:20:58 +08:00
|
|
|
else {
|
2019-03-22 03:58:50 +08:00
|
|
|
boxBorder.right(childSpacing/2);
|
|
|
|
clipBorder.right(childSpacing/2);
|
2015-05-09 23:20:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bottom
|
|
|
|
if (!pickedXY(entries, i, 0, +1))
|
2019-03-22 03:58:50 +08:00
|
|
|
clipBorder.bottom(outlineWidth);
|
2015-05-09 23:20:58 +08:00
|
|
|
else {
|
2019-03-22 03:58:50 +08:00
|
|
|
boxBorder.bottom(childSpacing/2);
|
|
|
|
clipBorder.bottom(childSpacing/2);
|
2015-05-09 23:20:58 +08:00
|
|
|
}
|
2019-03-22 03:58:50 +08:00
|
|
|
|
|
|
|
box.enlarge(boxBorder);
|
|
|
|
clip.enlarge(clipBorder);
|
2015-05-09 23:20:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool PaletteView::pickedXY(const doc::PalettePicks& entries, int i, int dx, int dy) const
|
|
|
|
{
|
2019-03-22 03:58:50 +08:00
|
|
|
const int x = (i % m_columns) + dx;
|
|
|
|
const int y = (i / m_columns) + dy;
|
|
|
|
const int lastcolor = entries.size()-1;
|
2015-05-09 23:20:58 +08:00
|
|
|
|
2015-06-18 23:50:33 +08:00
|
|
|
if (x < 0 || x >= m_columns || y < 0 || y > lastcolor/m_columns)
|
2015-05-09 23:20:58 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
i = x + y*m_columns;
|
|
|
|
if (i >= 0 && i < entries.size())
|
|
|
|
return entries[i];
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-05-10 02:57:46 +08:00
|
|
|
void PaletteView::updateCopyFlag(ui::Message* msg)
|
|
|
|
{
|
|
|
|
bool oldCopy = m_copy;
|
|
|
|
m_copy = (msg->ctrlPressed() || msg->altPressed());
|
2015-07-05 06:11:50 +08:00
|
|
|
if (oldCopy != m_copy) {
|
2015-05-10 02:57:46 +08:00
|
|
|
setCursor();
|
2015-07-05 06:11:50 +08:00
|
|
|
setStatusBar();
|
|
|
|
invalidate();
|
|
|
|
}
|
2015-05-10 02:57:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void PaletteView::setCursor()
|
|
|
|
{
|
|
|
|
if (m_state == State::DRAGGING_OUTLINE ||
|
2015-08-20 23:08:08 +08:00
|
|
|
(m_state == State::WAITING &&
|
|
|
|
m_hot.part == Hit::OUTLINE)) {
|
2015-05-10 02:57:46 +08:00
|
|
|
if (m_copy)
|
|
|
|
ui::set_mouse_cursor(kArrowPlusCursor);
|
|
|
|
else
|
|
|
|
ui::set_mouse_cursor(kMoveCursor);
|
|
|
|
}
|
2015-08-20 23:08:08 +08:00
|
|
|
else if (m_state == State::RESIZING_PALETTE ||
|
|
|
|
(m_state == State::WAITING &&
|
|
|
|
m_hot.part == Hit::RESIZE_HANDLE)) {
|
|
|
|
ui::set_mouse_cursor(kSizeWECursor);
|
|
|
|
}
|
2015-05-10 02:57:46 +08:00
|
|
|
else
|
|
|
|
ui::set_mouse_cursor(kArrowCursor);
|
|
|
|
}
|
|
|
|
|
2015-07-05 06:11:50 +08:00
|
|
|
void PaletteView::setStatusBar()
|
|
|
|
{
|
2018-07-05 23:39:49 +08:00
|
|
|
StatusBar* statusBar = StatusBar::instance();
|
|
|
|
|
|
|
|
if (m_hot.part == Hit::NONE) {
|
2018-11-13 04:52:05 +08:00
|
|
|
statusBar->showDefaultText();
|
2018-07-05 23:39:49 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-05 06:11:50 +08:00
|
|
|
switch (m_state) {
|
|
|
|
|
|
|
|
case State::WAITING:
|
|
|
|
case State::SELECTING_COLOR:
|
2018-07-05 23:39:49 +08:00
|
|
|
if ((m_hot.part == Hit::COLOR ||
|
|
|
|
m_hot.part == Hit::OUTLINE ||
|
|
|
|
m_hot.part == Hit::POSSIBLE_COLOR) &&
|
2019-03-30 02:57:10 +08:00
|
|
|
(m_hot.color < m_adapter->size())) {
|
2020-05-19 07:24:22 +08:00
|
|
|
const int index = std::max(0, m_hot.color);
|
2015-07-05 06:11:50 +08:00
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
m_adapter->showEntryInStatusBar(statusBar, index);
|
2015-07-05 06:11:50 +08:00
|
|
|
}
|
2015-07-06 06:41:11 +08:00
|
|
|
else {
|
2018-11-13 04:52:05 +08:00
|
|
|
statusBar->showDefaultText();
|
2015-07-06 06:41:11 +08:00
|
|
|
}
|
2015-07-05 06:11:50 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case State::DRAGGING_OUTLINE:
|
|
|
|
if (m_hot.part == Hit::COLOR) {
|
2018-07-05 22:29:12 +08:00
|
|
|
const int picks = m_selectedEntries.picks();
|
2020-04-08 23:03:32 +08:00
|
|
|
const int destIndex = std::max(0, m_hot.color);
|
2019-03-30 02:57:10 +08:00
|
|
|
const int palSize = m_adapter->size();
|
2018-07-05 22:29:12 +08:00
|
|
|
const int newPalSize =
|
2020-04-08 23:03:32 +08:00
|
|
|
(m_copy ? std::max(palSize + picks, destIndex + picks):
|
|
|
|
std::max(palSize, destIndex + picks));
|
2015-07-05 06:11:50 +08:00
|
|
|
|
2019-03-30 02:57:10 +08:00
|
|
|
m_adapter->showDragInfoInStatusBar(
|
|
|
|
statusBar, m_copy, destIndex, newPalSize);
|
2015-07-06 06:41:11 +08:00
|
|
|
}
|
|
|
|
else {
|
2018-11-13 04:52:05 +08:00
|
|
|
statusBar->showDefaultText();
|
2015-07-05 06:11:50 +08:00
|
|
|
}
|
|
|
|
break;
|
2015-08-20 23:08:08 +08:00
|
|
|
|
|
|
|
case State::RESIZING_PALETTE:
|
|
|
|
if (m_hot.part == Hit::COLOR ||
|
2015-08-27 19:09:13 +08:00
|
|
|
m_hot.part == Hit::POSSIBLE_COLOR ||
|
2015-08-20 23:08:08 +08:00
|
|
|
m_hot.part == Hit::RESIZE_HANDLE) {
|
2020-05-19 07:24:22 +08:00
|
|
|
const int newSize = std::max(1, m_hot.color);
|
2019-03-30 02:57:10 +08:00
|
|
|
|
|
|
|
m_adapter->showResizeInfoInStatusBar(statusBar, newSize);
|
2015-08-20 23:08:08 +08:00
|
|
|
}
|
|
|
|
else {
|
2018-11-13 04:52:05 +08:00
|
|
|
statusBar->showDefaultText();
|
2015-08-20 23:08:08 +08:00
|
|
|
}
|
|
|
|
break;
|
2015-07-05 06:11:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-18 23:50:33 +08:00
|
|
|
doc::Palette* PaletteView::currentPalette() const
|
|
|
|
{
|
|
|
|
return get_current_palette();
|
|
|
|
}
|
|
|
|
|
|
|
|
int PaletteView::findExactIndex(const app::Color& color) const
|
2015-05-11 21:53:50 +08:00
|
|
|
{
|
|
|
|
switch (color.getType()) {
|
|
|
|
|
2022-09-09 04:00:22 +08:00
|
|
|
case Color::MaskType: {
|
2022-10-20 23:31:22 +08:00
|
|
|
auto editor = Editor::activeEditor();
|
|
|
|
if (editor && editor->sprite()->pixelFormat() == IMAGE_INDEXED)
|
|
|
|
return editor->sprite()->transparentColor();
|
2022-09-09 04:00:22 +08:00
|
|
|
return currentPalette()->findMaskColor();
|
|
|
|
}
|
2015-05-11 21:53:50 +08:00
|
|
|
|
|
|
|
case Color::RgbType:
|
|
|
|
case Color::HsvType:
|
2017-05-30 01:20:42 +08:00
|
|
|
case Color::HslType:
|
2015-05-11 21:53:50 +08:00
|
|
|
case Color::GrayType:
|
2015-07-17 23:26:11 +08:00
|
|
|
return currentPalette()->findExactMatch(
|
|
|
|
color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha(), -1);
|
2015-05-11 21:53:50 +08:00
|
|
|
|
|
|
|
case Color::IndexType:
|
|
|
|
return color.getIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT(false);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-08-20 23:08:08 +08:00
|
|
|
void PaletteView::setNewPalette(doc::Palette* oldPalette,
|
|
|
|
doc::Palette* newPalette,
|
|
|
|
PaletteViewModification mod)
|
2015-06-19 03:20:42 +08:00
|
|
|
{
|
|
|
|
// No differences
|
|
|
|
if (!newPalette->countDiff(oldPalette, nullptr, nullptr))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (m_delegate) {
|
2015-08-20 23:08:08 +08:00
|
|
|
m_delegate->onPaletteViewModification(newPalette, mod);
|
2017-04-11 05:03:00 +08:00
|
|
|
if (m_currentEntry >= 0)
|
|
|
|
m_delegate->onPaletteViewIndexChange(m_currentEntry, ui::kButtonLeft);
|
2015-06-19 03:20:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
set_current_palette(newPalette, false);
|
Refactor several "getNoun()" getters to "noun()"
This is a work-in-progress to create a consistent API and finally
separate the whole Aseprite base/gfx/ui libs into a reusable C++ library.
Classes:
app::IFileItem, app::AppMenuItem, app::skin::SkinPart,
gfx::Rect, gfx::Border, she::FileDialog,
ui::IButtonIcon, ui::Graphics, ui::Overlay, ui::Widget,
ui::ScrollableViewDelegate, and UI events
2015-12-05 01:39:04 +08:00
|
|
|
manager()->invalidate();
|
2015-06-19 03:20:42 +08:00
|
|
|
}
|
|
|
|
|
2019-03-22 03:58:50 +08:00
|
|
|
int PaletteView::boxSizePx() const
|
|
|
|
{
|
|
|
|
return m_boxsize*guiscale()
|
|
|
|
+ (m_withSeparator ? 0: childSpacing());
|
|
|
|
}
|
|
|
|
|
|
|
|
void PaletteView::updateBorderAndChildSpacing()
|
|
|
|
{
|
2022-02-19 06:01:46 +08:00
|
|
|
auto theme = SkinTheme::get(this);
|
2019-03-22 03:58:50 +08:00
|
|
|
const int dim = theme->dimensions.paletteEntriesSeparator();
|
|
|
|
setBorder(gfx::Border(dim));
|
|
|
|
setChildSpacing(m_withSeparator ? dim: 0);
|
|
|
|
|
|
|
|
View* view = View::getView(this);
|
|
|
|
if (view)
|
|
|
|
view->updateView();
|
|
|
|
|
|
|
|
invalidate();
|
2015-07-05 06:11:50 +08:00
|
|
|
}
|
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
} // namespace app
|