mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-12 16:14:10 +00:00
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:
parent
feb592db73
commit
62a3594598
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
|
64
src/app/ui/marching_ants.h
Normal file
64
src/app/ui/marching_ants.h
Normal 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
|
@ -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
|
||||
|
@ -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
65
src/doc/palette_picks.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user