Add HSV-saturation/value sliders in ColorSpectrum/Wheel (related to #707)

Simplified ColorSelectors using more shared code in the base class.
This commit is contained in:
David Capello 2017-05-29 16:39:48 -03:00
parent 29a49ad7fa
commit b7f8efc0a3
8 changed files with 337 additions and 403 deletions

View File

@ -10,20 +10,29 @@
#include "app/ui/color_selector.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h"
#include "she/surface.h"
#include "ui/manager.h"
#include "ui/message.h"
#include "ui/paint_event.h"
#include "ui/scale.h"
#include "ui/size_hint_event.h"
#include "ui/system.h"
#include <cmath>
namespace app {
using namespace app::skin;
using namespace ui;
ColorSelector::ColorSelector()
: Widget(kGenericWidget)
, m_lockColor(false)
, m_capturedInBottom(false)
{
setBorder(gfx::Border(3*ui::guiscale()));
}
void ColorSelector::selectColor(const app::Color& color)
@ -35,6 +44,26 @@ void ColorSelector::selectColor(const app::Color& color)
invalidate();
}
app::Color ColorSelector::getColorByPosition(const gfx::Point& pos)
{
gfx::Rect rc = childrenBounds();
if (rc.isEmpty())
return app::Color::fromMask();
int u, v, umax, vmax;
int barSize = getBottomBarSize();
u = pos.x - rc.x;
v = pos.y - rc.y;
umax = MAX(1, rc.w-1);
vmax = MAX(1, rc.h-1-barSize);
if (( hasCapture() && m_capturedInBottom) ||
(!hasCapture() && inBottomBarArea(pos)))
return getBottomBarColor(u, umax);
else
return getMainAreaColor(u, umax, v, vmax);
}
void ColorSelector::onSizeHint(SizeHintEvent& ev)
{
ev.setSizeHint(gfx::Size(32*ui::guiscale(), 32*ui::guiscale()));
@ -44,6 +73,47 @@ bool ColorSelector::onProcessMessage(ui::Message* msg)
{
switch (msg->type()) {
case kMouseDownMessage:
if (manager()->getCapture())
break;
captureMouse();
// Continue...
case kMouseMoveMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
if (msg->type() == kMouseDownMessage)
m_capturedInBottom = inBottomBarArea(mouseMsg->position());
app::Color color = getColorByPosition(mouseMsg->position());
if (color != app::Color::fromMask()) {
// base::ScopedValue<bool> switcher(m_lockColor, m_harmonyPicked, false);
StatusBar::instance()->showColor(0, "", color);
if (hasCapture())
ColorChange(color, mouseMsg->buttons());
}
break;
}
case kMouseUpMessage:
if (hasCapture()) {
releaseMouse();
}
return true;
case kSetCursorMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
app::Color color = getColorByPosition(mouseMsg->position());
if (color.getType() != app::Color::MaskType) {
ui::set_mouse_cursor(kCustomCursor, SkinTheme::instance()->cursors.eyedropper());
return true;
}
break;
}
case kMouseWheelMessage:
if (!hasCapture()) {
double scale = 1.0;
@ -78,4 +148,58 @@ bool ColorSelector::onProcessMessage(ui::Message* msg)
return Widget::onProcessMessage(msg);
}
void ColorSelector::onPaint(ui::PaintEvent& ev)
{
ui::Graphics* g = ev.graphics();
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
theme->drawRect(g, clientBounds(),
theme->parts.editorNormal().get(),
false); // Do not fill the center
gfx::Rect rc = clientChildrenBounds();
if (rc.isEmpty())
return;
int barSize = getBottomBarSize();
rc.h -= barSize;
onPaintMainArea(g, rc);
if (barSize > 0) {
rc.y += rc.h;
rc.h = barSize;
onPaintBottomBar(g, rc);
}
}
void ColorSelector::paintColorIndicator(ui::Graphics* g,
const gfx::Point& pos,
const bool white)
{
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
she::Surface* icon = theme->parts.colorWheelIndicator()->bitmap(0);
g->drawColoredRgbaSurface(
icon,
white ? gfx::rgba(255, 255, 255): gfx::rgba(0, 0, 0),
pos.x-icon->width()/2,
pos.y-icon->height()/2);
}
bool ColorSelector::inBottomBarArea(const gfx::Point& pos) const
{
gfx::Rect rc = childrenBounds();
if (rc.isEmpty() || !rc.contains(pos))
return false;
else
return (pos.y >= rc.y+rc.h-getBottomBarSize());
}
int ColorSelector::getBottomBarSize() const
{
gfx::Rect rc = clientChildrenBounds();
int size = 8*guiscale();
return rc.h < 2*size ? 0: size;
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2016 David Capello
// Copyright (C) 2016-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -23,19 +23,43 @@ namespace app {
void selectColor(const app::Color& color);
// IColorSource impl
app::Color getColorByPosition(const gfx::Point& pos) override;
// Signals
obs::signal<void(const app::Color&, ui::MouseButtons)> ColorChange;
protected:
void onSizeHint(ui::SizeHintEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
virtual app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) = 0;
virtual app::Color getBottomBarColor(const int u, const int umax) = 0;
virtual void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) = 0;
virtual void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) = 0;
void paintColorIndicator(ui::Graphics* g,
const gfx::Point& pos,
const bool white);
app::Color m_color;
private:
void onSizeHint(ui::SizeHintEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
void onPaint(ui::PaintEvent& ev) override;
bool inBottomBarArea(const gfx::Point& pos) const;
int getBottomBarSize() const;
// Internal flag used to lock the modification of m_color.
// E.g. When the user picks a color harmony, we don't want to
// change the main color.
bool m_lockColor;
// True when the user pressed the mouse button in the bottom
// slider. It's used to avoid swapping in both areas (main color
// area vs bottom slider) when we drag the mouse above this
// widget.
bool m_capturedInBottom;
};
} // namespace app

View File

@ -29,140 +29,77 @@ using namespace ui;
ColorSpectrum::ColorSpectrum()
{
setAlign(HORIZONTAL);
setBorder(gfx::Border(3*ui::guiscale()));
}
app::Color ColorSpectrum::getColorByPosition(const gfx::Point& pos)
app::Color ColorSpectrum::getMainAreaColor(const int u, const int umax,
const int v, const int vmax)
{
gfx::Rect rc = childrenBounds();
if (rc.isEmpty())
return app::Color::fromMask();
int vmid = (align() & HORIZONTAL ? rc.h/2 : rc.w/2);
vmid = MAX(1, vmid);
int u, v, umax;
if (align() & HORIZONTAL) {
u = pos.x - rc.x;
v = pos.y - rc.y;
umax = MAX(1, rc.w-1);
}
else {
u = pos.y - rc.y;
v = pos.x - rc.x;
umax = MAX(1, rc.h-1);
}
double hue = 360.0 * u / umax;
double sat = (v < vmid ? double(v) / double(vmid) : 1.0);
double val = (v < vmid ? 1.0 : 1.0-(double(v-vmid) / double(vmid)));
return app::Color::fromHsv(
double lit = 1.0 - (double(v)/double(vmax));
return app::Color::fromHsl(
MID(0.0, hue, 360.0),
MID(0.0, sat, 1.0),
MID(0.0, val, 1.0));
m_color.getHslSaturation(),
MID(0.0, lit, 1.0));
}
void ColorSpectrum::onPaint(ui::PaintEvent& ev)
app::Color ColorSpectrum::getBottomBarColor(const int u, const int umax)
{
ui::Graphics* g = ev.graphics();
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
double sat = double(u) / double(umax);
return app::Color::fromHsl(
m_color.getHslHue(),
MID(0.0, sat, 1.0),
m_color.getHslLightness());
}
theme->drawRect(g, clientBounds(),
theme->parts.editorNormal().get(),
false); // Do not fill the center
gfx::Rect rc = clientChildrenBounds();
if (rc.isEmpty())
return;
int vmid = (align() & HORIZONTAL ? rc.h/2 : rc.w/2);
vmid = MAX(1, vmid);
void ColorSpectrum::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
{
double sat = m_color.getHslSaturation();
int umax = MAX(1, rc.w-1);
int vmax = MAX(1, rc.h-1);
for (int y=0; y<rc.h; ++y) {
for (int x=0; x<rc.w; ++x) {
int u, v, umax;
if (align() & HORIZONTAL) {
u = x;
v = y;
umax = MAX(1, rc.w-1);
}
else {
u = y;
v = x;
umax = MAX(1, rc.h-1);
}
double hue = 360.0 * u / umax;
double sat = (v < vmid ? double(v) / double(vmid) : 1.0);
double val = (v < vmid ? 1.0 : 1.0-(double(v-vmid) / double(vmid)));
double hue = 360.0 * double(x) / double(umax);
double lit = 1.0 - double(y) / double(vmax);
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(
app::Color::fromHsl(
MID(0.0, hue, 360.0),
MID(0.0, sat, 1.0),
MID(0.0, val, 1.0)));
sat,
MID(0.0, lit, 1.0)));
g->putPixel(color, rc.x+x, rc.y+y);
}
}
if (m_color.getType() != app::Color::MaskType) {
double hue = m_color.getHsvHue();
double sat = m_color.getHsvSaturation();
double val = m_color.getHsvValue();
double lit = (2.0 - sat) * val / 2.0;
double hue = m_color.getHslHue();
double lit = m_color.getHslLightness();
gfx::Point pos(rc.x + int(hue * rc.w / 360.0),
rc.y + rc.h - int(lit * rc.h));
she::Surface* icon = theme->parts.colorWheelIndicator()->bitmap(0);
g->drawColoredRgbaSurface(
icon,
lit > 0.5 ? gfx::rgba(0, 0, 0): gfx::rgba(255, 255, 255),
pos.x-icon->width()/2,
pos.y-icon->height()/2);
paintColorIndicator(g, pos, lit < 0.5);
}
}
bool ColorSpectrum::onProcessMessage(ui::Message* msg)
void ColorSpectrum::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
{
switch (msg->type()) {
double hue = m_color.getHslHue();
double lit = m_color.getHslLightness();
case kMouseDownMessage:
captureMouse();
// Continue...
case kMouseMoveMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
app::Color color = getColorByPosition(mouseMsg->position());
if (color != app::Color::fromMask()) {
StatusBar::instance()->showColor(0, "", color);
if (hasCapture())
ColorChange(color, mouseMsg->buttons());
}
break;
}
case kMouseUpMessage:
if (hasCapture()) {
releaseMouse();
}
return true;
case kSetCursorMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
if (childrenBounds().contains(mouseMsg->position())) {
ui::set_mouse_cursor(kCustomCursor, SkinTheme::instance()->cursors.eyedropper());
return true;
}
break;
}
for (int x=0; x<rc.w; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsl(hue, double(x) / double(rc.w), lit));
g->drawVLine(color, rc.x+x, rc.y, rc.h);
}
return ColorSelector::onProcessMessage(msg);
if (m_color.getType() != app::Color::MaskType) {
double sat = m_color.getHslSaturation();
gfx::Point pos(rc.x + int(double(rc.w) * sat),
rc.y + rc.h/2);
paintColorIndicator(g, pos, false);
}
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -9,7 +9,6 @@
#pragma once
#include "app/ui/color_selector.h"
#include "ui/button.h"
namespace app {
@ -17,12 +16,12 @@ namespace app {
public:
ColorSpectrum();
// IColorSource
app::Color getColorByPosition(const gfx::Point& pos) override;
protected:
void onPaint(ui::PaintEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) override;
app::Color getBottomBarColor(const int u, const int umax) override;
void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) override;
};
} // namespace app

View File

@ -12,14 +12,7 @@
#include "app/color_utils.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h"
#include "she/surface.h"
#include "ui/graphics.h"
#include "ui/message.h"
#include "ui/paint_event.h"
#include "ui/size_hint_event.h"
#include "ui/resize_event.h"
#include "ui/system.h"
namespace app {
@ -28,67 +21,36 @@ using namespace gfx;
using namespace ui;
ColorTintShadeTone::ColorTintShadeTone()
: m_capturedInHue(false)
{
setBorder(gfx::Border(3*ui::guiscale()));
}
app::Color ColorTintShadeTone::getColorByPosition(const gfx::Point& pos)
app::Color ColorTintShadeTone::getMainAreaColor(const int u, const int umax,
const int v, const int vmax)
{
gfx::Rect rc = childrenBounds();
if (rc.isEmpty())
return app::Color::fromMask();
int u, v, umax, vmax;
int huebar = getHueBarSize();
u = pos.x - rc.x;
v = pos.y - rc.y;
umax = MAX(1, rc.w-1);
vmax = MAX(1, rc.h-1-huebar);
double hue, sat, val;
bool inHue =
(( hasCapture() && m_capturedInHue) ||
(!hasCapture() && inHueBarArea(pos)));
if (inHue) {
hue = (360.0 * u / umax);
sat = m_color.getHsvSaturation();
val = m_color.getHsvValue();
}
else {
hue = m_color.getHsvHue();
sat = (1.0 * u / umax);
val = (1.0 - double(v) / double(vmax));
}
double sat = (1.0 * u / umax);
double val = (1.0 - double(v) / double(vmax));
return app::Color::fromHsv(
MID(0.0, hue, 360.0),
m_color.getHsvHue(),
MID(0.0, sat, 1.0),
MID(0.0, val, 1.0));
}
void ColorTintShadeTone::onPaint(ui::PaintEvent& ev)
app::Color ColorTintShadeTone::getBottomBarColor(const int u, const int umax)
{
ui::Graphics* g = ev.graphics();
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
theme->drawRect(g, clientBounds(),
theme->parts.editorNormal().get(),
false); // Do not fill the center
gfx::Rect rc = clientChildrenBounds();
if (rc.isEmpty())
return;
double hue = (360.0 * u / umax);
return app::Color::fromHsv(
MID(0.0, hue, 360.0),
m_color.getHsvSaturation(),
m_color.getHsvValue());
}
void ColorTintShadeTone::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
{
double hue = m_color.getHsvHue();
int umax, vmax;
int huebar = getHueBarSize();
umax = MAX(1, rc.w-1);
vmax = MAX(1, rc.h-1-huebar);
int umax = MAX(1, rc.w-1);
int vmax = MAX(1, rc.h-1);
for (int y=0; y<rc.h-huebar; ++y) {
for (int y=0; y<rc.h; ++y) {
for (int x=0; x<rc.w; ++x) {
double sat = double(x) / double(umax);
double val = 1.0 - double(y) / double(vmax);
@ -103,104 +65,32 @@ void ColorTintShadeTone::onPaint(ui::PaintEvent& ev)
}
}
if (huebar > 0) {
for (int y=rc.h-huebar; y<rc.h; ++y) {
for (int x=0; x<rc.w; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(
(360.0 * x / rc.w), 1.0, 1.0));
g->putPixel(color, rc.x+x, rc.y+y);
}
}
}
if (m_color.getType() != app::Color::MaskType) {
double sat = m_color.getHsvSaturation();
double val = m_color.getHsvValue();
gfx::Point pos(rc.x + int(sat * rc.w),
rc.y + int((1.0-val) * (rc.h-huebar)));
rc.y + int((1.0-val) * rc.h));
she::Surface* icon = theme->parts.colorWheelIndicator()->bitmap(0);
g->drawColoredRgbaSurface(
icon,
val > 0.5 ? gfx::rgba(0, 0, 0): gfx::rgba(255, 255, 255),
pos.x-icon->width()/2,
pos.y-icon->height()/2);
if (huebar > 0) {
pos.x = rc.x + int(rc.w * hue / 360.0);
pos.y = rc.y + rc.h - huebar/2;
g->drawColoredRgbaSurface(
icon,
gfx::rgba(0, 0, 0),
pos.x-icon->width()/2,
pos.y-icon->height()/2);
}
paintColorIndicator(g, pos, val < 0.5);
}
}
bool ColorTintShadeTone::onProcessMessage(ui::Message* msg)
void ColorTintShadeTone::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
{
switch (msg->type()) {
case kMouseDownMessage:
if (manager()->getCapture())
break;
captureMouse();
// Continue...
case kMouseMoveMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
if (msg->type() == kMouseDownMessage)
m_capturedInHue = inHueBarArea(mouseMsg->position());
app::Color color = getColorByPosition(mouseMsg->position());
if (color != app::Color::fromMask()) {
StatusBar::instance()->showColor(0, "", color);
if (hasCapture())
ColorChange(color, mouseMsg->buttons());
}
break;
}
case kMouseUpMessage:
if (hasCapture()) {
releaseMouse();
}
return true;
case kSetCursorMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
if (childrenBounds().contains(mouseMsg->position())) {
ui::set_mouse_cursor(kCustomCursor, SkinTheme::instance()->cursors.eyedropper());
return true;
}
break;
}
for (int x=0; x<rc.w; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(
(360.0 * x / rc.w), 1.0, 1.0));
g->drawVLine(color, rc.x+x, rc.y, rc.h);
}
return ColorSelector::onProcessMessage(msg);
}
bool ColorTintShadeTone::inHueBarArea(const gfx::Point& pos) const
{
gfx::Rect rc = childrenBounds();
if (rc.isEmpty() || !rc.contains(pos))
return false;
else
return (pos.y >= rc.y+rc.h-getHueBarSize());
}
int ColorTintShadeTone::getHueBarSize() const
{
gfx::Rect rc = clientChildrenBounds();
int size = 8*guiscale();
return rc.h < 2*size ? 0: size;
if (m_color.getType() != app::Color::MaskType) {
double hue = m_color.getHsvHue();
gfx::Point pos(rc.x + int(rc.w * hue / 360.0),
rc.y + rc.h/2);
paintColorIndicator(g, pos, false);
}
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2016 David Capello
// Copyright (C) 2016-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -16,21 +16,12 @@ namespace app {
public:
ColorTintShadeTone();
// IColorSource
app::Color getColorByPosition(const gfx::Point& pos) override;
protected:
void onPaint(ui::PaintEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
private:
bool inHueBarArea(const gfx::Point& pos) const;
int getHueBarSize() const;
// True when the user pressed the mouse button in the hue slider.
// It's used to avoid swapping in both areas (tint/shades/tones
// area vs hue slider) when we drag the mouse above this widget.
bool m_capturedInHue;
app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) override;
app::Color getBottomBarColor(const int u, const int umax) override;
void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) override;
};
} // namespace app

View File

@ -56,7 +56,6 @@ ColorWheel::ColorWheel()
, m_harmonyPicked(false)
{
SkinTheme* theme = SkinTheme::instance();
setBorder(gfx::Border(3*ui::guiscale()));
m_options.Click.connect(base::Bind<void>(&ColorWheel::onOptions, this));
m_options.setStyle(theme->styles.colorWheelOptions());
@ -64,17 +63,13 @@ ColorWheel::ColorWheel()
addChild(&m_options);
}
app::Color ColorWheel::getColorByPosition(const gfx::Point& mousePos)
{
return getColorInClientPos(mousePos - bounds().origin());
}
app::Color ColorWheel::getColorInClientPos(const gfx::Point& pos)
app::Color ColorWheel::getMainAreaColor(const int _u, const int umax,
const int _v, const int vmax)
{
m_harmonyPicked = false;
int u = (pos.x - (m_wheelBounds.x+m_wheelBounds.w/2));
int v = (pos.y - (m_wheelBounds.y+m_wheelBounds.h/2));
int u = _u - umax/2;
int v = _v - vmax/2;
double d = std::sqrt(u*u + v*v);
// Pick from the wheel
@ -106,7 +101,7 @@ app::Color ColorWheel::getColorInClientPos(const gfx::Point& pos)
return app::Color::fromHsv(
MID(0, hue, 360),
MID(0, sat / 100.0, 1.0),
1.0);
m_color.getHsvValue());
}
// Pick harmonies
@ -120,7 +115,7 @@ app::Color ColorWheel::getColorInClientPos(const gfx::Point& pos)
if (gfx::Rect(rc.x+rc.w-(n-i)*boxsize,
rc.y+rc.h-boxsize,
boxsize, boxsize).contains(pos)) {
boxsize, boxsize).contains(gfx::Point(u, v))) {
m_harmonyPicked = true;
color = app::Color::fromHsv(convertHueAngle(int(color.getHsvHue()), 1),
@ -134,6 +129,97 @@ app::Color ColorWheel::getColorInClientPos(const gfx::Point& pos)
return app::Color::fromMask();
}
app::Color ColorWheel::getBottomBarColor(const int u, const int umax)
{
double val = double(u) / double(umax);
return app::Color::fromHsv(
m_color.getHsvHue(),
m_color.getHsvSaturation(),
MID(0.0, val, 1.0));
}
void ColorWheel::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
{
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
int r = MIN(rc.w/2, rc.h/2);
m_clientBounds = rc;
m_wheelRadius = r;
m_wheelBounds = gfx::Rect(rc.x+rc.w/2-r,
rc.y+rc.h/2-r,
r*2, r*2);
int umax = MAX(1, rc.w-1);
int vmax = MAX(1, rc.h-1);
for (int y=0; y<rc.h; ++y) {
for (int x=0; x<rc.w; ++x) {
app::Color appColor =
getMainAreaColor(x, umax,
y, vmax);
gfx::Color color;
if (appColor.getType() != app::Color::MaskType) {
color = color_utils::color_for_ui(appColor);
}
else {
color = theme->colors.editorFace();
}
g->putPixel(color, rc.x+x, rc.y+y);
}
}
if (m_color.getAlpha() > 0) {
int n = getHarmonies();
int boxsize = MIN(rc.w/10, rc.h/10);
for (int i=0; i<n; ++i) {
app::Color color = getColorInHarmony(i);
double angle = color.getHsvHue()-30.0;
double dist = color.getHsvSaturation();
color = app::Color::fromHsv(convertHueAngle(int(color.getHsvHue()), 1),
color.getHsvSaturation(),
color.getHsvValue());
gfx::Point pos =
m_wheelBounds.center() +
gfx::Point(int(+std::cos(PI*angle/180.0)*double(m_wheelRadius)*dist),
int(-std::sin(PI*angle/180.0)*double(m_wheelRadius)*dist));
paintColorIndicator(g, pos, false);
g->fillRect(gfx::rgba(color.getRed(),
color.getGreen(),
color.getBlue(), 255),
gfx::Rect(rc.x+rc.w-(n-i)*boxsize,
rc.y+rc.h-boxsize,
boxsize, boxsize));
}
}
}
void ColorWheel::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
{
double hue = m_color.getHsvHue();
double sat = m_color.getHsvValue();
for (int x=0; x<rc.w; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(hue, sat, double(x) / double(rc.w)));
g->drawVLine(color, rc.x+x, rc.y, rc.h);
}
if (m_color.getType() != app::Color::MaskType) {
double val = m_color.getHsvValue();
gfx::Point pos(rc.x + int(double(rc.w) * val),
rc.y + rc.h/2);
paintColorIndicator(g, pos, false);
}
}
void ColorWheel::setDiscrete(bool state)
{
m_discrete = state;
@ -180,14 +266,6 @@ void ColorWheel::onResize(ui::ResizeEvent& ev)
ColorSelector::onResize(ev);
gfx::Rect rc = clientChildrenBounds();
int r = MIN(rc.w/2, rc.h/2);
m_clientBounds = rc;
m_wheelRadius = r;
m_wheelBounds = gfx::Rect(rc.x+rc.w/2-r,
rc.y+rc.h/2-r,
r*2, r*2);
gfx::Size prefSize = m_options.sizeHint();
rc = childrenBounds();
rc.x += rc.w-prefSize.w;
@ -196,116 +274,6 @@ void ColorWheel::onResize(ui::ResizeEvent& ev)
m_options.setBounds(rc);
}
void ColorWheel::onPaint(ui::PaintEvent& ev)
{
ui::Graphics* g = ev.graphics();
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
theme->drawRect(g, clientBounds(),
theme->parts.editorNormal().get(),
false); // Do not fill the center
const gfx::Rect& rc = m_clientBounds;
for (int y=rc.y; y<rc.y+rc.h; ++y) {
for (int x=rc.x; x<rc.x+rc.w; ++x) {
app::Color appColor =
ColorWheel::getColorInClientPos(gfx::Point(x, y));
gfx::Color color;
if (appColor.getType() != app::Color::MaskType) {
color = color_utils::color_for_ui(appColor);
}
else {
color = theme->colors.editorFace();
}
g->putPixel(color, x, y);
}
}
if (m_color.getAlpha() > 0) {
int n = getHarmonies();
int boxsize = MIN(rc.w/10, rc.h/10);
for (int i=0; i<n; ++i) {
app::Color color = getColorInHarmony(i);
double angle = color.getHsvHue()-30.0;
double dist = color.getHsvSaturation();
color = app::Color::fromHsv(convertHueAngle(int(color.getHsvHue()), 1),
color.getHsvSaturation(),
color.getHsvValue());
gfx::Point pos =
m_wheelBounds.center() +
gfx::Point(int(+std::cos(PI*angle/180.0)*double(m_wheelRadius)*dist),
int(-std::sin(PI*angle/180.0)*double(m_wheelRadius)*dist));
she::Surface* icon = theme->parts.colorWheelIndicator()->bitmap(0);
g->drawRgbaSurface(icon,
pos.x-icon->width()/2,
pos.y-icon->height()/2);
g->fillRect(gfx::rgba(color.getRed(),
color.getGreen(),
color.getBlue(), 255),
gfx::Rect(rc.x+rc.w-(n-i)*boxsize,
rc.y+rc.h-boxsize,
boxsize, boxsize));
}
}
}
bool ColorWheel::onProcessMessage(ui::Message* msg)
{
switch (msg->type()) {
case kMouseDownMessage:
captureMouse();
// Continue...
case kMouseMoveMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
app::Color color = getColorInClientPos(
mouseMsg->position()
- bounds().origin());
if (color != app::Color::fromMask()) {
base::ScopedValue<bool> switcher(m_lockColor, m_harmonyPicked, false);
StatusBar::instance()->showColor(0, "", color);
if (hasCapture())
ColorChange(color, mouseMsg->buttons());
}
break;
}
case kMouseUpMessage:
if (hasCapture()) {
releaseMouse();
}
return true;
case kSetCursorMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
app::Color color = getColorInClientPos(
mouseMsg->position()
- bounds().origin());
if (color.getType() != app::Color::MaskType) {
ui::set_mouse_cursor(kCustomCursor, SkinTheme::instance()->cursors.eyedropper());
return true;
}
break;
}
}
return ColorSelector::onProcessMessage(msg);
}
void ColorWheel::onOptions()
{
Menu menu;

View File

@ -34,20 +34,21 @@ namespace app {
ColorWheel();
// IColorSource
app::Color getColorByPosition(const gfx::Point& pos) override;
bool isDiscrete() const { return m_discrete; }
void setDiscrete(bool state);
void setColorModel(ColorModel colorModel);
void setHarmony(Harmony harmony);
protected:
app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) override;
app::Color getBottomBarColor(const int u, const int umax) override;
void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) override;
private:
app::Color getColorInClientPos(const gfx::Point& pos);
void onResize(ui::ResizeEvent& ev) override;
void onPaint(ui::PaintEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
void onOptions();
int getHarmonies() const;
app::Color getColorInHarmony(int i) const;