mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
Add mode selection to color picker popups (RGBA, HSVA, HSLA)
Add buttons for selecting the color mode (RGBA, HSVA, HSLA) in color picker popups in menu and editor. Previously, the color picker popups always showed the color components in HSVA. Now the mode can be switched so the components are shown in either RGBA or HSLA. The hex color code is always RGBA, same as before. In the editor, the RGBA mode is used per default, to prevent any loss of information due to color conversions, as the map format stores RGBA values. In the menus, the HSVA mode is the default, same as before, although the config actually stores HSLA values. The HSVA color picker controls (1D slider for hue, 2D picker for saturation and value) are still shown for all modes. As many color conversions as possible are avoided by only converting between color formats when necessary. Switching between color modes should not change the selected color. Changing the RGBA hex value in the editor will set the exact value also when another color mode is selected. However, changing the color with the other controls in HSVA/HSLA mode will cause a conversion and possible loss of information. The conversion directly from RGBA to HSVA and vice versa is also explicitly avoided, as this internally converts to HSLA first, which is done separately already. Closes #7409.
This commit is contained in:
parent
91e6fca2a5
commit
78217c44d7
|
@ -411,7 +411,9 @@ ColorHSLA CMenus::DoButton_ColorPicker(const CUIRect *pRect, unsigned int *pHsla
|
|||
if(UI()->DoButtonLogic(pHslaColor, 0, pRect))
|
||||
{
|
||||
s_ColorPickerPopupContext.m_pHslaColor = pHslaColor;
|
||||
s_ColorPickerPopupContext.m_HslaColor = HslaColor;
|
||||
s_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(HslaColor);
|
||||
s_ColorPickerPopupContext.m_RgbaColor = color_cast<ColorRGBA>(s_ColorPickerPopupContext.m_HsvaColor);
|
||||
s_ColorPickerPopupContext.m_Alpha = Alpha;
|
||||
UI()->ShowPopupColorPicker(UI()->MouseX(), UI()->MouseY(), &s_ColorPickerPopupContext);
|
||||
}
|
||||
|
|
|
@ -1687,9 +1687,9 @@ CUI::EPopupMenuFunctionResult CUI::PopupColorPicker(void *pContext, CUIRect View
|
|||
SColorPickerPopupContext *pColorPicker = static_cast<SColorPickerPopupContext *>(pContext);
|
||||
CUI *pUI = pColorPicker->m_pUI;
|
||||
|
||||
CUIRect ColorsArea = View, HueArea, BottomArea, HueRect, SatRect, ValueRect, HexRect, AlphaRect;
|
||||
CUIRect ColorsArea, HueArea, BottomArea, ModeButtonArea, HueRect, SatRect, ValueRect, HexRect, AlphaRect;
|
||||
|
||||
ColorsArea.HSplitBottom(View.h - 140.0f, &ColorsArea, &BottomArea);
|
||||
View.HSplitTop(140.0f, &ColorsArea, &BottomArea);
|
||||
ColorsArea.VSplitRight(20.0f, &ColorsArea, &HueArea);
|
||||
|
||||
BottomArea.HSplitTop(3.0f, nullptr, &BottomArea);
|
||||
|
@ -1708,8 +1708,10 @@ CUI::EPopupMenuFunctionResult CUI::PopupColorPicker(void *pContext, CUIRect View
|
|||
ValueRect.VSplitLeft(ValuePadding, nullptr, &ValueRect);
|
||||
|
||||
BottomArea.HSplitTop(20.0f, &HexRect, &BottomArea);
|
||||
BottomArea.HSplitTop(3.0f, nullptr, &BottomArea);
|
||||
HexRect.VSplitLeft(HexValueWidth, &HexRect, &AlphaRect);
|
||||
AlphaRect.VSplitLeft(ValuePadding, nullptr, &AlphaRect);
|
||||
BottomArea.HSplitTop(20.0f, &ModeButtonArea, &BottomArea);
|
||||
|
||||
const ColorRGBA BlackColor = ColorRGBA(0.0f, 0.0f, 0.0f, 0.5f);
|
||||
|
||||
|
@ -1720,10 +1722,8 @@ CUI::EPopupMenuFunctionResult CUI::PopupColorPicker(void *pContext, CUIRect View
|
|||
ColorsArea.Margin(1.0f, &ColorsArea);
|
||||
|
||||
ColorHSVA PickerColorHSV = pColorPicker->m_HsvaColor;
|
||||
unsigned H = (unsigned)(PickerColorHSV.x * 255.0f);
|
||||
unsigned S = (unsigned)(PickerColorHSV.y * 255.0f);
|
||||
unsigned V = (unsigned)(PickerColorHSV.z * 255.0f);
|
||||
unsigned A = (unsigned)(PickerColorHSV.a * 255.0f);
|
||||
ColorRGBA PickerColorRGB = pColorPicker->m_RgbaColor;
|
||||
ColorHSLA PickerColorHSL = pColorPicker->m_HslaColor;
|
||||
|
||||
// Color Area
|
||||
ColorRGBA TL, TR, BL, BR;
|
||||
|
@ -1759,35 +1759,98 @@ CUI::EPopupMenuFunctionResult CUI::PopupColorPicker(void *pContext, CUIRect View
|
|||
HuePartialArea.Draw4(TL, TL, BL, BL, IGraphics::CORNER_NONE, 0.0f);
|
||||
}
|
||||
|
||||
// Editboxes Area
|
||||
H = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[0], &HueRect, "H:", H, 0, 255);
|
||||
S = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[1], &SatRect, "S:", S, 0, 255);
|
||||
V = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[2], &ValueRect, "V:", V, 0, 255);
|
||||
const auto &&RenderAlphaSelector = [&](unsigned OldA) -> unsigned {
|
||||
if(pColorPicker->m_Alpha)
|
||||
{
|
||||
A = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[3], &AlphaRect, "A:", A, 0, 255);
|
||||
return pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[3], &AlphaRect, "A:", OldA, 0, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
char aBuf[8];
|
||||
str_format(aBuf, sizeof(aBuf), "A: %d", A);
|
||||
str_format(aBuf, sizeof(aBuf), "A: %d", OldA);
|
||||
pUI->DoLabel(&AlphaRect, aBuf, 10.0f, TEXTALIGN_MC);
|
||||
AlphaRect.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.65f), IGraphics::CORNER_ALL, 3.0f);
|
||||
return OldA;
|
||||
}
|
||||
};
|
||||
|
||||
// Editboxes Area
|
||||
if(pColorPicker->m_ColorMode == SColorPickerPopupContext::MODE_HSVA)
|
||||
{
|
||||
const unsigned OldH = (unsigned)(PickerColorHSV.h * 255.0f);
|
||||
const unsigned OldS = (unsigned)(PickerColorHSV.s * 255.0f);
|
||||
const unsigned OldV = (unsigned)(PickerColorHSV.v * 255.0f);
|
||||
const unsigned OldA = (unsigned)(PickerColorHSV.a * 255.0f);
|
||||
|
||||
const unsigned H = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[0], &HueRect, "H:", OldH, 0, 255);
|
||||
const unsigned S = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[1], &SatRect, "S:", OldS, 0, 255);
|
||||
const unsigned V = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[2], &ValueRect, "V:", OldV, 0, 255);
|
||||
const unsigned A = RenderAlphaSelector(OldA);
|
||||
|
||||
if(OldH != H || OldS != S || OldV != V || OldA != A)
|
||||
{
|
||||
PickerColorHSV = ColorHSVA(H / 255.0f, S / 255.0f, V / 255.0f, A / 255.0f);
|
||||
PickerColorHSL = color_cast<ColorHSLA>(PickerColorHSV);
|
||||
PickerColorRGB = color_cast<ColorRGBA>(PickerColorHSL);
|
||||
}
|
||||
}
|
||||
else if(pColorPicker->m_ColorMode == SColorPickerPopupContext::MODE_RGBA)
|
||||
{
|
||||
const unsigned OldR = (unsigned)(PickerColorRGB.r * 255.0f);
|
||||
const unsigned OldG = (unsigned)(PickerColorRGB.g * 255.0f);
|
||||
const unsigned OldB = (unsigned)(PickerColorRGB.b * 255.0f);
|
||||
const unsigned OldA = (unsigned)(PickerColorRGB.a * 255.0f);
|
||||
|
||||
const unsigned R = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[0], &HueRect, "R:", OldR, 0, 255);
|
||||
const unsigned G = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[1], &SatRect, "G:", OldG, 0, 255);
|
||||
const unsigned B = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[2], &ValueRect, "B:", OldB, 0, 255);
|
||||
const unsigned A = RenderAlphaSelector(OldA);
|
||||
|
||||
if(OldR != R || OldG != G || OldB != B || OldA != A)
|
||||
{
|
||||
PickerColorRGB = ColorRGBA(R / 255.0f, G / 255.0f, B / 255.0f, A / 255.0f);
|
||||
PickerColorHSL = color_cast<ColorHSLA>(PickerColorRGB);
|
||||
PickerColorHSV = color_cast<ColorHSVA>(PickerColorHSL);
|
||||
}
|
||||
}
|
||||
else if(pColorPicker->m_ColorMode == SColorPickerPopupContext::MODE_HSLA)
|
||||
{
|
||||
const unsigned OldH = (unsigned)(PickerColorHSL.h * 255.0f);
|
||||
const unsigned OldS = (unsigned)(PickerColorHSL.s * 255.0f);
|
||||
const unsigned OldL = (unsigned)(PickerColorHSL.l * 255.0f);
|
||||
const unsigned OldA = (unsigned)(PickerColorHSL.a * 255.0f);
|
||||
|
||||
const unsigned H = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[0], &HueRect, "H:", OldH, 0, 255);
|
||||
const unsigned S = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[1], &SatRect, "S:", OldS, 0, 255);
|
||||
const unsigned L = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[2], &ValueRect, "L:", OldL, 0, 255);
|
||||
const unsigned A = RenderAlphaSelector(OldA);
|
||||
|
||||
if(OldH != H || OldS != S || OldL != L || OldA != A)
|
||||
{
|
||||
PickerColorHSL = ColorHSLA(H / 255.0f, S / 255.0f, L / 255.0f, A / 255.0f);
|
||||
PickerColorHSV = color_cast<ColorHSVA>(PickerColorHSL);
|
||||
PickerColorRGB = color_cast<ColorRGBA>(PickerColorHSL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_assert(false, "Color picker mode invalid");
|
||||
}
|
||||
|
||||
SValueSelectorProperties Props;
|
||||
Props.m_UseScroll = false;
|
||||
Props.m_IsHex = true;
|
||||
Props.m_HexPrefix = pColorPicker->m_Alpha ? 8 : 6;
|
||||
const unsigned Hex = color_cast<ColorRGBA>(PickerColorHSV).PackAlphaLast(pColorPicker->m_Alpha);
|
||||
const unsigned NewHex = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[4], &HexRect, "Hex:", Hex, 0, pColorPicker->m_Alpha ? 0xFFFFFFFFll : 0xFFFFFFll, Props);
|
||||
if(Hex != NewHex)
|
||||
const unsigned OldHex = PickerColorRGB.PackAlphaLast(pColorPicker->m_Alpha);
|
||||
const unsigned Hex = pUI->DoValueSelector(&pColorPicker->m_aValueSelectorIds[4], &HexRect, "Hex:", OldHex, 0, pColorPicker->m_Alpha ? 0xFFFFFFFFll : 0xFFFFFFll, Props);
|
||||
if(OldHex != Hex)
|
||||
{
|
||||
PickerColorHSV = color_cast<ColorHSVA>(ColorRGBA::UnpackAlphaLast<ColorRGBA>(NewHex, pColorPicker->m_Alpha));
|
||||
const float OldAlpha = PickerColorRGB.a;
|
||||
PickerColorRGB = ColorRGBA::UnpackAlphaLast<ColorRGBA>(Hex, pColorPicker->m_Alpha);
|
||||
if(!pColorPicker->m_Alpha)
|
||||
PickerColorHSV.a = A / 255.0f;
|
||||
PickerColorRGB.a = OldAlpha;
|
||||
PickerColorHSL = color_cast<ColorHSLA>(PickerColorRGB);
|
||||
PickerColorHSV = color_cast<ColorHSVA>(PickerColorHSL);
|
||||
}
|
||||
|
||||
// Logic
|
||||
|
@ -1796,10 +1859,16 @@ CUI::EPopupMenuFunctionResult CUI::PopupColorPicker(void *pContext, CUIRect View
|
|||
{
|
||||
PickerColorHSV.y = PickerX / ColorsArea.w;
|
||||
PickerColorHSV.z = 1.0f - PickerY / ColorsArea.h;
|
||||
PickerColorHSL = color_cast<ColorHSLA>(PickerColorHSV);
|
||||
PickerColorRGB = color_cast<ColorRGBA>(PickerColorHSL);
|
||||
}
|
||||
|
||||
if(pUI->DoPickerLogic(&pColorPicker->m_HuePickerId, &HueArea, &PickerX, &PickerY))
|
||||
{
|
||||
PickerColorHSV.x = 1.0f - PickerY / HueArea.h;
|
||||
PickerColorHSL = color_cast<ColorHSLA>(PickerColorHSV);
|
||||
PickerColorRGB = color_cast<ColorRGBA>(PickerColorHSL);
|
||||
}
|
||||
|
||||
// Marker Color Area
|
||||
const float MarkerX = ColorsArea.x + ColorsArea.w * PickerColorHSV.y;
|
||||
|
@ -1812,7 +1881,7 @@ CUI::EPopupMenuFunctionResult CUI::PopupColorPicker(void *pContext, CUIRect View
|
|||
pUI->Graphics()->QuadsBegin();
|
||||
pUI->Graphics()->SetColor(MarkerOutline);
|
||||
pUI->Graphics()->DrawCircle(MarkerX, MarkerY, 4.5f, 32);
|
||||
pUI->Graphics()->SetColor(color_cast<ColorRGBA>(PickerColorHSV));
|
||||
pUI->Graphics()->SetColor(PickerColorRGB);
|
||||
pUI->Graphics()->DrawCircle(MarkerX, MarkerY, 3.5f, 32);
|
||||
pUI->Graphics()->QuadsEnd();
|
||||
|
||||
|
@ -1831,8 +1900,23 @@ CUI::EPopupMenuFunctionResult CUI::PopupColorPicker(void *pContext, CUIRect View
|
|||
HueMarker.Draw(HueMarkerColor, IGraphics::CORNER_ALL, 1.2f);
|
||||
|
||||
pColorPicker->m_HsvaColor = PickerColorHSV;
|
||||
pColorPicker->m_RgbaColor = PickerColorRGB;
|
||||
pColorPicker->m_HslaColor = PickerColorHSL;
|
||||
if(pColorPicker->m_pHslaColor != nullptr)
|
||||
*pColorPicker->m_pHslaColor = color_cast<ColorHSLA>(PickerColorHSV).Pack(pColorPicker->m_Alpha);
|
||||
*pColorPicker->m_pHslaColor = PickerColorHSL.Pack(pColorPicker->m_Alpha);
|
||||
|
||||
static const SColorPickerPopupContext::EColorPickerMode s_aModes[] = {SColorPickerPopupContext::MODE_HSVA, SColorPickerPopupContext::MODE_RGBA, SColorPickerPopupContext::MODE_HSLA};
|
||||
static const char *s_apModeLabels[std::size(s_aModes)] = {"HSVA", "RGBA", "HSLA"};
|
||||
for(SColorPickerPopupContext::EColorPickerMode Mode : s_aModes)
|
||||
{
|
||||
CUIRect ModeButton;
|
||||
ModeButtonArea.VSplitLeft(HsvValueWidth, &ModeButton, &ModeButtonArea);
|
||||
ModeButtonArea.VSplitLeft(ValuePadding, nullptr, &ModeButtonArea);
|
||||
if(pUI->DoButton_PopupMenu(&pColorPicker->m_aModeButtons[(int)Mode], s_apModeLabels[Mode], &ModeButton, 10.0f, TEXTALIGN_MC, 2.0f, false, pColorPicker->m_ColorMode != Mode))
|
||||
{
|
||||
pColorPicker->m_ColorMode = Mode;
|
||||
}
|
||||
}
|
||||
|
||||
return CUI::POPUP_KEEP_OPEN;
|
||||
}
|
||||
|
@ -1840,5 +1924,7 @@ CUI::EPopupMenuFunctionResult CUI::PopupColorPicker(void *pContext, CUIRect View
|
|||
void CUI::ShowPopupColorPicker(float X, float Y, SColorPickerPopupContext *pContext)
|
||||
{
|
||||
pContext->m_pUI = this;
|
||||
DoPopupMenu(pContext, X, Y, 160.0f + 10.0f, 186.0f + 10.0f, pContext, PopupColorPicker);
|
||||
if(pContext->m_ColorMode == SColorPickerPopupContext::MODE_UNSET)
|
||||
pContext->m_ColorMode = SColorPickerPopupContext::MODE_HSVA;
|
||||
DoPopupMenu(pContext, X, Y, 160.0f + 10.0f, 209.0f + 10.0f, pContext, PopupColorPicker);
|
||||
}
|
||||
|
|
|
@ -600,13 +600,26 @@ public:
|
|||
|
||||
struct SColorPickerPopupContext : public SPopupMenuId
|
||||
{
|
||||
enum EColorPickerMode
|
||||
{
|
||||
MODE_UNSET = -1,
|
||||
MODE_HSVA,
|
||||
MODE_RGBA,
|
||||
MODE_HSLA,
|
||||
};
|
||||
|
||||
CUI *m_pUI; // set by CUI when popup is shown
|
||||
EColorPickerMode m_ColorMode = MODE_UNSET;
|
||||
bool m_Alpha = false;
|
||||
unsigned int *m_pHslaColor = nullptr; // may be nullptr
|
||||
ColorHSVA m_HsvaColor;
|
||||
ColorRGBA m_RgbaColor;
|
||||
ColorHSLA m_HslaColor;
|
||||
// UI element IDs
|
||||
const char m_HuePickerId = 0;
|
||||
const char m_ColorPickerId = 0;
|
||||
const char m_aValueSelectorIds[5] = {0};
|
||||
CButtonContainer m_aModeButtons[(int)MODE_HSLA + 1];
|
||||
};
|
||||
void ShowPopupColorPicker(float X, float Y, SColorPickerPopupContext *pContext);
|
||||
|
||||
|
|
|
@ -3133,14 +3133,17 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int *
|
|||
}
|
||||
else if(ButtonResult > 0)
|
||||
{
|
||||
s_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(ColorPick);
|
||||
if(s_ColorPickerPopupContext.m_ColorMode == CUI::SColorPickerPopupContext::MODE_UNSET)
|
||||
s_ColorPickerPopupContext.m_ColorMode = CUI::SColorPickerPopupContext::MODE_RGBA;
|
||||
s_ColorPickerPopupContext.m_RgbaColor = ColorPick;
|
||||
s_ColorPickerPopupContext.m_HslaColor = color_cast<ColorHSLA>(ColorPick);
|
||||
s_ColorPickerPopupContext.m_HsvaColor = color_cast<ColorHSVA>(s_ColorPickerPopupContext.m_HslaColor);
|
||||
s_ColorPickerPopupContext.m_Alpha = true;
|
||||
UI()->ShowPopupColorPicker(UI()->MouseX(), UI()->MouseY(), &s_ColorPickerPopupContext);
|
||||
}
|
||||
else if(UI()->IsPopupOpen(&s_ColorPickerPopupContext))
|
||||
{
|
||||
ColorRGBA c = color_cast<ColorRGBA>(s_ColorPickerPopupContext.m_HsvaColor);
|
||||
const int NewColor = c.PackAlphaLast(s_ColorPickerPopupContext.m_Alpha);
|
||||
const int NewColor = s_ColorPickerPopupContext.m_RgbaColor.PackAlphaLast(s_ColorPickerPopupContext.m_Alpha);
|
||||
if(NewColor != pProps[i].m_Value)
|
||||
{
|
||||
*pNewVal = NewColor;
|
||||
|
|
Loading…
Reference in a new issue