summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCallum Prentice <callum@lindenlab.com>2026-06-03 15:36:38 -0700
committerCallum Prentice <callum@lindenlab.com>2026-06-03 15:36:38 -0700
commitfe425958e42360f2258fdde596e062471ecde29b (patch)
tree40d2857f9c130ffd8451c15026659230795151b9
parent3e4502cbfa5b9b1b749ea52ca0168f90ee7a5e98 (diff)
Some minor tweaks to the color picker after interest was expressed in a CCUG and a Canny was generated - details here: https://github.com/secondlife/viewer/issues/5400
-rw-r--r--indra/newview/llfloatercolorpicker.cpp162
-rw-r--r--indra/newview/llfloatercolorpicker.h8
-rw-r--r--indra/newview/skins/default/xui/en/floater_color_picker.xml86
3 files changed, 241 insertions, 15 deletions
diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp
index 56e086502a..520206fd5c 100644
--- a/indra/newview/llfloatercolorpicker.cpp
+++ b/indra/newview/llfloatercolorpicker.cpp
@@ -32,6 +32,7 @@
#include "lltoolmgr.h"
#include "lltoolpipette.h"
#include "llviewercontrol.h"
+#include "llviewerwindow.h"
#include "llworld.h"
// Linden library includes
@@ -45,6 +46,7 @@
#include "lllineeditor.h"
#include "v4coloru.h"
#include "llbutton.h"
+#include "llcombobox.h"
#include "lluictrlfactory.h"
#include "llgl.h"
#include "llpointer.h"
@@ -76,7 +78,7 @@ LLFloaterColorPicker::LLFloaterColorPicker (LLColorSwatchCtrl* swatch, bool show
mMouseDownInSwatch ( false ),
// *TODO: Specify this in XML
mRGBViewerImageLeft ( 140 ),
- mRGBViewerImageTop ( 356 ),
+ mRGBViewerImageTop ( 436 ),
mRGBViewerImageWidth ( 256 ),
mRGBViewerImageHeight ( 256 ),
mLumRegionLeft ( mRGBViewerImageLeft + mRGBViewerImageWidth + 16 ),
@@ -86,18 +88,18 @@ LLFloaterColorPicker::LLFloaterColorPicker (LLColorSwatchCtrl* swatch, bool show
mLumMarkerSize ( 6 ),
// *TODO: Specify this in XML
mSwatchRegionLeft ( 12 ),
- mSwatchRegionTop ( 190 ),
+ mSwatchRegionTop ( 249 ),
mSwatchRegionWidth ( 116 ),
mSwatchRegionHeight ( 60 ),
mSwatchView ( NULL ),
// *TODO: Specify this in XML
numPaletteColumns ( 16 ),
- numPaletteRows ( 2 ),
+ numPaletteRows ( 4 ),
highlightEntry ( -1 ),
mPaletteRegionLeft ( 11 ),
- mPaletteRegionTop ( 100 - 8 ),
+ mPaletteRegionTop ( 180 - 8 ),
mPaletteRegionWidth ( mLumRegionLeft + mLumRegionWidth - 10 ),
- mPaletteRegionHeight ( 40 ),
+ mPaletteRegionHeight ( 120 ),
mSwatch ( swatch ),
mActive ( true ),
mCanApplyImmediately ( show_apply_immediate ),
@@ -154,7 +156,7 @@ void LLFloaterColorPicker::createUI ()
// create palette
for ( S32 each = 0; each < numPaletteColumns * numPaletteRows; ++each )
{
- mPalette.push_back(new LLColor4(LLUIColorTable::instance().getColor(llformat("ColorPaletteEntry%02d", each + 1))));
+ mPalette.push_back(new LLColor4(LLUIColorTable::instance().getColor(llformat("ColorPaletteEntry%02d", each + 1), LLColor4::grey)));
}
}
@@ -232,6 +234,7 @@ bool LLFloaterColorPicker::postBuild()
childSetCommitCallback("hspin", onTextCommit, (void*)this );
childSetCommitCallback("sspin", onTextCommit, (void*)this );
childSetCommitCallback("lspin", onTextCommit, (void*)this );
+ childSetCommitCallback("hex_color", onTextCommit, (void*)this );
mPipetteConnection = LLToolPipette::getInstance()->setToolSelectCallback(
[this](LLPointer<LLViewerObject> object, S32 te_index)
@@ -243,6 +246,9 @@ bool LLFloaterColorPicker::postBuild()
}
});
+ mCopyColorAsCombo = getChild<LLComboBox>("copy_color_combobox");
+ mCopyColorAsCombo->setCommitCallback(boost::bind(&LLFloaterColorPicker::onCopyColor, this));
+
return true;
}
@@ -678,6 +684,61 @@ void LLFloaterColorPicker::drawPalette ()
}
//////////////////////////////////////////////////////////////////////////////
+// Convert input RGB (0..1.0) to a hex color string
+std::string rgbFloatToHex(float r, float g, float b)
+{
+ auto toUint8 = [](float val) -> unsigned char {
+ return static_cast<unsigned char>(std::round(std::clamp(val * 255.0f, 0.0f, 255.0f)));
+ };
+
+ std::ostringstream oss;
+ oss << std::hex << std::uppercase << std::setfill('0');
+ oss << std::setw(2) << static_cast<int>(toUint8(r));
+ oss << std::setw(2) << static_cast<int>(toUint8(g));
+ oss << std::setw(2) << static_cast<int>(toUint8(b));
+
+ return oss.str();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Boolean test if input string is a valid hex color string
+bool LLFloaterColorPicker::isValidHexColor(std::string& hex_color, F32& hr, F32& hg, F32& hb)
+{
+ // Strip any whitespace and a leading # character if present
+ // (Often included in hex color strings from other places)
+ LLStringUtil::trim(hex_color);
+ if (!hex_color.empty() && hex_color.front() == '#')
+ {
+ hex_color = hex_color.substr(1);
+ }
+
+ // Make sure it's a real string and valid hex - we can't use the
+ // hex to dec code because that coerces invalid hex into decimal 0
+ if (hex_color.length() ==0 || hex_color.find_first_not_of("0123456789abcdefABCDEF") != std::string::npos)
+ {
+ return false;
+ }
+
+ // Convert the hex string to a decimal number
+ std::stringstream oss;
+ oss << std::hex << hex_color;
+ unsigned int dec_val;
+ oss >> dec_val;
+
+ // Break out the RGB values in 0..1.0 range and pass back
+ // (They will only be used if this function returns true.)
+ hr = F32(((dec_val >> 16) & 0xff)) / 255.0f;
+ hg = F32(((dec_val >> 8) & 0xff)) / 255.0f;
+ hb = F32(((dec_val >> 0) & 0xff)) / 255.0f;
+
+ // The max chars in the field is more than 6 now so we can paste in
+ // poorly formatted color strings and reformat them on commit.
+ // TODO: there must be a way to to the preformat when the string
+ // is pasted into the line editor control.
+ return dec_val > 0xffffff ? false : true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
// update text entry values for RGB/HSL (can't be done in ::draw () since this overwrites input
void LLFloaterColorPicker::updateTextEntry ()
{
@@ -688,6 +749,8 @@ void LLFloaterColorPicker::updateTextEntry ()
getChild<LLUICtrl>("hspin")->setValue(( getCurH () * 360.0f ) );
getChild<LLUICtrl>("sspin")->setValue(( getCurS () * 100.0f ) );
getChild<LLUICtrl>("lspin")->setValue(( getCurL () * 100.0f ) );
+
+ getChild<LLUICtrl>("hex_color")->setValue(rgbFloatToHex(curR, curG, curB));
}
//////////////////////////////////////////////////////////////////////////////
@@ -745,6 +808,22 @@ void LLFloaterColorPicker::onTextEntryChanged ( LLUICtrl* ctrl )
selectCurHsl ( hVal, sVal, lVal );
updateTextEntry ();
+ } else
+ if (name == "hex_color")
+ {
+ F32 rVal, gVal, bVal;
+ std::string hex = ctrl->getValue().asString();
+ if (isValidHexColor(hex, rVal, gVal, bVal))
+ {
+ // Valid hex color - update the picker with specified value
+ selectCurRgb ( rVal, gVal, bVal );
+ updateTextEntry ();
+ }
+ else
+ {
+ // Invalid hex color provided - update the hex color field with current color
+ updateTextEntry ();
+ }
}
}
@@ -1096,3 +1175,74 @@ void LLFloaterColorPicker::stopUsingPipette()
LLToolMgr::getInstance()->clearTransientTool();
}
}
+
+void LLFloaterColorPicker::onCopyColor()
+{
+ LLComboBox* combo = getChild<LLComboBox>("copy_color_combobox");
+ S32 combo_val = combo->getSelectedValue().asInteger();
+
+ std::ostringstream to_copy;
+
+ // LSL - e.g. <0.571, 0.929, 0.585>
+ if (combo_val == 1)
+ {
+ to_copy << "<";
+ to_copy << std::setfill('0') << std::setprecision(3) << ((getCurR() < 0.001) ? 0.0 : getCurR());
+ to_copy << ", ";
+ to_copy << std::setfill('0') << std::setprecision(3) << ((getCurG() < 0.001) ? 0.0 : getCurG());
+ to_copy << ", ";
+ to_copy << std::setfill('0') << std::setprecision(3) << ((getCurB() < 0.001) ? 0.0 : getCurB());
+ to_copy << ">";
+ } else
+ // HEX e.g. #92ED95
+ if (combo_val == 2)
+ {
+ to_copy << "#";
+ to_copy << rgbFloatToHex(getCurR(), getCurG(), getCurB());
+ } else
+ // RGB e.g. (146, 237, 149)
+ if (combo_val == 3)
+ {
+ to_copy << "(";
+ to_copy << (S32)(getCurR() * 255.0f + 0.5f);
+ to_copy << ", ";
+ to_copy << (S32)(getCurG() * 255.0f + 0.5f);
+ to_copy << ", ";
+ to_copy << (S32)(getCurB() * 255.0f + 0.5f);
+ to_copy << ")";
+ } else
+ // HSL e.g. (122°, 71%, 75%)
+ if (combo_val == 4)
+ {
+ to_copy << "(";
+ to_copy << (S32)(getCurH() * 360.0f + 0.5f);
+ to_copy << "\xC2\xB0, ";
+ to_copy << (S32)(getCurS() * 100.0f + 0.5f);
+ to_copy << "%, ";
+ to_copy << (S32)(getCurL() * 100.0f + 0.5f);
+ to_copy << "%)";
+ }
+ // CMYK e.g. (38%, 0%, 37%, 7%)
+ if (combo_val == 5)
+ {
+ F32 k = 1.0f - std::max({getCurR(), getCurG(), getCurB()});
+ F32 c = (k == 1.0f) ? 0.0f : (1.0f - getCurR() - k) / (1.0f - k);
+ F32 m = (k == 1.0f) ? 0.0f : (1.0f - getCurG() - k) / (1.0f - k);
+ F32 y = (k == 1.0f) ? 0.0f : (1.0f - getCurB() - k) / (1.0f - k);
+ to_copy << "(";
+ to_copy << (S32)(c * 100.0f + 0.5f);
+ to_copy << "%, ";
+ to_copy << (S32)(m * 100.0f + 0.5f);
+ to_copy << "%, ";
+ to_copy << (S32)(y * 100.0f + 0.5f);
+ to_copy << "%, ";
+ to_copy << (S32)(k * 100.0f + 0.5f);
+ to_copy << "%";
+ to_copy << ")";
+ }
+
+ if (! to_copy.str().empty())
+ {
+ gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(to_copy.str()));
+ }
+}
diff --git a/indra/newview/llfloatercolorpicker.h b/indra/newview/llfloatercolorpicker.h
index af199d92a3..93f67f40ea 100644
--- a/indra/newview/llfloatercolorpicker.h
+++ b/indra/newview/llfloatercolorpicker.h
@@ -37,6 +37,7 @@
class LLButton;
class LLLineEditor;
class LLCheckBoxCtrl;
+class LLComboBox;
//////////////////////////////////////////////////////////////////////////////
// floater class
@@ -127,6 +128,10 @@ class LLFloaterColorPicker
// mutators for color values, can raise event to preview changes at object
void selectCurRgb ( F32 curRIn, F32 curGIn, F32 curBIn );
void selectCurHsl ( F32 curHIn, F32 curSIn, F32 curLIn );
+
+ // utility functions for manipulating hex colors
+ bool isValidHexColor(std::string& hex_color, F32& hr, F32& hg, F32& hb);
+
// draws color selection palette
void drawPalette ();
@@ -192,6 +197,9 @@ class LLFloaterColorPicker
LLButton* mPipetteBtn;
+ LLComboBox* mCopyColorAsCombo;
+ void onCopyColor();
+
F32 mContextConeOpacity;
F32 mContextConeInAlpha;
F32 mContextConeOutAlpha;
diff --git a/indra/newview/skins/default/xui/en/floater_color_picker.xml b/indra/newview/skins/default/xui/en/floater_color_picker.xml
index 397b66fbd2..d937860f53 100644
--- a/indra/newview/skins/default/xui/en/floater_color_picker.xml
+++ b/indra/newview/skins/default/xui/en/floater_color_picker.xml
@@ -2,7 +2,7 @@
<floater
legacy_header_height="18"
can_minimize="false"
- height="380"
+ height="460"
layout="topleft"
name="ColorPicker"
help_topic="colorpicker"
@@ -153,6 +153,41 @@
name="lspin"
top_delta="0"
width="50" />
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ top_pad="6"
+ left="10"
+ mouse_opaque="false"
+ name="hex_text"
+ width="413">
+ Hex:
+ </text>
+ <text
+ type="string"
+ length="1"
+ follows="left|top"
+ height="20"
+ layout="topleft"
+ left="60"
+ name="hash_text"
+ top_pad="-20"
+ width="20">
+ #
+ </text>
+ <line_editor
+ font="SansSerif"
+ max_length_bytes="16"
+ top_pad="-22"
+ follows="left"
+ height="20"
+ layout="topleft"
+ left_delta="13"
+ name="hex_color"
+ width="50" />
<check_box
follows="left|bottom"
height="20"
@@ -160,7 +195,7 @@
layout="topleft"
left="10"
name="apply_immediate"
- top_pad="185"
+ top_pad="240"
width="100" />
<button
follows="left|bottom"
@@ -168,19 +203,52 @@
image_selected="eye_button_active.tga"
image_unselected="eye_button_inactive.tga"
layout="topleft"
- left_pad="50"
+ left_pad="0"
name="color_pipette"
width="28" />
+ <combo_box
+ control_name="CopyColorDestination"
+ follows="right|bottom"
+ layout="topleft"
+ right="-197"
+ top_delta="8"
+ name="copy_color_combobox"
+ width="84">
+ <combo_box.item
+ label="Copy as..."
+ name="copy_as"
+ value="0" />
+ <combo_box.item
+ label="LSL"
+ name="lsl_color"
+ value="1" />
+ <combo_box.item
+ label="HEX"
+ name="hex_color"
+ value="2" />
+ <combo_box.item
+ label="RGB"
+ name="rgb_color"
+ value="3" />
+ <combo_box.item
+ label="HSL"
+ name="hsl_color"
+ value="4" />
+ <combo_box.item
+ label="CMYK"
+ name="cmyk_color"
+ value="5" />
+ </combo_box>
<button
follows="right|bottom"
height="23"
label="OK"
label_selected="OK"
layout="topleft"
- right="-120"
- top_delta="9"
+ right="-100"
+ top_delta="0"
name="select_btn"
- width="100" />
+ width="80" />
<button
follows="right|bottom"
height="23"
@@ -190,7 +258,7 @@
left_pad="5"
right="-10"
name="cancel_btn"
- width="100" />
+ width="80" />
<text
type="string"
length="1"
@@ -199,7 +267,7 @@
layout="topleft"
left="10"
name="Current color:"
- top="172"
+ top="194"
width="110">
Current color:
</text>
@@ -211,7 +279,7 @@
layout="topleft"
left="10"
name="(Drag below to save.)"
- top_pad="66"
+ top_pad="62"
width="130"
wrap="true">
(Drag below to save)