Improve drag-and-drop in ColorBar/PaletteView widget

This commit is contained in:
David Capello 2015-07-04 19:11:50 -03:00
parent be70c1645b
commit b085fee918
3 changed files with 139 additions and 69 deletions

View File

@ -23,11 +23,13 @@
#include "app/ui/skin/style.h" #include "app/ui/skin/style.h"
#include "app/ui/status_bar.h" #include "app/ui/status_bar.h"
#include "app/util/clipboard.h" #include "app/util/clipboard.h"
#include "base/convert_to.h"
#include "doc/image.h" #include "doc/image.h"
#include "doc/palette.h" #include "doc/palette.h"
#include "doc/remap.h" #include "doc/remap.h"
#include "gfx/color.h" #include "gfx/color.h"
#include "gfx/point.h" #include "gfx/point.h"
#include "she/font.h"
#include "ui/graphics.h" #include "ui/graphics.h"
#include "ui/manager.h" #include "ui/manager.h"
#include "ui/message.h" #include "ui/message.h"
@ -307,13 +309,13 @@ bool PaletteView::onProcessMessage(Message* msg)
case kMouseMoveMessage: { case kMouseMoveMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg); MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
if (m_state == State::SELECTING_COLOR) { setStatusBar();
if (m_hot.part == Hit::COLOR) {
if (m_state == State::SELECTING_COLOR &&
m_hot.part == Hit::COLOR) {
int idx = m_hot.color; int idx = m_hot.color;
idx = MID(0, idx, currentPalette()->size()-1); idx = MID(0, idx, currentPalette()->size()-1);
StatusBar::instance()->showColor(
0, "", app::Color::fromIndex(idx));
MouseButtons buttons = mouseMsg->buttons(); MouseButtons buttons = mouseMsg->buttons();
if (hasCapture() && ((idx != m_currentEntry) || if (hasCapture() && ((idx != m_currentEntry) ||
@ -336,7 +338,6 @@ bool PaletteView::onProcessMessage(Message* msg)
m_delegate->onPaletteViewIndexChange(idx, buttons); m_delegate->onPaletteViewIndexChange(idx, buttons);
} }
} }
}
if (hasCapture()) if (hasCapture())
return true; return true;
@ -350,7 +351,10 @@ bool PaletteView::onProcessMessage(Message* msg)
if (m_state == State::DRAGGING_OUTLINE && if (m_state == State::DRAGGING_OUTLINE &&
m_hot.part == Hit::COLOR) { m_hot.part == Hit::COLOR) {
dropColors(m_hot.color + (m_hot.after ? 1: 0)); int i = m_hot.color;
if (!m_copy && i > m_selectedEntries.firstPick())
i += m_selectedEntries.picks();
dropColors(i);
} }
m_state = State::WAITING; m_state = State::WAITING;
@ -409,6 +413,8 @@ void PaletteView::onPaint(ui::PaintEvent& ev)
int fgIndex = -1; int fgIndex = -1;
int bgIndex = -1; int bgIndex = -1;
int transparentIndex = -1; int transparentIndex = -1;
bool drag = (m_state == State::DRAGGING_OUTLINE &&
m_hot.part == Hit::COLOR);
if (m_style == FgBgColors && m_delegate) { if (m_style == FgBgColors && m_delegate) {
fgIndex = findExactIndex(m_delegate->onPaletteViewGetForegroundIndex()); fgIndex = findExactIndex(m_delegate->onPaletteViewGetForegroundIndex());
@ -421,22 +427,23 @@ void PaletteView::onPaint(ui::PaintEvent& ev)
g->fillRect(theme->colors.editorFace(), bounds); g->fillRect(theme->colors.editorFace(), bounds);
// Draw palette entries // Draw palette entries
for (int i=0; i<palette->size(); ++i) { int picksCount = m_selectedEntries.picks();
gfx::Rect box = getPaletteEntryBounds(i); int idxOffset = 0;
doc::color_t palColor = palette->getEntry(i); int boxOffset = 0;
app::Color appColor = app::Color::fromRgb( for (int i=0; i<palette->size()-(drag && !m_copy ? picksCount: 0); ++i) {
rgba_getr(palColor), if (drag) {
rgba_getg(palColor), if (!m_copy) {
rgba_getb(palColor), if (m_selectedEntries[i]) {
rgba_geta(palColor)); ++idxOffset;
gfx::Color gfxColor = gfx::rgba( }
rgba_getr(palColor), }
rgba_getg(palColor), if (!boxOffset && m_hot.color == i) {
rgba_getb(palColor), boxOffset += picksCount;
rgba_geta(palColor)); }
}
g->drawRect(gfx::rgba(0, 0, 0), gfx::Rect(box).enlarge(guiscale())); gfx::Rect box = getPaletteEntryBounds(i + boxOffset);
draw_color(g, box, appColor); gfx::Color gfxColor = drawEntry(g, box, i + idxOffset);
switch (m_style) { switch (m_style) {
@ -467,25 +474,54 @@ void PaletteView::onPaint(ui::PaintEvent& ev)
} }
// Draw selected entries // Draw selected entries
Style::State state = Style::active(); Style::State state = Style::active();
if (m_hot.part == Hit::OUTLINE) state += Style::hover(); if (m_hot.part == Hit::OUTLINE) state += Style::hover();
PalettePicks dragPicks;
int j = 0;
if (drag) {
dragPicks.resize(m_hot.color+picksCount);
std::fill(dragPicks.begin()+m_hot.color, dragPicks.end(), true);
}
PalettePicks& picks = (drag ? dragPicks: m_selectedEntries);
for (int i=0; i<palette->size(); ++i) { for (int i=0; i<palette->size(); ++i) {
if (!m_selectedEntries[i]) if (!m_selectedEntries[i])
continue; continue;
int k = (drag ? m_hot.color+j: i);
gfx::Rect box, clipR; gfx::Rect box, clipR;
getEntryBoundsAndClip(i, m_selectedEntries, box, clipR, outlineWidth); getEntryBoundsAndClip(k, picks, box, clipR, outlineWidth);
IntersectClip clip(g, clipR); IntersectClip clip(g, clipR);
if (clip) { if (clip) {
theme->styles.timelineRangeOutline()->paint(g, box, // Draw color being dragged + label
NULL, state); if (drag) {
gfx::Rect box2 = getPaletteEntryBounds(k);
gfx::Color gfxColor = drawEntry(g, box2, i); // Draw color entry
gfx::Color neg = color_utils::blackandwhite_neg(gfxColor);
she::Font* minifont = theme->getMiniFont();
std::string text = base::convert_to<std::string>(k);
g->setFont(minifont);
g->drawString(text, neg, gfx::ColorNone,
gfx::Point(box2.x + box2.w/2 - minifont->textLength(text)/2,
box2.y + box2.h/2 - minifont->height()/2));
} }
// Draw outlines
theme->styles.timelineRangeOutline()->paint(
g, box, NULL, state);
}
++j;
} }
// Draw marching ants // Draw marching ants
if ((isMarchingAntsRunning()) && if ((m_state == State::WAITING) &&
(isMarchingAntsRunning()) &&
(clipboard::get_current_format() == clipboard::ClipboardPaletteEntries)) { (clipboard::get_current_format() == clipboard::ClipboardPaletteEntries)) {
Palette* clipboardPalette = clipboard::get_palette(); Palette* clipboardPalette = clipboard::get_palette();
const PalettePicks& clipboardPicks = clipboard::get_palette_picks(); const PalettePicks& clipboardPicks = clipboard::get_palette_picks();
@ -507,23 +543,6 @@ void PaletteView::onPaint(ui::PaintEvent& ev)
} }
} }
} }
// Draw drop target
if (m_state == State::DRAGGING_OUTLINE) {
if (m_hot.part == Hit::COLOR) {
gfx::Rect box = getPaletteEntryBounds(m_hot.color);
if (m_hot.after)
box.x += box.w+guiscale();
box.x -= 3*guiscale();
box.y -= 3*guiscale();
box.w = 5*guiscale();
box.h += 6*guiscale();
theme->styles.timelineDropFrameDeco()->paint(g,
box, NULL, Style::active());
}
}
} }
void PaletteView::onResize(ui::ResizeEvent& ev) void PaletteView::onResize(ui::ResizeEvent& ev)
@ -660,7 +679,6 @@ PaletteView::Hit PaletteView::hitTest(const gfx::Point& pos)
box.h += childSpacing(); box.h += childSpacing();
if (box.contains(pos)) { if (box.contains(pos)) {
Hit hit(Hit::COLOR, i); Hit hit(Hit::COLOR, i);
hit.after = (pos.x > box.x+box.w/2);
return hit; return hit;
} }
} }
@ -684,7 +702,9 @@ void PaletteView::dropColors(int beforeIndex)
int picks = m_selectedEntries.picks(); int picks = m_selectedEntries.picks();
ASSERT(picks >= 1); ASSERT(picks >= 1);
remap = create_remap_to_expand_palette(palette.size(), picks, beforeIndex); remap = create_remap_to_expand_palette(palette.size()+picks,
picks,
beforeIndex);
newPalette.resize(palette.size()+picks); newPalette.resize(palette.size()+picks);
for (int i=0; i<palette.size(); ++i) for (int i=0; i<palette.size(); ++i)
@ -764,7 +784,7 @@ bool PaletteView::pickedXY(const doc::PalettePicks& entries, int i, int dx, int
{ {
int x = (i % m_columns) + dx; int x = (i % m_columns) + dx;
int y = (i / m_columns) + dy; int y = (i / m_columns) + dy;
int lastcolor = currentPalette()->size()-1; int lastcolor = entries.size()-1;
if (x < 0 || x >= m_columns || y < 0 || y > lastcolor/m_columns) if (x < 0 || x >= m_columns || y < 0 || y > lastcolor/m_columns)
return false; return false;
@ -780,8 +800,11 @@ void PaletteView::updateCopyFlag(ui::Message* msg)
{ {
bool oldCopy = m_copy; bool oldCopy = m_copy;
m_copy = (msg->ctrlPressed() || msg->altPressed()); m_copy = (msg->ctrlPressed() || msg->altPressed());
if (oldCopy != m_copy) if (oldCopy != m_copy) {
setCursor(); setCursor();
setStatusBar();
invalidate();
}
} }
void PaletteView::setCursor() void PaletteView::setCursor()
@ -797,6 +820,34 @@ void PaletteView::setCursor()
ui::set_mouse_cursor(kArrowCursor); ui::set_mouse_cursor(kArrowCursor);
} }
void PaletteView::setStatusBar()
{
switch (m_state) {
case State::WAITING:
case State::SELECTING_COLOR:
if (m_hot.part == Hit::COLOR) {
int i = MID(0, m_hot.color, currentPalette()->size()-1);
StatusBar::instance()->showColor(
0, "", app::Color::fromIndex(i));
}
break;
case State::DRAGGING_OUTLINE:
if (m_hot.part == Hit::COLOR) {
int i = MAX(0, m_hot.color);
if (!m_copy && i <= m_selectedEntries.firstPick())
i -= m_selectedEntries.picks();
StatusBar::instance()->setStatusText(
0, "%s to %d", (m_copy ? "Copy": "Move"), i);
}
break;
}
}
doc::Palette* PaletteView::currentPalette() const doc::Palette* PaletteView::currentPalette() const
{ {
return get_current_palette(); return get_current_palette();
@ -837,4 +888,23 @@ void PaletteView::setNewPalette(doc::Palette* oldPalette, doc::Palette* newPalet
getManager()->invalidate(); getManager()->invalidate();
} }
gfx::Color PaletteView::drawEntry(ui::Graphics* g, const gfx::Rect& box, int palIdx)
{
doc::color_t palColor = currentPalette()->getEntry(palIdx);
app::Color appColor = app::Color::fromRgb(
rgba_getr(palColor),
rgba_getg(palColor),
rgba_getb(palColor),
rgba_geta(palColor));
gfx::Color gfxColor = gfx::rgba(
rgba_getr(palColor),
rgba_getg(palColor),
rgba_getb(palColor),
rgba_geta(palColor));
g->drawRect(gfx::rgba(0, 0, 0), gfx::Rect(box).enlarge(guiscale()));
draw_color(g, box, appColor);
return gfxColor;
}
} // namespace app } // namespace app

View File

@ -99,16 +99,14 @@ namespace app {
}; };
Part part; Part part;
int color; int color;
bool after;
Hit(Part part, int color = -1) : part(part), color(color), after(false) { Hit(Part part, int color = -1) : part(part), color(color) {
} }
bool operator==(const Hit& hit) const { bool operator==(const Hit& hit) const {
return ( return (
part == hit.part && part == hit.part &&
color == hit.color && color == hit.color);
after == hit.after);
} }
bool operator!=(const Hit& hit) const { bool operator!=(const Hit& hit) const {
return !operator==(hit); return !operator==(hit);
@ -127,9 +125,11 @@ namespace app {
bool pickedXY(const doc::PalettePicks& entries, int i, int dx, int dy) const; bool pickedXY(const doc::PalettePicks& entries, int i, int dx, int dy) const;
void updateCopyFlag(ui::Message* msg); void updateCopyFlag(ui::Message* msg);
void setCursor(); void setCursor();
void setStatusBar();
doc::Palette* currentPalette() const; doc::Palette* currentPalette() const;
int findExactIndex(const app::Color& color) const; int findExactIndex(const app::Color& color) const;
void setNewPalette(doc::Palette* oldPalette, doc::Palette* newPalette, const doc::Remap& remap); void setNewPalette(doc::Palette* oldPalette, doc::Palette* newPalette, const doc::Remap& remap);
gfx::Color drawEntry(ui::Graphics* g, const gfx::Rect& box, int palIdx);
State m_state; State m_state;
bool m_editable; bool m_editable;

View File

@ -37,7 +37,7 @@ namespace doc {
reference operator[](int idx) { return m_items[idx]; } reference operator[](int idx) { return m_items[idx]; }
void resize(int n) { void resize(int n) {
m_items.resize(n); m_items.resize(n, false);
} }
void clear() { void clear() {