Add ability to copy & paste colors with Ctrl+C and Ctrl+V

This commit includes the new doc::PalettePicks class. It's a helper
class to identify which entries are selected in the ColorBar.

We've removed the Copy/Paste buttons from the Palette Editor window.
This commit is contained in:
David Capello 2015-05-09 12:20:58 -03:00
parent feb592db73
commit 62a3594598
10 changed files with 392 additions and 222 deletions

View File

@ -57,26 +57,11 @@ void ColorQuantizationCommand::onExecute(Context* context)
Sprite* sprite = writer.sprite();
frame_t frame = writer.frame();
if (sprite) {
PaletteView::SelectedEntries entries;
PalettePicks entries;
ColorBar::instance()->getPaletteView()->getSelectedEntries(entries);
// TODO convert SelectedEntries into a class, and put the
// following lines in that class it is used several times (here
// and in ColorBar)
int n = 0;
for (bool state : entries) {
if (state)
++n;
}
if (n < 2) {
n = 0;
for (auto& state : entries) {
state = true;
++n;
}
}
entries.pickAllIfNeeded();
int n = entries.picks();
Palette palette(frame, n);
render::create_palette_from_rgb(sprite, frame, &palette);

View File

@ -72,14 +72,12 @@ protected:
void onColorSlidersChange(ColorSlidersChangeEvent& ev);
void onColorHexEntryChange(const app::Color& color);
void onColorTypeButtonClick(Event& ev);
void onMoreOptionsClick(Event& ev);
void onCopyColorsClick(Event& ev);
void onPasteColorsClick(Event& ev);
private:
void selectColorType(app::Color::Type type);
void setPaletteEntry(const app::Color& color);
void setPaletteEntryChannel(const app::Color& color, ColorSliders::Channel channel);
void setNewPalette(Palette* palette, const char* operationName);
void updateCurrentSpritePalette(const char* operationName);
void updateColorBar();
void onPalChange();
@ -91,11 +89,8 @@ private:
RadioButton m_hsvButton;
HexColorEntry m_hexColorEntry;
Label m_entryLabel;
Button m_moreOptions;
RgbSliders m_rgbSliders;
HsvSliders m_hsvSliders;
Button m_copyButton;
Button m_pasteButton;
// This variable is used to avoid updating the m_hexColorEntry text
// when the color change is generated from a
@ -116,10 +111,6 @@ private:
bool m_selfPalChange;
ScopedConnection m_palChangeConn;
// Internal-clipboard to copy & paste colors between palettes. It's
// used in onCopy/PasteColorsClick.
std::vector<uint32_t> m_clipboardColors;
};
static PaletteEntryEditor* g_window = NULL;
@ -242,9 +233,6 @@ PaletteEntryEditor::PaletteEntryEditor()
, m_rgbButton("RGB", 1, kButtonWidget)
, m_hsvButton("HSB", 1, kButtonWidget)
, m_entryLabel("")
, m_moreOptions("+")
, m_copyButton("Copy")
, m_pasteButton("Paste")
, m_disableHexUpdate(false)
, m_redrawTimer(250, this)
, m_redrawAll(false)
@ -257,26 +245,12 @@ PaletteEntryEditor::PaletteEntryEditor()
setup_mini_look(&m_rgbButton);
setup_mini_look(&m_hsvButton);
setup_mini_look(&m_moreOptions);
setup_mini_look(&m_copyButton);
setup_mini_look(&m_pasteButton);
// Top box
m_topBox.addChild(&m_rgbButton);
m_topBox.addChild(&m_hsvButton);
m_topBox.addChild(&m_hexColorEntry);
m_topBox.addChild(&m_entryLabel);
m_topBox.addChild(new BoxFiller);
m_topBox.addChild(&m_moreOptions);
// Bottom box
{
Box* box = new Box(JI_HORIZONTAL);
box->child_spacing = 0;
box->addChild(&m_copyButton);
box->addChild(&m_pasteButton);
m_bottomBox.addChild(box);
}
// Main vertical box
m_vbox.addChild(&m_topBox);
@ -285,14 +259,8 @@ PaletteEntryEditor::PaletteEntryEditor()
m_vbox.addChild(&m_bottomBox);
addChild(&m_vbox);
// Hide (or show) the "More Options" depending the saved value in .cfg file
m_bottomBox.setVisible(get_config_bool("PaletteEditor", "ShowMoreOptions", false));
m_rgbButton.Click.connect(&PaletteEntryEditor::onColorTypeButtonClick, this);
m_hsvButton.Click.connect(&PaletteEntryEditor::onColorTypeButtonClick, this);
m_moreOptions.Click.connect(&PaletteEntryEditor::onMoreOptionsClick, this);
m_copyButton.Click.connect(&PaletteEntryEditor::onCopyColorsClick, this);
m_pasteButton.Click.connect(&PaletteEntryEditor::onPasteColorsClick, this);
m_rgbSliders.ColorChange.connect(&PaletteEntryEditor::onColorSlidersChange, this);
m_hsvSliders.ColorChange.connect(&PaletteEntryEditor::onColorSlidersChange, this);
@ -325,7 +293,7 @@ void PaletteEntryEditor::setColor(const app::Color& color)
m_hexColorEntry.setColor(color);
PaletteView* palette_editor = ColorBar::instance()->getPaletteView();
PaletteView::SelectedEntries entries;
PalettePicks entries;
palette_editor->getSelectedEntries(entries);
int i, j, i2;
@ -447,99 +415,10 @@ void PaletteEntryEditor::onColorTypeButtonClick(Event& ev)
else if (source == &m_hsvButton) selectColorType(app::Color::HsvType);
}
void PaletteEntryEditor::onMoreOptionsClick(Event& ev)
{
Size reqSize;
if (m_bottomBox.isVisible()) {
set_config_bool("PaletteEditor", "ShowMoreOptions", false);
m_bottomBox.setVisible(false);
// Get the required size of the "More options" panel
reqSize = m_bottomBox.getPreferredSize();
reqSize.h += 4;
// Remove the space occupied by the "More options" panel
moveWindow(gfx::Rect(getOrigin(), getSize() - gfx::Size(0, reqSize.h)));
}
else {
set_config_bool("PaletteEditor", "ShowMoreOptions", true);
m_bottomBox.setVisible(true);
// Get the required size of the whole window
reqSize = getPreferredSize();
// Add space for the "more_options" panel
if (getBounds().h < reqSize.h) {
gfx::Rect rect(getOrigin(), gfx::Size(getBounds().w, reqSize.h));
// Show the expanded area inside the screen
if (rect.y2() > ui::display_h())
rect.offset(0, ui::display_h() - rect.y2());
moveWindow(rect);
}
else
setBounds(getBounds()); // TODO layout() method is missing
}
// Redraw the window
invalidate();
}
void PaletteEntryEditor::onCopyColorsClick(Event& ev)
{
// Get the selected entries in the palette view.
PaletteView* palette_editor = ColorBar::instance()->getPaletteView();
PaletteView::SelectedEntries selectedEntries;
palette_editor->getSelectedEntries(selectedEntries);
// Copy all selected entries into "m_clipboardColors" vector. These
// entries then are copied back to the palette in the "paste"
// operation (onPasteColorsClick).
Palette* palette = get_current_palette();
m_clipboardColors.clear();
for (int i=0; i<(int)selectedEntries.size(); ++i)
if (selectedEntries[i])
m_clipboardColors.push_back(palette->getEntry(i));
}
void PaletteEntryEditor::onPasteColorsClick(Event& ev)
{
// Get the selected entries in the palette view.
PaletteView* palette_editor = ColorBar::instance()->getPaletteView();
PaletteView::SelectedEntries selectedEntries;
palette_editor->getSelectedEntries(selectedEntries);
// Count how many colors are selected so we can continue pasting
// colors even if the current number of selected colors is less than
// the number of colors into the clipboard.
int selectedEntriesCount = 0;
for (int i=0; i<(int)selectedEntries.size(); ++i)
if (selectedEntries[i])
selectedEntriesCount++;
Palette* palette = get_current_palette();
for (int i=0, j=0; i<(int)selectedEntries.size() && j<(int)m_clipboardColors.size(); ++i) {
// The color is pasted if the entry is selected or if the
// clipboard contains more entries than the current number of
// selected palette entries.
if (selectedEntries[i] || j >= selectedEntriesCount)
palette->setEntry(i, m_clipboardColors[j++]);
}
updateCurrentSpritePalette("Paste Colors");
updateColorBar();
// Generate onPalChange() event so we update all sliders to the new
// values.
onPalChange();
}
void PaletteEntryEditor::setPaletteEntry(const app::Color& color)
{
PaletteView* palView = ColorBar::instance()->getPaletteView();
PaletteView::SelectedEntries entries;
PalettePicks entries;
palView->getSelectedEntries(entries);
color_t new_pal_color = doc::rgba(color.getRed(),
@ -556,7 +435,7 @@ void PaletteEntryEditor::setPaletteEntry(const app::Color& color)
void PaletteEntryEditor::setPaletteEntryChannel(const app::Color& color, ColorSliders::Channel channel)
{
PaletteView* palView = ColorBar::instance()->getPaletteView();
PaletteView::SelectedEntries entries;
PalettePicks entries;
palView->getSelectedEntries(entries);
int begSel, endSel;

View File

@ -26,6 +26,7 @@
#include "app/pref/preferences.h"
#include "app/transaction.h"
#include "app/ui/color_spectrum.h"
#include "app/ui/editor/editor.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
@ -35,6 +36,7 @@
#include "doc/palette.h"
#include "doc/remap.h"
#include "doc/sort_palette.h"
#include "doc/sprite.h"
#include "she/surface.h"
#include "ui/graphics.h"
#include "ui/menu.h"
@ -391,6 +393,47 @@ void ColorBar::onPaletteViewChangeSize(int boxsize)
App::instance()->preferences().colorBar.boxSize(boxsize);
}
void ColorBar::onPaletteViewPasteColors(
Editor* editor, const doc::PalettePicks& from, const doc::PalettePicks& _to)
{
if (!from.picks() || !_to.picks()) // Nothing to do
return;
doc::PalettePicks to = _to;
int to_first = to.firstPick();
int to_last = to.lastPick();
// Add extra picks in to range if it's needed to paste more colors.
int from_picks = from.picks();
int to_picks = to.picks();
if (to_picks < from_picks) {
for (int j=to_last+1; j<to.size() && to_picks<from_picks; ++j) {
to[j] = true;
++to_picks;
}
}
Palette* copyFromPalette = editor->sprite()->palette(editor->frame());
Palette newPalette(*get_current_palette());
int i = 0;
int j = to_first;
for (auto state : from) {
if (state) {
if (j < newPalette.size()) {
newPalette.setEntry(j, copyFromPalette->getEntry(i));
for (++j; j<to.size(); ++j)
if (to[j])
break;
}
}
++i;
}
setPalette(&newPalette, "Paste Colors");
}
void ColorBar::onFgColorButtonChange(const app::Color& color)
{
if (!m_lock)
@ -429,24 +472,11 @@ void ColorBar::onPickSpectrum(const app::Color& color, ui::MouseButtons buttons)
void ColorBar::onReverseColors()
{
PaletteView::SelectedEntries entries;
doc::PalettePicks entries;
m_paletteView.getSelectedEntries(entries);
// Count the number of selected entries.
int n = 0;
for (bool state : entries) {
if (state)
++n;
}
// If there is just one selected color, we select sort them all.
if (n < 2) {
n = 0;
for (auto& state : entries) {
state = true;
++n;
}
}
entries.pickAllIfNeeded();
int n = entries.picks();
std::vector<int> mapToOriginal(n); // Maps index from selectedPalette -> palette
int i = 0, j = 0;
@ -473,24 +503,11 @@ void ColorBar::onReverseColors()
void ColorBar::onSortBy(SortPaletteBy channel)
{
PaletteView::SelectedEntries entries;
PalettePicks entries;
m_paletteView.getSelectedEntries(entries);
// Count the number of selected entries.
int n = 0;
for (bool state : entries) {
if (state)
++n;
}
// If there is just one selected color, we select sort them all.
if (n < 2) {
n = 0;
for (auto& state : entries) {
state = true;
++n;
}
}
entries.pickAllIfNeeded();
int n = entries.picks();
// Create a "subpalette" with selected entries only.
Palette palette(*get_current_palette());

View File

@ -75,6 +75,7 @@ namespace app {
void onPaletteViewIndexChange(int index, ui::MouseButtons buttons) override;
void onPaletteViewRemapColors(const doc::Remap& remap, const doc::Palette* newPalette) override;
void onPaletteViewChangeSize(int boxsize) override;
void onPaletteViewPasteColors(Editor* editor, const doc::PalettePicks& from, const doc::PalettePicks& to) override;
private:
void destroyRemap();

View File

@ -0,0 +1,64 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
#ifndef APP_UI_MARCHING_ANTS_H_INCLUDED
#define APP_UI_MARCHING_ANTS_H_INCLUDED
#pragma once
#include "base/connection.h"
#include "ui/timer.h"
#include <cmath>
namespace app {
class MarchingAnts {
public:
MarchingAnts()
: m_timer(100)
, m_offset(0)
{
m_scopedConn = m_timer.Tick.connect(&MarchingAnts::onTick, this);
}
~MarchingAnts() {
m_timer.stop();
}
protected:
virtual void onDrawMarchingAnts() = 0;
int getMarchingAntsOffset() const {
return m_offset;
}
bool isMarchingAntsRunning() const {
return m_timer.isRunning();
}
void startMarchingAnts() {
m_timer.start();
}
void stopMarchingAnts() {
m_timer.stop();
}
private:
void onTick() {
m_offset = ((m_offset+1) % 8);
onDrawMarchingAnts();
}
ui::Timer m_timer;
int m_offset;
ScopedConnection m_scopedConn;
};
} // namespace app
#endif

View File

@ -12,8 +12,12 @@
#include "app/app.h"
#include "app/color.h"
#include "app/color_utils.h"
#include "app/commands/commands.h"
#include "app/modules/editors.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/ui/editor/editor.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/palette_view.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/skin/style.h"
@ -60,9 +64,11 @@ PaletteView::PaletteView(bool editable, PaletteViewDelegate* delegate, int boxsi
, m_boxsize(boxsize)
, m_currentEntry(-1)
, m_rangeAnchor(-1)
, m_selectedEntries(Palette::MaxColors, false)
, m_selectedEntries(Palette::MaxColors)
, m_clipboardEntries(Palette::MaxColors)
, m_isUpdatingColumns(false)
, m_hot(Hit::NONE)
, m_clipboardEditor(nullptr)
{
setFocusStop(true);
setDoubleBuffered(true);
@ -74,6 +80,11 @@ PaletteView::PaletteView(bool editable, PaletteViewDelegate* delegate, int boxsi
m_conn = App::instance()->PaletteChange.connect(&PaletteView::onAppPaletteChange, this);
}
PaletteView::~PaletteView()
{
setClipboardEditor(nullptr);
}
void PaletteView::setColumns(int columns)
{
int old_columns = m_columns;
@ -155,7 +166,7 @@ bool PaletteView::getSelectedRange(int& index1, int& index2) const
return false;
}
void PaletteView::getSelectedEntries(SelectedEntries& entries) const
void PaletteView::getSelectedEntries(PalettePicks& entries) const
{
entries = m_selectedEntries;
}
@ -188,10 +199,63 @@ void PaletteView::setBoxSize(int boxsize)
view->layout();
}
void PaletteView::copyToClipboard()
{
if (current_editor) {
setClipboardEditor(current_editor);
m_clipboardEntries = m_selectedEntries;
startMarchingAnts();
invalidate();
}
}
void PaletteView::pasteFromClipboard()
{
if (m_clipboardEditor && m_clipboardEntries.picks()) {
if (m_delegate)
m_delegate->onPaletteViewPasteColors(
m_clipboardEditor, m_clipboardEntries, m_selectedEntries);
stopMarchingAnts();
}
}
bool PaletteView::areColorsInClipboard() const
{
return isMarchingAntsRunning();
}
bool PaletteView::onProcessMessage(Message* msg)
{
switch (msg->type()) {
case kKeyDownMessage: {
Key* key = KeyboardShortcuts::instance()->command(CommandId::Copy);
if (key && key->isPressed(msg)) {
copyToClipboard();
return true;
}
key = KeyboardShortcuts::instance()->command(CommandId::Paste);
if (key && key->isPressed(msg)) {
pasteFromClipboard();
return true;
}
if (isMarchingAntsRunning()) {
key = KeyboardShortcuts::instance()->command(CommandId::DeselectMask);
Key* esc = KeyboardShortcuts::instance()->command(CommandId::Cancel);
if ((key && key->isPressed(msg)) ||
(esc && esc->isPressed(msg))) {
stopMarchingAnts();
invalidate();
return true;
}
}
break;
}
case kMouseDownMessage:
switch (m_hot.part) {
@ -331,39 +395,8 @@ void PaletteView::onPaint(ui::PaintEvent& ev)
if (!m_selectedEntries[i])
continue;
const int max = Palette::MaxColors;
bool top = (i >= m_columns && i-m_columns >= 0 ? m_selectedEntries[i-m_columns]: false);
bool bottom = (i < max-m_columns && i+m_columns < max ? 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 < max ? m_selectedEntries[i+1]: false);
gfx::Rect box = getPaletteEntryBounds(i);
gfx::Rect clipR = box;
box.enlarge(outlineWidth);
if (!left) {
clipR.x -= outlineWidth;
clipR.w += outlineWidth;
}
if (!top) {
clipR.y -= outlineWidth;
clipR.h += outlineWidth;
}
if (!right)
clipR.w += outlineWidth;
else {
clipR.w += guiscale();
box.w += outlineWidth;
}
if (!bottom)
clipR.h += outlineWidth;
else {
clipR.h += guiscale();
box.h += outlineWidth;
}
gfx::Rect box, clipR;
getEntryBoundsAndClip(i, m_selectedEntries, box, clipR, outlineWidth);
IntersectClip clip(g, clipR);
if (clip) {
@ -372,6 +405,25 @@ void PaletteView::onPaint(ui::PaintEvent& ev)
}
}
// Draw marching ants
if (isMarchingAntsRunning() &&
m_clipboardEditor &&
m_clipboardEditor == current_editor) {
for (int i=0; i<palette->size(); ++i) {
if (!m_clipboardEntries[i])
continue;
gfx::Rect box, clipR;
getEntryBoundsAndClip(i, m_clipboardEntries, box, clipR, outlineWidth);
IntersectClip clip(g, clipR);
if (clip) {
CheckedDrawMode checked(g, getMarchingAntsOffset());
g->drawRect(gfx::rgba(0, 0, 0), box);
}
}
}
// Draw drop target
if (m_state == State::DRAGGING_OUTLINE) {
if (m_hot.part == Hit::COLOR) {
@ -414,6 +466,19 @@ void PaletteView::onPreferredSize(ui::PreferredSizeEvent& ev)
ev.setPreferredSize(sz);
}
void PaletteView::onDrawMarchingAnts()
{
invalidate();
}
void PaletteView::onDestroyEditor(Editor* editor)
{
if (m_clipboardEditor == editor) {
setClipboardEditor(nullptr);
stopMarchingAnts();
}
}
void PaletteView::request_size(int* w, int* h)
{
div_t d = div(Palette::MaxColors, m_columns);
@ -464,7 +529,7 @@ void PaletteView::onAppPaletteChange()
invalidate();
}
gfx::Rect PaletteView::getPaletteEntryBounds(int index)
gfx::Rect PaletteView::getPaletteEntryBounds(int index) const
{
gfx::Rect bounds = getClientBounds();
int cols = m_columns;
@ -544,4 +609,66 @@ void PaletteView::dropColors(int beforeIndex)
getManager()->invalidate();
}
void PaletteView::getEntryBoundsAndClip(int i, const PalettePicks& entries,
gfx::Rect& box, gfx::Rect& clip,
int outlineWidth) const
{
box = clip = getPaletteEntryBounds(i);
box.enlarge(outlineWidth);
// Left
if (!pickedXY(entries, i, -1, 0)) {
clip.x -= outlineWidth;
clip.w += outlineWidth;
}
// Top
if (!pickedXY(entries, i, 0, -1)) {
clip.y -= outlineWidth;
clip.h += outlineWidth;
}
// Right
if (!pickedXY(entries, i, +1, 0))
clip.w += outlineWidth;
else {
clip.w += guiscale();
box.w += outlineWidth;
}
// Bottom
if (!pickedXY(entries, i, 0, +1))
clip.h += outlineWidth;
else {
clip.h += guiscale();
box.h += outlineWidth;
}
}
bool PaletteView::pickedXY(const doc::PalettePicks& entries, int i, int dx, int dy) const
{
int x = (i % m_columns) + dx;
int y = (i / m_columns) + dy;
if (x < 0 || x >= m_columns || y < 0 || y > (Palette::MaxColors-1)/m_columns)
return false;
i = x + y*m_columns;
if (i >= 0 && i < entries.size())
return entries[i];
else
return false;
}
void PaletteView::setClipboardEditor(Editor* editor)
{
if (m_clipboardEditor)
m_clipboardEditor->removeObserver(this);
m_clipboardEditor = editor;
if (m_clipboardEditor)
m_clipboardEditor->addObserver(this);
}
} // namespace app

View File

@ -9,7 +9,11 @@
#define APP_UI_PALETTE_VIEW_H_INCLUDED
#pragma once
#include "app/color.h"
#include "app/ui/editor/editor_observer.h"
#include "app/ui/marching_ants.h"
#include "base/connection.h"
#include "doc/palette_picks.h"
#include "ui/event.h"
#include "ui/mouse_buttons.h"
#include "ui/widget.h"
@ -23,19 +27,26 @@ namespace doc {
namespace app {
class Editor;
class PaletteViewDelegate {
public:
virtual ~PaletteViewDelegate() { }
virtual void onPaletteViewIndexChange(int index, ui::MouseButtons buttons) { }
virtual void onPaletteViewRemapColors(const doc::Remap& remap, const doc::Palette* newPalette) { }
virtual void onPaletteViewChangeSize(int boxsize) { }
virtual void onPaletteViewPasteColors(
Editor* editor, const doc::PalettePicks& from, const doc::PalettePicks& to) { }
};
class PaletteView : public ui::Widget {
class PaletteView : public ui::Widget
, public MarchingAnts
, public EditorObserver {
public:
typedef std::vector<bool> SelectedEntries;
PaletteView(bool editable, PaletteViewDelegate* delegate, int boxsize);
~PaletteView();
bool isEditable() const { return m_editable; }
int getColumns() const { return m_columns; }
void setColumns(int columns);
@ -46,18 +57,26 @@ namespace app {
int getSelectedEntry() const;
bool getSelectedRange(int& index1, int& index2) const;
void getSelectedEntries(SelectedEntries& entries) const;
void getSelectedEntries(doc::PalettePicks& entries) const;
app::Color getColorByPosition(const gfx::Point& pos);
int getBoxSize() const;
void setBoxSize(int boxsize);
void copyToClipboard();
void pasteFromClipboard();
bool areColorsInClipboard() const;
protected:
bool onProcessMessage(ui::Message* msg) override;
void onPaint(ui::PaintEvent& ev) override;
void onResize(ui::ResizeEvent& ev) override;
void onPreferredSize(ui::PreferredSizeEvent& ev) override;
void onDrawMarchingAnts() override;
// EditorObserver impl
void onDestroyEditor(Editor* editor) override;
private:
@ -94,9 +113,14 @@ namespace app {
void request_size(int* w, int* h);
void update_scroll(int color);
void onAppPaletteChange();
gfx::Rect getPaletteEntryBounds(int index);
gfx::Rect getPaletteEntryBounds(int index) const;
Hit hitTest(const gfx::Point& pos);
void dropColors(int beforeIndex);
void getEntryBoundsAndClip(int i, const doc::PalettePicks& entries,
gfx::Rect& box, gfx::Rect& clip,
int outlineWidth) const;
bool pickedXY(const doc::PalettePicks& entries, int i, int dx, int dy) const;
void setClipboardEditor(Editor* editor);
State m_state;
bool m_editable;
@ -105,10 +129,12 @@ namespace app {
int m_boxsize;
int m_currentEntry;
int m_rangeAnchor;
SelectedEntries m_selectedEntries;
doc::PalettePicks m_selectedEntries;
doc::PalettePicks m_clipboardEntries;
bool m_isUpdatingColumns;
ScopedConnection m_conn;
Hit m_hot;
Editor* m_clipboardEditor;
};
ui::WidgetType palette_view_type();

65
src/doc/palette_picks.h Normal file
View File

@ -0,0 +1,65 @@
// 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_PALETTE_PICKS_H_INCLUDED
#define DOC_PALETTE_PICKS_H_INCLUDED
#pragma once
#include <algorithm>
#include <vector>
namespace doc {
class PalettePicks {
public:
typedef std::vector<bool> list_type;
typedef list_type::iterator iterator;
typedef list_type::const_iterator const_iterator;
typedef list_type::reference reference;
typedef list_type::const_reference const_reference;
PalettePicks() { }
PalettePicks(int n) : m_items(n, false) { }
int size() const { return int(m_items.size()); }
int picks() const { return std::count(m_items.begin(), m_items.end(), true); }
iterator begin() { return m_items.begin(); }
iterator end() { return m_items.end(); }
const_iterator begin() const { return m_items.begin(); }
const_iterator end() const { return m_items.end(); }
const_reference operator[](int idx) const { return m_items[idx]; }
reference operator[](int idx) { return m_items[idx]; }
// If there is just one selected color (or none), we select them all.
void pickAllIfNeeded() {
if (picks() < 2)
std::fill(m_items.begin(), m_items.end(), true);
}
int firstPick() const {
for (int i=0; i<size(); ++i)
if (m_items[i])
return i;
return -1;
}
int lastPick() const {
for (int i=size()-1; i>=0; --i)
if (m_items[i])
return i;
return -1;
}
private:
list_type m_items;
};
} // namespace doc
#endif

View File

@ -10,18 +10,22 @@
#include "doc/remap.h"
#include "doc/palette_picks.h"
namespace doc {
// TODO this should be a non-member function, it's related to PalettePicks and Remap
// static
Remap Remap::moveSelectedEntriesTo(const std::vector<bool>& selectedEntries, int beforeIndex)
Remap Remap::moveSelectedEntriesTo(const PalettePicks& picks, int beforeIndex)
{
Remap map(selectedEntries.size());
Remap map(picks.size());
int selectedTotal = 0;
int selectedBeforeIndex = 0;
for (int i=0; i<map.size(); ++i) {
if (selectedEntries[i]) {
if (picks[i]) {
++selectedTotal;
if (i < beforeIndex)
++selectedBeforeIndex;
@ -32,7 +36,7 @@ Remap Remap::moveSelectedEntriesTo(const std::vector<bool>& selectedEntries, int
if (k == beforeIndex - selectedBeforeIndex)
k += selectedTotal;
if (selectedEntries[i]) {
if (picks[i]) {
map.map(i, beforeIndex - selectedBeforeIndex + j);
++j;
}

View File

@ -12,6 +12,8 @@
namespace doc {
class PalettePicks;
class Remap {
public:
Remap(int entries) : m_map(entries, 0) { }
@ -19,7 +21,7 @@ namespace doc {
// 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);
const PalettePicks& picks, int beforeIndex);
int size() const {
return (int)m_map.size();