tge/engine/gui/controls/guiColorPicker.cc
2017-04-17 06:17:10 -06:00

465 lines
15 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "console/console.h"
#include "dgl/dgl.h"
#include "console/consoleTypes.h"
#include "platform/platformAudio.h"
#include "gui/core/guiCanvas.h"
#include "gui/controls/guiButtonCtrl.h"
#include "gui/core/guiDefaultControlRender.h"
#include "gui/controls/guiColorPicker.h"
/// @name Common colors we use
/// @{
ColorF colorWhite(1.,1.,1.);
ColorF colorWhiteBlend(1.,1.,1.,.75);
ColorF colorBlack(.0,.0,.0);
ColorF colorAlpha(0.0f, 0.0f, 0.0f, 0.0f);
ColorI GuiColorPickerCtrl::mColorRange[9] = {
ColorI(255,255,255), // White
ColorI(255,255,255), // White
ColorI(255,0,255), // Pink
ColorI(0,0,255), // Blue
ColorI(0,255,255), // Light blue
ColorI(0,255,0), // Green
ColorI(255,255,0), // Yellow
ColorI(255,0,0), // Red
ColorI(0,0,0) // Black
};
/// @}
IMPLEMENT_CONOBJECT(GuiColorPickerCtrl);
//--------------------------------------------------------------------------
GuiColorPickerCtrl::GuiColorPickerCtrl()
{
mBounds.extent.set(140, 30);
mDisplayMode = pPallet;
mBaseColor = ColorF(1.,.0,1.);
mPickColor = ColorF(.0,.0,.0);
mSelectorPos = Point2I(0,0);
mMouseDown = mMouseOver = false;
mActive = true;
mPositionChanged = false;
mSelectorGap = 1;
mActionOnMove = false;
}
//--------------------------------------------------------------------------
static EnumTable::Enums gColorPickerModeEnums[] =
{
{ GuiColorPickerCtrl::pPallet, "Pallet" },
{ GuiColorPickerCtrl::pHorizColorRange, "HorizColor"},
{ GuiColorPickerCtrl::pVertColorRange, "VertColor" },
{ GuiColorPickerCtrl::pHorizColorBrightnessRange, "HorizBrightnessColor"},
{ GuiColorPickerCtrl::pVertColorBrightnessRange, "VertBrightnessColor" },
{ GuiColorPickerCtrl::pBlendColorRange, "BlendColor"},
{ GuiColorPickerCtrl::pHorizAlphaRange, "HorizAlpha"},
{ GuiColorPickerCtrl::pVertAlphaRange, "VertAlpha" },
{ GuiColorPickerCtrl::pDropperBackground, "Dropper" },
};
static EnumTable gColorPickerModeTable( 9, gColorPickerModeEnums );
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("ColorPicker");
addField("BaseColor", TypeColorF, Offset(mBaseColor, GuiColorPickerCtrl));
addField("PickColor", TypeColorF, Offset(mPickColor, GuiColorPickerCtrl));
addField("SelectorGap", TypeS32, Offset(mSelectorGap, GuiColorPickerCtrl));
addField("DisplayMode", TypeEnum, Offset(mDisplayMode, GuiColorPickerCtrl), 1, &gColorPickerModeTable );
addField("ActionOnMove", TypeBool,Offset(mActionOnMove, GuiColorPickerCtrl));
endGroup("ColorPicker");
}
//--------------------------------------------------------------------------
// Function to draw a box which can have 4 different colors in each corner blended together
void dglDrawBlendBox(RectI &bounds, ColorF &c1, ColorF &c2, ColorF &c3, ColorF &c4)
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
// Make sure the top row of pixels are the right color.
glColor4f(c1.red, c1.green, c1.blue, c1.alpha);
glVertex2f(l, t);
glColor4f(c2.red, c2.green, c2.blue, c2.alpha);
glVertex2f(r, t);
glColor4f(c2.red, c2.green, c2.blue, c2.alpha);
glVertex2f(r, t + 1);
glColor4f(c1.red, c1.green, c1.blue, c1.alpha);
glVertex2f(l, t + 1);
// The main blend box.
glColor4f(c1.red, c1.green, c1.blue, c1.alpha);
glVertex2f(l, t + 1);
glColor4f(c2.red, c2.green, c2.blue, c2.alpha);
glVertex2f(r, t + 1);
glColor4f(c3.red, c3.green, c3.blue, c3.alpha);
glVertex2f(r, b - 1);
glColor4f(c4.red, c4.green, c4.blue, c4.alpha);
glVertex2f(l, b - 1);
// Make sure the bottom row of pixels are the right color.
glColor4f(c4.red, c4.green, c4.blue, c4.alpha);
glVertex2f(l, b - 1);
glColor4f(c3.red, c3.green, c3.blue, c3.alpha);
glVertex2f(r, b - 1);
glColor4f(c3.red, c3.green, c3.blue, c3.alpha);
glVertex2f(r, b);
glColor4f(c4.red, c4.green, c4.blue, c4.alpha);
glVertex2f(l, b);
glEnd();
}
//--------------------------------------------------------------------------
/// Function to draw a set of boxes blending throughout an array of colors
void dglDrawBlendRangeBox(RectI &bounds, bool vertical, U8 numColors, ColorI *colors)
{
S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1;
S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1;
// Calculate increment value
S32 x_inc = int(mFloor((r - l) / (numColors-1)));
S32 y_inc = int(mFloor((b - t) / (numColors-1)));
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
for (U16 i=0;i<numColors-1;i++)
{
// If we are at the end, x_inc and y_inc need to go to the end (otherwise there is a rendering bug)
if (i == numColors-2)
{
x_inc += r-l-1;
y_inc += b-t-1;
}
if (!vertical) // Horizontal (+x)
{
// First color
glColor4ub(colors[i].red, colors[i].green, colors[i].blue, colors[i].alpha);
glVertex2f(l, t);
glVertex2f(l, b);
// Second color
glColor4ub(colors[i+1].red, colors[i+1].green, colors[i+1].blue, colors[i+1].alpha);
glVertex2f(l+x_inc, b);
glVertex2f(l+x_inc, t);
l += x_inc;
}
else // Vertical (+y)
{
// First color
glColor4ub(colors[i].red, colors[i].green, colors[i].blue, colors[i].alpha);
glVertex2f(l, t);
glVertex2f(r, t);
// Second color
glColor4ub(colors[i+1].red, colors[i+1].green, colors[i+1].blue, colors[i+1].alpha);
glVertex2f(r, t+y_inc);
glVertex2f(l, t+y_inc);
t += y_inc;
}
}
glEnd();
}
void GuiColorPickerCtrl::drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode)
{
U16 sMax = mSelectorGap*2;
switch (mode)
{
case sVertical:
// Now draw the vertical selector
// Up -> Pos
if (selectorPos.y != bounds.point.y+1)
dglDrawLine(selectorPos.x, bounds.point.y, selectorPos.x, selectorPos.y-sMax-1, colorWhiteBlend);
// Down -> Pos
if (selectorPos.y != bounds.point.y+bounds.extent.y)
dglDrawLine(selectorPos.x, selectorPos.y + sMax, selectorPos.x, bounds.point.y + bounds.extent.y, colorWhiteBlend);
break;
case sHorizontal:
// Now draw the horizontal selector
// Left -> Pos
if (selectorPos.x != bounds.point.x)
dglDrawLine(bounds.point.x, selectorPos.y-1, selectorPos.x-sMax, selectorPos.y-1, colorWhiteBlend);
// Right -> Pos
if (selectorPos.x != bounds.point.x)
dglDrawLine(bounds.point.x+mSelectorPos.x+sMax, selectorPos.y-1, bounds.point.x + bounds.extent.x, selectorPos.y-1, colorWhiteBlend);
break;
}
}
//--------------------------------------------------------------------------
/// Function to invoke calls to draw the picker box and selector
void GuiColorPickerCtrl::renderColorBox(RectI &bounds)
{
RectI pickerBounds;
pickerBounds.point.x = bounds.point.x+1;
pickerBounds.point.y = bounds.point.y+1;
pickerBounds.extent.x = bounds.extent.x-1;
pickerBounds.extent.y = bounds.extent.y-1;
if (mProfile->mBorder)
dglDrawRect(bounds, mProfile->mBorderColor);
Point2I selectorPos = Point2I(bounds.point.x+mSelectorPos.x+1, bounds.point.y+mSelectorPos.y+1);
// Draw color box differently depending on mode
RectI blendRect;
switch (mDisplayMode)
{
case pHorizColorRange:
dglDrawBlendRangeBox( pickerBounds, false, 7, mColorRange + 2);
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pVertColorRange:
dglDrawBlendRangeBox( pickerBounds, true, 7, mColorRange + 2);
drawSelector( pickerBounds, selectorPos, sHorizontal );
break;
case pHorizColorBrightnessRange:
blendRect = pickerBounds;
blendRect.point.y++;
blendRect.extent.y -= 2;
dglDrawBlendRangeBox( pickerBounds, false, 9, mColorRange);
// This is being drawn slightly offset from the larger rect so as to insure 255 and 0
// can both be selected for every color.
dglDrawBlendBox( blendRect, colorAlpha, colorAlpha, colorBlack, colorBlack );
blendRect.point.y += blendRect.extent.y - 1;
blendRect.extent.y = 2;
dglDrawRect( blendRect, colorBlack);
drawSelector( pickerBounds, selectorPos, sHorizontal );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pVertColorBrightnessRange:
dglDrawBlendRangeBox( pickerBounds, true, 9, mColorRange);
dglDrawBlendBox( pickerBounds, colorAlpha, colorBlack, colorBlack, colorAlpha );
drawSelector( pickerBounds, selectorPos, sHorizontal );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pHorizAlphaRange:
dglDrawBlendBox( pickerBounds, colorBlack, colorWhite, colorWhite, colorBlack );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pVertAlphaRange:
dglDrawBlendBox( pickerBounds, colorBlack, colorBlack, colorWhite, colorWhite );
drawSelector( pickerBounds, selectorPos, sHorizontal );
break;
case pBlendColorRange:
dglDrawBlendBox( pickerBounds, colorWhite, mBaseColor, colorBlack, colorBlack );
drawSelector( pickerBounds, selectorPos, sHorizontal );
drawSelector( pickerBounds, selectorPos, sVertical );
break;
case pDropperBackground:
break;
case pPallet:
default:
dglDrawRectFill( pickerBounds, mBaseColor );
break;
}
}
void GuiColorPickerCtrl::onRender(Point2I offset, const RectI& updateRect)
{
RectI boundsRect(offset, mBounds.extent);
renderColorBox(boundsRect);
if (mPositionChanged)
{
mPositionChanged = false;
Point2I extent = Canvas->getExtent();
// If we are anything but a pallete, change the pick color
if (mDisplayMode != pPallet)
{
// To simplify things a bit, just read the color via glReadPixels()
GLfloat rBuffer[4];
glReadBuffer(GL_BACK);
U32 buf_x = offset.x+mSelectorPos.x+1;
U32 buf_y = extent.y-(offset.y+mSelectorPos.y+1);
glReadPixels(buf_x, buf_y, 1, 1, GL_RGBA, GL_FLOAT, rBuffer);
mPickColor.red = rBuffer[0];
mPickColor.green = rBuffer[1];
mPickColor.blue = rBuffer[2];
mPickColor.alpha = rBuffer[3];
// Now do onAction() if we are allowed
if (mActionOnMove)
onAction();
}
}
//render the children
renderChildControls( offset, updateRect);
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::setSelectorPos(const Point2I &pos)
{
Point2I extent = mBounds.extent;
RectI rect;
if (mDisplayMode != pDropperBackground)
{
extent.x -= 3;
extent.y -= 2;
rect = RectI(Point2I(1,1), extent);
}
else
{
rect = RectI(Point2I(0,0), extent);
}
if (rect.pointInRect(pos))
{
mSelectorPos = pos;
mPositionChanged = true;
// We now need to update
setUpdate();
}
else
{
if ((pos.x > rect.point.x) && (pos.x < (rect.point.x + rect.extent.x)))
mSelectorPos.x = pos.x;
else if (pos.x <= rect.point.x)
mSelectorPos.x = rect.point.x;
else if (pos.x >= (rect.point.x + rect.extent.x))
mSelectorPos.x = rect.point.x + rect.extent.x - 1;
if ((pos.y > rect.point.y) && (pos.y < (rect.point.y + rect.extent.y)))
mSelectorPos.y = pos.y;
else if (pos.y <= rect.point.y)
mSelectorPos.y = rect.point.y;
else if (pos.y >= (rect.point.y + rect.extent.y))
mSelectorPos.y = rect.point.y + rect.extent.y - 1;
mPositionChanged = true;
setUpdate();
}
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseDown(const GuiEvent &event)
{
if (!mActive)
return;
if (mDisplayMode == pDropperBackground)
return;
mouseLock(this);
if (mProfile->mCanKeyFocus)
setFirstResponder();
// Update the picker cross position
if (mDisplayMode != pPallet)
setSelectorPos(globalToLocalCoord(event.mousePoint));
mMouseDown = true;
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseDragged(const GuiEvent &event)
{
if ((mActive && mMouseDown) || (mActive && (mDisplayMode == pDropperBackground)))
{
// Update the picker cross position
if (mDisplayMode != pPallet)
setSelectorPos(globalToLocalCoord(event.mousePoint));
}
if (!mActionOnMove && mAltConsoleCommand[0] )
Con::evaluate( mAltConsoleCommand, false );
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseMove(const GuiEvent &event)
{
// Only for dropper mode
if (mActive && (mDisplayMode == pDropperBackground))
setSelectorPos(globalToLocalCoord(event.mousePoint));
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseEnter(const GuiEvent &event)
{
mMouseOver = true;
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseLeave(const GuiEvent &)
{
// Reset state
mMouseOver = false;
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::onMouseUp(const GuiEvent &)
{
//if we released the mouse within this control, perform the action
if (mActive && mMouseDown && (mDisplayMode != pDropperBackground))
{
onAction();
mMouseDown = false;
}
else if (mActive && (mDisplayMode == pDropperBackground))
{
// In a dropper, the alt command executes the mouse up action (to signal stopping)
if ( mAltConsoleCommand[0] )
Con::evaluate( mAltConsoleCommand, false );
}
mouseUnlock();
}
//--------------------------------------------------------------------------
const char *GuiColorPickerCtrl::getScriptValue()
{
static char temp[256];
ColorF color = getValue();
dSprintf(temp,256,"%f %f %f %f",color.red, color.green, color.blue, color.alpha);
return temp;
}
//--------------------------------------------------------------------------
void GuiColorPickerCtrl::setScriptValue(const char *value)
{
ColorF newValue;
dSscanf(value, "%f %f %f %f", &newValue.red, &newValue.green, &newValue.blue, &newValue.alpha);
setValue(newValue);
}
ConsoleMethod(GuiColorPickerCtrl, getSelectorPos, const char*, 2, 2, "Gets the current position of the selector")
{
char *temp = Con::getReturnBuffer(256);
Point2I pos;
pos = object->getSelectorPos();
dSprintf(temp,256,"%d %d",pos.x, pos.y);
return temp;
}
ConsoleMethod(GuiColorPickerCtrl, setSelectorPos, void, 3, 3, "Sets the current position of the selector")
{
Point2I newPos;
dSscanf(argv[2], "%d %d", &newPos.x, &newPos.y);
object->setSelectorPos(newPos);
}
ConsoleMethod(GuiColorPickerCtrl, updateColor, void, 2, 2, "Forces update of pick color")
{
object->updateColor();
}