Add a basic RGB color wheel (related to #707)

This commit is contained in:
David Capello 2015-08-06 11:23:16 -03:00
parent d9af752e9d
commit da8eff0ec2
11 changed files with 435 additions and 15 deletions

View File

@ -767,6 +767,13 @@
<param name="size" value="17" />
</item>
<separator />
<item command="SetColorSelector" text="Color Spectrum">
<param name="type" value="spectrum" />
</item>
<item command="SetColorSelector" text="Color Wheel">
<param name="type" value="wheel" />
</item>
<separator />
<item command="LoadPalette" text="L&amp;oad Palette" />
<item command="SavePalette" text="S&amp;ave Palette" />
<item command="LoadPalette" text="Load Default Palette">

View File

@ -103,6 +103,8 @@
<option id="box_size" type="int" default="11" />
<option id="fg_color" type="app::Color" default="app::Color::fromRgb(255, 255, 255)" />
<option id="bg_color" type="app::Color" default="app::Color::fromRgb(0, 0, 0)" />
<option id="selector" type="app::ColorBar::ColorSelector" default="app::ColorBar::ColorSelector::SPECTRUM" />
<option id="discrete_wheel" type="bool" default="false" />
</section>
<section id="tool_box">
<option id="active_tool" type="std::string" default="&quot;pencil&quot;" />

View File

@ -221,6 +221,7 @@ add_library(app-lib
commands/cmd_save_mask.cpp
commands/cmd_save_palette.cpp
commands/cmd_scroll.cpp
commands/cmd_set_color_selector.cpp
commands/cmd_set_loop_section.cpp
commands/cmd_set_palette.cpp
commands/cmd_set_palette_entry_size.cpp
@ -312,6 +313,7 @@ add_library(app-lib
ui/color_selector.cpp
ui/color_sliders.cpp
ui/color_spectrum.cpp
ui/color_wheel.cpp
ui/configure_timeline_popup.cpp
ui/context_bar.cpp
ui/devconsole_view.cpp

View File

@ -0,0 +1,68 @@
// 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.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/params.h"
#include "app/ui/color_bar.h"
namespace app {
class SetColorSelectorCommand : public Command {
public:
SetColorSelectorCommand();
Command* clone() const override { return new SetColorSelectorCommand(*this); }
protected:
void onLoadParams(const Params& params) override;
bool onChecked(Context* context) override;
void onExecute(Context* context) override;
private:
ColorBar::ColorSelector m_type;
};
SetColorSelectorCommand::SetColorSelectorCommand()
: Command("SetColorSelector",
"Set Color Selector",
CmdUIOnlyFlag)
, m_type(ColorBar::ColorSelector::SPECTRUM)
{
}
void SetColorSelectorCommand::onLoadParams(const Params& params)
{
std::string type = params.get("type");
if (type == "spectrum") {
m_type = ColorBar::ColorSelector::SPECTRUM;
}
else if (type == "wheel") {
m_type = ColorBar::ColorSelector::WHEEL;
}
}
bool SetColorSelectorCommand::onChecked(Context* context)
{
return (ColorBar::instance()->getColorSelector() == m_type);
}
void SetColorSelectorCommand::onExecute(Context* context)
{
ColorBar::instance()->setColorSelector(m_type);
}
Command* CommandFactory::createSetColorSelectorCommand()
{
return new SetColorSelectorCommand;
}
} // namespace app

View File

@ -99,6 +99,7 @@ FOR_EACH_COMMAND(SaveFileCopyAs)
FOR_EACH_COMMAND(SaveMask)
FOR_EACH_COMMAND(SavePalette)
FOR_EACH_COMMAND(Scroll)
FOR_EACH_COMMAND(SetColorSelector)
FOR_EACH_COMMAND(SetLoopSection)
FOR_EACH_COMMAND(SetPalette)
FOR_EACH_COMMAND(SetPaletteEntrySize)

View File

@ -15,6 +15,7 @@
#include "app/tools/ink_type.h"
#include "app/tools/rotation_algorithm.h"
#include "app/tools/selection_mode.h"
#include "app/ui/color_bar.h"
#include "doc/anidir.h"
#include "doc/brush_pattern.h"
#include "doc/documents_observer.h"

View File

@ -30,7 +30,9 @@
#include "app/pref/preferences.h"
#include "app/transaction.h"
#include "app/ui/color_spectrum.h"
#include "app/ui/color_wheel.h"
#include "app/ui/editor/editor.h"
#include "app/ui/hex_color_entry.h"
#include "app/ui/input_chain.h"
#include "app/ui/palette_popup.h"
#include "app/ui/skin/skin_theme.h"
@ -128,9 +130,13 @@ ColorBar* ColorBar::m_instance = NULL;
ColorBar::ColorBar(int align)
: Box(align)
, m_buttons(int(PalButton::MAX))
, m_splitter(Splitter::ByPercentage, VERTICAL)
, m_paletteView(true, PaletteView::FgBgColors, this,
Preferences::instance().colorBar.boxSize() * guiscale())
, m_remapButton("Remap")
, m_selector(ColorSelector::NONE)
, m_spectrum(nullptr)
, m_wheel(nullptr)
, m_fgColor(app::Color::fromRgb(255, 255, 255), IMAGE_RGB)
, m_bgColor(app::Color::fromRgb(0, 0, 0), IMAGE_RGB)
, m_fgWarningIcon(new WarningIcon)
@ -163,24 +169,23 @@ ColorBar::ColorBar(int align)
m_remapButton.setVisible(false);
Box* palViewBox = new VBox();
palViewBox->addChild(&m_scrollableView);
palViewBox->addChild(&m_remapButton);
m_palettePlaceholder.addChild(&m_scrollableView);
m_palettePlaceholder.addChild(&m_remapButton);
m_splitter.setPosition(80);
m_splitter.setExpansive(true);
m_splitter.addChild(&m_palettePlaceholder);
m_splitter.addChild(&m_selectorPlaceholder);
ColorSpectrum* spectrum = new ColorSpectrum;
Splitter* splitter = new Splitter(Splitter::ByPercentage, VERTICAL);
splitter->setPosition(80);
splitter->setExpansive(true);
splitter->addChild(palViewBox);
splitter->addChild(spectrum);
setColorSelector(
Preferences::instance().colorBar.selector());
Box* buttonsBox = new HBox();
buttonsBox->addChild(&m_buttons);
m_buttons.setMaxSize(gfx::Size(m_buttons.maxSize().w,
15*ui::guiscale()));
16*ui::guiscale()));
addChild(buttonsBox);
addChild(splitter);
addChild(&m_splitter);
HBox* fgBox = new HBox;
HBox* bgBox = new HBox;
@ -199,7 +204,6 @@ ColorBar::ColorBar(int align)
m_remapButton.Click.connect(Bind<void>(&ColorBar::onRemapButtonClick, this));
m_fgColor.Change.connect(&ColorBar::onFgColorButtonChange, this);
m_bgColor.Change.connect(&ColorBar::onBgColorButtonChange, this);
spectrum->ColorChange.connect(&ColorBar::onPickSpectrum, this);
m_fgWarningIcon->Click.connect(Bind<void>(&ColorBar::onFixWarningClick, this, &m_fgColor, m_fgWarningIcon));
m_bgWarningIcon->Click.connect(Bind<void>(&ColorBar::onFixWarningClick, this, &m_bgColor, m_bgWarningIcon));
@ -291,6 +295,49 @@ PaletteView* ColorBar::getPaletteView()
return &m_paletteView;
}
ColorBar::ColorSelector ColorBar::getColorSelector()
{
return m_selector;
}
void ColorBar::setColorSelector(ColorSelector selector)
{
if (m_selector == selector)
return;
if (m_spectrum) m_spectrum->setVisible(false);
if (m_wheel) m_wheel->setVisible(false);
m_selector = selector;
Preferences::instance().colorBar.selector(m_selector);
switch (m_selector) {
case ColorSelector::SPECTRUM:
if (!m_spectrum) {
m_spectrum = new ColorSpectrum;
m_spectrum->setExpansive(true);
m_spectrum->ColorChange.connect(&ColorBar::onPickSpectrum, this);
m_selectorPlaceholder.addChild(m_spectrum);
}
m_spectrum->setVisible(true);
break;
case ColorSelector::WHEEL:
if (!m_wheel) {
m_wheel = new ColorWheel;
m_wheel->setExpansive(true);
m_wheel->ColorChange.connect(&ColorBar::onPickSpectrum, this);
m_selectorPlaceholder.addChild(m_wheel);
}
m_wheel->setVisible(true);
break;
}
m_selectorPlaceholder.layout();
}
void ColorBar::setPaletteEditorButtonState(bool state)
{
m_buttons.getItem(int(PalButton::EDIT))->setSelected(state);

View File

@ -22,11 +22,15 @@
#include "doc/sort_palette.h"
#include "ui/box.h"
#include "ui/button.h"
#include "ui/splitter.h"
#include "ui/tooltips.h"
#include "ui/view.h"
namespace app {
class ColorButton;
class ColorSpectrum;
class ColorWheel;
class Command;
class PaletteIndexChangeEvent;
class PalettePopup;
@ -38,6 +42,12 @@ namespace app {
, public app::InputChainElement {
static ColorBar* m_instance;
public:
enum class ColorSelector {
NONE,
SPECTRUM,
WHEEL,
};
static ColorBar* instance() { return m_instance; }
ColorBar(int align);
@ -52,6 +62,9 @@ namespace app {
PaletteView* getPaletteView();
ColorSelector getColorSelector();
void setColorSelector(ColorSelector selector);
// Used by the Palette Editor command to change the status of button
// when the visibility of the dialog changes.
void setPaletteEditorButtonState(bool state);
@ -119,9 +132,15 @@ namespace app {
ui::TooltipManager m_tooltips;
ButtonSet m_buttons;
base::UniquePtr<PalettePopup> m_palettePopup;
ui::Splitter m_splitter;
ui::VBox m_palettePlaceholder;
ui::VBox m_selectorPlaceholder;
ScrollableView m_scrollableView;
PaletteView m_paletteView;
ui::Button m_remapButton;
ColorSelector m_selector;
ColorSpectrum* m_spectrum;
ColorWheel* m_wheel;
ColorButton m_fgColor;
ColorButton m_bgColor;
WarningIcon* m_fgWarningIcon;

View File

@ -31,6 +31,7 @@ ColorSpectrum::ColorSpectrum()
: Widget(kGenericWidget)
{
setAlign(HORIZONTAL);
setBorder(gfx::Border(3*ui::guiscale()));
}
ColorSpectrum::~ColorSpectrum()
@ -39,7 +40,7 @@ ColorSpectrum::~ColorSpectrum()
app::Color ColorSpectrum::pickColor(const gfx::Point& pos) const
{
gfx::Rect rc = getBounds().shrink(3*ui::guiscale());
gfx::Rect rc = getChildrenBounds();
if (rc.isEmpty() || !rc.contains(pos))
return app::Color::fromMask();
@ -87,7 +88,7 @@ void ColorSpectrum::onPaint(ui::PaintEvent& ev)
theme->parts.editorNormal().get(),
getBgColor());
gfx::Rect rc = getClientBounds().shrink(3*ui::guiscale());
gfx::Rect rc = getClientChildrenBounds();
if (rc.isEmpty())
return;
@ -151,7 +152,7 @@ bool ColorSpectrum::onProcessMessage(ui::Message* msg)
case kSetCursorMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
if (getBounds().shrink(3*ui::guiscale()).contains(mouseMsg->position())) {
if (getChildrenBounds().contains(mouseMsg->position())) {
ui::set_mouse_cursor(kEyedropperCursor);
return true;
}

223
src/app/ui/color_wheel.cpp Normal file
View File

@ -0,0 +1,223 @@
// 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.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/ui/color_wheel.h"
#include "app/color_utils.h"
#include "app/pref/preferences.h"
#include "app/ui/skin/button_icon_impl.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h"
#include "base/bind.h"
#include "base/pi.h"
#include "ui/graphics.h"
#include "ui/menu.h"
#include "ui/message.h"
#include "ui/paint_event.h"
#include "ui/preferred_size_event.h"
#include "ui/resize_event.h"
#include "ui/system.h"
namespace app {
using namespace app::skin;
using namespace gfx;
using namespace ui;
ColorWheel::ColorWheel()
: Widget(kGenericWidget)
, m_discrete(Preferences::instance().colorBar.discreteWheel())
, m_options("", kButtonWidget, kButtonWidget, kCheckWidget)
{
SkinTheme* theme = SkinTheme::instance();
setBorder(gfx::Border(3*ui::guiscale()));
m_options.Click.connect(Bind<void>(&ColorWheel::onOptions, this));
m_options.setBgColor(theme->colors.editorFace());
m_options.setIconInterface(
new ButtonIconImpl(theme->parts.palOptions(),
theme->parts.palOptions(),
theme->parts.palOptions(),
CENTER | MIDDLE));
addChild(&m_options);
}
ColorWheel::~ColorWheel()
{
}
app::Color ColorWheel::pickColor(const gfx::Point& pos) const
{
int u = (pos.x - (m_wheelBounds.x+m_wheelBounds.w/2));
int v = (pos.y - (m_wheelBounds.y+m_wheelBounds.h/2));
double d = std::sqrt(u*u + v*v);
if (d < m_wheelRadius) {
double a = std::atan2(-v, u);
int hue = (int(180.0 * a / PI)
+ 180 // To avoid [-180,0) range
+ 180 + 30 // To locate green at 12 o'clock
);
if (m_discrete) {
hue += 15;
hue /= 30;
hue *= 30;
}
hue %= 360; // To leave hue in [0,360) range
int sat = int(120.0 * d / m_wheelRadius);
if (m_discrete) {
sat /= 20;
sat *= 20;
}
return app::Color::fromHsv(
MID(0, hue, 360),
MID(0, sat, 100),
100);
}
else {
return app::Color::fromMask();
}
}
void ColorWheel::setDiscrete(bool state)
{
m_discrete = state;
Preferences::instance().colorBar.discreteWheel(m_discrete);
invalidate();
}
void ColorWheel::onPreferredSize(PreferredSizeEvent& ev)
{
ev.setPreferredSize(gfx::Size(32*ui::guiscale(), 32*ui::guiscale()));
}
void ColorWheel::onResize(ui::ResizeEvent& ev)
{
Widget::onResize(ev);
gfx::Rect rc = getClientChildrenBounds();
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.getPreferredSize();
rc = getChildrenBounds();
rc.x += rc.w-prefSize.w;
rc.w = prefSize.w;
rc.h = prefSize.h;
m_options.setBounds(rc);
}
void ColorWheel::onPaint(ui::PaintEvent& ev)
{
ui::Graphics* g = ev.getGraphics();
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
theme->drawRect(g, getClientBounds(),
theme->parts.editorNormal().get(),
getBgColor());
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::pickColor(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);
}
}
}
bool ColorWheel::onProcessMessage(ui::Message* msg)
{
switch (msg->type()) {
case kMouseDownMessage:
captureMouse();
// Continue...
case kMouseMoveMessage: {
MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
app::Color color = pickColor(
mouseMsg->position()
- getBounds().getOrigin());
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);
app::Color color = pickColor(
mouseMsg->position()
- getBounds().getOrigin());
if (color.getType() != app::Color::MaskType) {
ui::set_mouse_cursor(kEyedropperCursor);
return true;
}
break;
}
}
return Widget::onProcessMessage(msg);
}
void ColorWheel::onOptions()
{
Menu menu;
MenuItem discrete("Discrete");
menu.addChild(&discrete);
if (isDiscrete())
discrete.setSelected(true);
discrete.Click.connect(
[this]() {
setDiscrete(!isDiscrete());
});
gfx::Rect rc = m_options.getBounds();
menu.showPopup(gfx::Point(rc.x+rc.w, rc.y));
}
} // namespace app

49
src/app/ui/color_wheel.h Normal file
View File

@ -0,0 +1,49 @@
// 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_COLOR_WHEEL_H_INCLUDED
#define APP_UI_COLOR_WHEEL_H_INCLUDED
#pragma once
#include "app/color.h"
#include "base/signal.h"
#include "ui/button.h"
#include "ui/mouse_buttons.h"
#include "ui/widget.h"
namespace app {
class ColorWheel : public ui::Widget {
public:
ColorWheel();
~ColorWheel();
app::Color pickColor(const gfx::Point& pos) const;
bool isDiscrete() const { return m_discrete; }
void setDiscrete(bool state);
// Signals
Signal2<void, const app::Color&, ui::MouseButtons> ColorChange;
private:
void onPreferredSize(ui::PreferredSizeEvent& ev) override;
void onResize(ui::ResizeEvent& ev) override;
void onPaint(ui::PaintEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
void onOptions();
gfx::Rect m_clientBounds;
gfx::Rect m_wheelBounds;
int m_wheelRadius;
bool m_discrete;
ui::ButtonBase m_options;
};
} // namespace app
#endif