mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-06 21:39:57 +00:00
Add support to drag-and-drop colors in ColorBar/PaletteView (fix #37)
This commit is contained in:
parent
d41b08caa8
commit
d14e4e8896
@ -20,11 +20,11 @@ namespace cmd {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
SetPalette::SetPalette(Sprite* sprite, frame_t frame, Palette* newPalette)
|
||||
SetPalette::SetPalette(Sprite* sprite, frame_t frame, const Palette* newPalette)
|
||||
: WithSprite(sprite)
|
||||
, m_frame(frame)
|
||||
{
|
||||
Palette* curPalette = sprite->palette(frame);
|
||||
const Palette* curPalette = sprite->palette(frame);
|
||||
|
||||
// Check differences between current sprite palette and the new one
|
||||
m_from = m_to = -1;
|
||||
|
@ -28,7 +28,7 @@ namespace cmd {
|
||||
class SetPalette : public Cmd
|
||||
, public WithSprite {
|
||||
public:
|
||||
SetPalette(Sprite* sprite, frame_t frame, Palette* newPalette);
|
||||
SetPalette(Sprite* sprite, frame_t frame, const Palette* newPalette);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
|
@ -11,14 +11,19 @@
|
||||
|
||||
#include "app/ui/color_bar.h"
|
||||
|
||||
#include "app/cmd/set_palette.h"
|
||||
#include "app/color.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/params.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/transaction.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "base/bind.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
@ -68,7 +73,7 @@ ColorBar* ColorBar::m_instance = NULL;
|
||||
ColorBar::ColorBar(int align)
|
||||
: Box(align)
|
||||
, m_paletteButton("Edit Palette")
|
||||
, m_paletteView(false)
|
||||
, m_paletteView(true, this)
|
||||
, m_fgColor(app::Color::fromRgb(255, 255, 255), IMAGE_RGB)
|
||||
, m_bgColor(app::Color::fromRgb(0, 0, 0), IMAGE_RGB)
|
||||
, m_lock(false)
|
||||
@ -97,7 +102,6 @@ ColorBar::ColorBar(int align)
|
||||
addChild(&m_fgColor);
|
||||
addChild(&m_bgColor);
|
||||
|
||||
m_paletteView.IndexChange.connect(&ColorBar::onPaletteIndexChange, this);
|
||||
m_fgColor.Change.connect(&ColorBar::onFgColorButtonChange, this);
|
||||
m_bgColor.Change.connect(&ColorBar::onBgColorButtonChange, this);
|
||||
|
||||
@ -190,13 +194,13 @@ void ColorBar::onPaletteButtonDropDownClick()
|
||||
}
|
||||
}
|
||||
|
||||
void ColorBar::onPaletteIndexChange(PaletteIndexChangeEvent& ev)
|
||||
void ColorBar::onPaletteViewIndexChange(int index, ui::MouseButtons buttons)
|
||||
{
|
||||
m_lock = true;
|
||||
|
||||
app::Color color = app::Color::fromIndex(ev.index());
|
||||
app::Color color = app::Color::fromIndex(index);
|
||||
|
||||
if ((ev.buttons() & kButtonRight) == kButtonRight)
|
||||
if ((buttons & kButtonRight) == kButtonRight)
|
||||
setBgColor(color);
|
||||
else
|
||||
setFgColor(color);
|
||||
@ -204,6 +208,23 @@ void ColorBar::onPaletteIndexChange(PaletteIndexChangeEvent& ev)
|
||||
m_lock = false;
|
||||
}
|
||||
|
||||
void ColorBar::onPaletteViewRemapColors(const Remap& remap, const Palette* newPalette)
|
||||
{
|
||||
try {
|
||||
ContextWriter writer(UIContext::instance());
|
||||
Sprite* sprite = writer.sprite();
|
||||
frame_t frame = writer.frame();
|
||||
if (sprite) {
|
||||
Transaction transaction(writer.context(), "Move Colors", ModifyDocument);
|
||||
transaction.execute(new cmd::SetPalette(sprite, frame, newPalette));
|
||||
transaction.commit();
|
||||
}
|
||||
}
|
||||
catch (base::Exception& e) {
|
||||
Console::showException(e);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorBar::onFgColorButtonChange(const app::Color& color)
|
||||
{
|
||||
if (!m_lock)
|
||||
|
@ -26,7 +26,8 @@ namespace app {
|
||||
class PalettesLoader;
|
||||
class PaletteIndexChangeEvent;
|
||||
|
||||
class ColorBar : public ui::Box {
|
||||
class ColorBar : public ui::Box
|
||||
, public PaletteViewDelegate {
|
||||
static ColorBar* m_instance;
|
||||
public:
|
||||
static ColorBar* instance() { return m_instance; }
|
||||
@ -59,6 +60,10 @@ namespace app {
|
||||
void onBgColorButtonChange(const app::Color& color);
|
||||
void onColorButtonChange(const app::Color& color);
|
||||
|
||||
// PaletteViewDelegate impl
|
||||
void onPaletteViewIndexChange(int index, ui::MouseButtons buttons) override;
|
||||
void onPaletteViewRemapColors(const doc::Remap& remap, const doc::Palette* newPalette) override;
|
||||
|
||||
private:
|
||||
class ScrollableView : public ui::View {
|
||||
public:
|
||||
|
@ -55,7 +55,7 @@ ColorSelector::ColorSelector()
|
||||
, m_vbox(JI_VERTICAL)
|
||||
, m_topBox(JI_HORIZONTAL)
|
||||
, m_color(app::Color::fromMask())
|
||||
, m_colorPalette(false)
|
||||
, m_colorPalette(false, this)
|
||||
, m_indexButton("Index", 1, kButtonWidget)
|
||||
, m_rgbButton("RGB", 1, kButtonWidget)
|
||||
, m_hsvButton("HSB", 1, kButtonWidget)
|
||||
@ -106,7 +106,6 @@ ColorSelector::ColorSelector()
|
||||
m_maskButton.Click.connect(&ColorSelector::onColorTypeButtonClick, this);
|
||||
m_warningIcon->Click.connect(&ColorSelector::onFixWarningClick, this);
|
||||
|
||||
m_colorPalette.IndexChange.connect(&ColorSelector::onColorPaletteIndexChange, this);
|
||||
m_rgbSliders.ColorChange.connect(&ColorSelector::onColorSlidersChange, this);
|
||||
m_hsvSliders.ColorChange.connect(&ColorSelector::onColorSlidersChange, this);
|
||||
m_graySlider.ColorChange.connect(&ColorSelector::onColorSlidersChange, this);
|
||||
@ -160,9 +159,9 @@ app::Color ColorSelector::getColor() const
|
||||
return m_color;
|
||||
}
|
||||
|
||||
void ColorSelector::onColorPaletteIndexChange(PaletteIndexChangeEvent& ev)
|
||||
void ColorSelector::onPaletteViewIndexChange(int index, ui::MouseButtons buttons)
|
||||
{
|
||||
setColorWithSignal(app::Color::fromIndex(ev.index()));
|
||||
setColorWithSignal(app::Color::fromIndex(index));
|
||||
}
|
||||
|
||||
void ColorSelector::onColorSlidersChange(ColorSlidersChangeEvent& ev)
|
||||
|
@ -25,7 +25,8 @@
|
||||
namespace app {
|
||||
class PaletteIndexChangeEvent;
|
||||
|
||||
class ColorSelector : public PopupWindowPin {
|
||||
class ColorSelector : public PopupWindowPin
|
||||
, public PaletteViewDelegate {
|
||||
public:
|
||||
enum SetColorOptions {
|
||||
ChangeType,
|
||||
@ -42,13 +43,15 @@ namespace app {
|
||||
Signal1<void, const app::Color&> ColorChange;
|
||||
|
||||
protected:
|
||||
void onColorPaletteIndexChange(PaletteIndexChangeEvent& ev);;
|
||||
void onColorSlidersChange(ColorSlidersChangeEvent& ev);
|
||||
void onColorHexEntryChange(const app::Color& color);
|
||||
void onColorTypeButtonClick(ui::Event& ev);
|
||||
void onFixWarningClick(ui::Event& ev);
|
||||
void onPaletteChange();
|
||||
|
||||
// PaletteViewDelegate impl
|
||||
void onPaletteViewIndexChange(int index, ui::MouseButtons buttons) override;
|
||||
|
||||
private:
|
||||
void selectColorType(app::Color::Type type);
|
||||
void setColorWithSignal(const app::Color& color);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "doc/blend.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/remap.h"
|
||||
#include "gfx/color.h"
|
||||
#include "gfx/point.h"
|
||||
#include "ui/graphics.h"
|
||||
@ -33,6 +34,7 @@
|
||||
#include "ui/view.h"
|
||||
#include "ui/widget.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
@ -49,10 +51,11 @@ WidgetType palette_view_type()
|
||||
return type;
|
||||
}
|
||||
|
||||
PaletteView::PaletteView(bool editable)
|
||||
PaletteView::PaletteView(bool editable, PaletteViewDelegate* delegate)
|
||||
: Widget(palette_view_type())
|
||||
, m_state(State::WAITING)
|
||||
, m_editable(editable)
|
||||
, m_delegate(delegate)
|
||||
, m_columns(16)
|
||||
, m_boxsize(7*guiscale())
|
||||
, m_currentEntry(-1)
|
||||
@ -174,9 +177,11 @@ bool PaletteView::onProcessMessage(Message* msg)
|
||||
|
||||
case kMouseDownMessage:
|
||||
switch (m_hot.part) {
|
||||
|
||||
case Hit::COLOR:
|
||||
m_state = State::SELECTING_COLOR;
|
||||
break;
|
||||
|
||||
case Hit::OUTLINE:
|
||||
m_state = State::DRAGGING_OUTLINE;
|
||||
break;
|
||||
@ -196,7 +201,7 @@ bool PaletteView::onProcessMessage(Message* msg)
|
||||
StatusBar::instance()->showColor(0, "",
|
||||
app::Color::fromIndex(idx), 255);
|
||||
|
||||
if (hasCapture() && idx != m_currentEntry) {
|
||||
if (hasCapture() && (idx != m_currentEntry || msg->type() == kMouseDownMessage)) {
|
||||
if (!msg->ctrlPressed())
|
||||
clearSelection();
|
||||
|
||||
@ -206,8 +211,8 @@ bool PaletteView::onProcessMessage(Message* msg)
|
||||
selectColor(idx);
|
||||
|
||||
// Emit signal
|
||||
PaletteIndexChangeEvent ev(this, idx, mouseMsg->buttons());
|
||||
IndexChange(ev);
|
||||
if (m_delegate)
|
||||
m_delegate->onPaletteViewIndexChange(idx, mouseMsg->buttons());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -222,6 +227,11 @@ bool PaletteView::onProcessMessage(Message* msg)
|
||||
if (hasCapture()) {
|
||||
releaseMouse();
|
||||
|
||||
if (m_state == State::DRAGGING_OUTLINE &&
|
||||
m_hot.part == Hit::COLOR) {
|
||||
dropColors(m_hot.color + (m_hot.after ? 1: 0));
|
||||
}
|
||||
|
||||
m_state = State::WAITING;
|
||||
invalidate();
|
||||
}
|
||||
@ -450,7 +460,7 @@ PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos)
|
||||
int outlineWidth = theme->dimensions.paletteOutlineWidth();
|
||||
Palette* palette = get_current_palette();
|
||||
|
||||
if (m_state == State::WAITING) {
|
||||
if (m_state == State::WAITING && m_editable) {
|
||||
// First check if the mouse is inside the selection outline.
|
||||
for (int i=0; i<palette->size(); ++i) {
|
||||
if (!m_selectedEntries[i])
|
||||
@ -488,4 +498,27 @@ PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos)
|
||||
return Hit(Hit::NONE);
|
||||
}
|
||||
|
||||
void PaletteView::dropColors(int beforeIndex)
|
||||
{
|
||||
Palette* palette = get_current_palette();
|
||||
Remap remap = Remap::moveSelectedEntriesTo(m_selectedEntries, beforeIndex);
|
||||
|
||||
auto oldSelectedCopies = m_selectedEntries;
|
||||
Palette oldPalCopy(*palette);
|
||||
for (int i=0; i<palette->size(); ++i) {
|
||||
palette->setEntry(remap[i], oldPalCopy.getEntry(i));
|
||||
m_selectedEntries[remap[i]] = oldSelectedCopies[i];
|
||||
}
|
||||
|
||||
m_currentEntry = remap[m_currentEntry];
|
||||
|
||||
if (m_delegate) {
|
||||
m_delegate->onPaletteViewRemapColors(remap, palette);
|
||||
m_delegate->onPaletteViewIndexChange(m_currentEntry, ui::kButtonLeft);
|
||||
}
|
||||
|
||||
set_current_palette(palette, false);
|
||||
getManager()->invalidate();
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -10,35 +10,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "base/connection.h"
|
||||
#include "base/signal.h"
|
||||
#include "ui/event.h"
|
||||
#include "ui/mouse_buttons.h"
|
||||
#include "ui/widget.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
class Palette;
|
||||
class Remap;
|
||||
}
|
||||
|
||||
namespace app {
|
||||
|
||||
class PaletteIndexChangeEvent : public ui::Event {
|
||||
class PaletteViewDelegate {
|
||||
public:
|
||||
PaletteIndexChangeEvent(ui::Widget* source, int index, ui::MouseButtons buttons)
|
||||
: Event(source)
|
||||
, m_index(index)
|
||||
, m_buttons(buttons) { }
|
||||
|
||||
int index() const { return m_index; }
|
||||
ui::MouseButtons buttons() const { return m_buttons; }
|
||||
|
||||
private:
|
||||
int m_index;
|
||||
ui::MouseButtons m_buttons;
|
||||
virtual ~PaletteViewDelegate() { }
|
||||
virtual void onPaletteViewIndexChange(int index, ui::MouseButtons buttons) { }
|
||||
virtual void onPaletteViewRemapColors(const doc::Remap& remap, const doc::Palette* newPalette) { }
|
||||
};
|
||||
|
||||
class PaletteView : public ui::Widget {
|
||||
public:
|
||||
typedef std::vector<bool> SelectedEntries;
|
||||
|
||||
PaletteView(bool editable);
|
||||
PaletteView(bool editable, PaletteViewDelegate* delegate);
|
||||
|
||||
int getColumns() const { return m_columns; }
|
||||
void setColumns(int columns);
|
||||
@ -53,9 +49,6 @@ namespace app {
|
||||
|
||||
app::Color getColorByPosition(const gfx::Point& pos);
|
||||
|
||||
// Signals
|
||||
Signal1<void, PaletteIndexChangeEvent&> IndexChange;
|
||||
|
||||
protected:
|
||||
bool onProcessMessage(ui::Message* msg) override;
|
||||
void onPaint(ui::PaintEvent& ev) override;
|
||||
@ -99,9 +92,11 @@ namespace app {
|
||||
void onAppPaletteChange();
|
||||
gfx::Rect getPaletteEntryBounds(int index);
|
||||
Hit hitTest(const gfx::Point& pos);
|
||||
void dropColors(int beforeIndex);
|
||||
|
||||
State m_state;
|
||||
bool m_editable;
|
||||
PaletteViewDelegate* m_delegate;
|
||||
int m_columns;
|
||||
int m_boxsize;
|
||||
int m_currentEntry;
|
||||
|
@ -40,6 +40,7 @@ add_library(doc-lib
|
||||
palette.cpp
|
||||
palette_io.cpp
|
||||
primitives.cpp
|
||||
remap.cpp
|
||||
rgbmap.cpp
|
||||
sprite.cpp
|
||||
sprites.cpp
|
||||
|
47
src/doc/remap.cpp
Normal file
47
src/doc/remap.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "doc/remap.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
// static
|
||||
Remap Remap::moveSelectedEntriesTo(const std::vector<bool>& selectedEntries, int beforeIndex)
|
||||
{
|
||||
Remap map(selectedEntries.size());
|
||||
|
||||
int selectedTotal = 0;
|
||||
int selectedBeforeIndex = 0;
|
||||
|
||||
for (int i=0; i<map.size(); ++i) {
|
||||
if (selectedEntries[i]) {
|
||||
++selectedTotal;
|
||||
if (i < beforeIndex)
|
||||
++selectedBeforeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0, j=0, k=0; i<map.size(); ++i) {
|
||||
if (k == beforeIndex - selectedBeforeIndex)
|
||||
k += selectedTotal;
|
||||
|
||||
if (selectedEntries[i]) {
|
||||
map.map(i, beforeIndex - selectedBeforeIndex + j);
|
||||
++j;
|
||||
}
|
||||
else {
|
||||
map.map(i, k++);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
} // namespace doc
|
46
src/doc/remap.h
Normal file
46
src/doc/remap.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_REMAP_H_INCLUDED
|
||||
#define DOC_REMAP_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace doc {
|
||||
|
||||
class Remap {
|
||||
public:
|
||||
Remap(int entries) : m_map(entries, 0) { }
|
||||
|
||||
// Creates a map to move a set of selected entries before the
|
||||
// given index "beforeIndex".
|
||||
static Remap moveSelectedEntriesTo(
|
||||
const std::vector<bool>& selectedEntries, int beforeIndex);
|
||||
|
||||
int size() const {
|
||||
return (int)m_map.size();
|
||||
}
|
||||
|
||||
void map(int fromIndex, int toIndex) {
|
||||
ASSERT(fromIndex >= 0 && fromIndex < size());
|
||||
ASSERT(toIndex >= 0 && toIndex < size());
|
||||
|
||||
m_map[fromIndex] = toIndex;
|
||||
}
|
||||
|
||||
int operator[](int index) const {
|
||||
ASSERT(index >= 0 && index < size());
|
||||
return m_map[index];
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<int> m_map;
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
76
src/doc/remap_tests.cpp
Normal file
76
src/doc/remap_tests.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "doc/remap.h"
|
||||
|
||||
using namespace doc;
|
||||
|
||||
TEST(Remap, Basics)
|
||||
{
|
||||
std::vector<bool> entries(20);
|
||||
std::fill(entries.begin(), entries.end(), false);
|
||||
entries[6] =
|
||||
entries[7] =
|
||||
entries[14] = true;
|
||||
|
||||
Remap map = Remap::moveSelectedEntriesTo(entries, 1);
|
||||
|
||||
EXPECT_EQ(0, map[0]);
|
||||
EXPECT_EQ(4, map[1]);
|
||||
EXPECT_EQ(5, map[2]);
|
||||
EXPECT_EQ(6, map[3]);
|
||||
EXPECT_EQ(7, map[4]);
|
||||
EXPECT_EQ(8, map[5]);
|
||||
EXPECT_EQ(1, map[6]);
|
||||
EXPECT_EQ(2, map[7]);
|
||||
EXPECT_EQ(9, map[8]);
|
||||
EXPECT_EQ(10, map[9]);
|
||||
EXPECT_EQ(11, map[10]);
|
||||
EXPECT_EQ(12, map[11]);
|
||||
EXPECT_EQ(13, map[12]);
|
||||
EXPECT_EQ(14, map[13]);
|
||||
EXPECT_EQ(3, map[14]);
|
||||
EXPECT_EQ(15, map[15]);
|
||||
EXPECT_EQ(16, map[16]);
|
||||
EXPECT_EQ(17, map[17]);
|
||||
EXPECT_EQ(18, map[18]);
|
||||
EXPECT_EQ(19, map[19]);
|
||||
|
||||
map = Remap::moveSelectedEntriesTo(entries, 18);
|
||||
|
||||
EXPECT_EQ(0, map[0]);
|
||||
EXPECT_EQ(1, map[1]);
|
||||
EXPECT_EQ(2, map[2]);
|
||||
EXPECT_EQ(3, map[3]);
|
||||
EXPECT_EQ(4, map[4]);
|
||||
EXPECT_EQ(5, map[5]);
|
||||
EXPECT_EQ(15, map[6]);
|
||||
EXPECT_EQ(16, map[7]);
|
||||
EXPECT_EQ(6, map[8]);
|
||||
EXPECT_EQ(7, map[9]);
|
||||
EXPECT_EQ(8, map[10]);
|
||||
EXPECT_EQ(9, map[11]);
|
||||
EXPECT_EQ(10, map[12]);
|
||||
EXPECT_EQ(11, map[13]);
|
||||
EXPECT_EQ(17, map[14]);
|
||||
EXPECT_EQ(12, map[15]);
|
||||
EXPECT_EQ(13, map[16]);
|
||||
EXPECT_EQ(14, map[17]);
|
||||
EXPECT_EQ(18, map[18]);
|
||||
EXPECT_EQ(19, map[19]);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user