llfloatercolorpicker.cpp 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058
  1. /**
  2. * @file llfloatercolorpicker.cpp
  3. * @brief Generic system color picker
  4. *
  5. * $LicenseInfo:firstyear=2004&license=viewergpl$
  6. *
  7. * Copyright(c) 2004-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "llviewerprecompiledheaders.h"
  33. #include <sstream>
  34. #include <iomanip>
  35. #include "llfloatercolorpicker.h"
  36. #include "llbutton.h"
  37. #include "llcheckboxctrl.h"
  38. #include "lldraghandle.h"
  39. #include "llgl.h"
  40. #include "llimage.h"
  41. #include "llimagegl.h"
  42. #include "lllineeditor.h"
  43. #include "llmousehandler.h"
  44. #include "llrender.h"
  45. #include "lltextbox.h"
  46. #include "lluictrlfactory.h"
  47. #include "llwindow.h"
  48. #include "lltoolmgr.h"
  49. #include "lltoolpipette.h"
  50. #include "llviewercontrol.h"
  51. constexpr F32 CONTEXT_CONE_IN_ALPHA = 0.f;
  52. constexpr F32 CONTEXT_CONE_OUT_ALPHA = 1.f;
  53. constexpr F32 CONTEXT_FADE_TIME = 0.08f;
  54. LLFloaterColorPicker::LLFloaterColorPicker(LLColorSwatchCtrl* swatch,
  55. bool show_apply_immediately)
  56. : LLFloater("color picker"),
  57. mComponents(3),
  58. mMouseDownInLumRegion(false),
  59. mMouseDownInHueRegion(false),
  60. mMouseDownInSwatch(false),
  61. // *TODO: Specify this in XML
  62. mRGBViewerImageLeft(140),
  63. mRGBViewerImageTop(356),
  64. mRGBViewerImageWidth(256),
  65. mRGBViewerImageHeight(256),
  66. mLumRegionLeft(mRGBViewerImageLeft + mRGBViewerImageWidth + 16),
  67. mLumRegionTop(mRGBViewerImageTop),
  68. mLumRegionWidth(16),
  69. mLumRegionHeight(mRGBViewerImageHeight),
  70. mLumMarkerSize(6),
  71. // *TODO: Specify this in XML
  72. mSwatchRegionLeft(12),
  73. mSwatchRegionTop(190),
  74. mSwatchRegionWidth(116),
  75. mSwatchRegionHeight(60),
  76. // *TODO: Specify this in XML
  77. mPaletteCols(16),
  78. mPaletteRows(2),
  79. mHighlightEntry(-1),
  80. mPaletteRegionLeft(11),
  81. mPaletteRegionTop(100 - 8),
  82. mPaletteRegionWidth(mLumRegionLeft + mLumRegionWidth - 10),
  83. mPaletteRegionHeight(40),
  84. mSwatch(swatch),
  85. mActive(true),
  86. mCancelled(false),
  87. mCanApplyImmediately(show_apply_immediately),
  88. mContextConeOpacity(0.f)
  89. {
  90. LLUICtrlFactory::getInstance()->buildFloater(this,
  91. "floater_color_picker.xml");
  92. }
  93. //virtual
  94. LLFloaterColorPicker::~LLFloaterColorPicker()
  95. {
  96. // Shut down pipette tool if active
  97. stopUsingPipette();
  98. mPalette.clear();
  99. }
  100. //virtual
  101. bool LLFloaterColorPicker::postBuild()
  102. {
  103. mCancelBtn = getChild<LLButton>("cancel_btn");
  104. mCancelBtn->setClickedCallback(onClickCancel);
  105. mCancelBtn->setCallbackUserData(this);
  106. mSelectBtn = getChild<LLButton>("select_btn");
  107. mSelectBtn->setClickedCallback(onClickSelect);
  108. mSelectBtn->setCallbackUserData(this);
  109. mSelectBtn->setFocus(true);
  110. mPipetteBtn = getChild<LLButton>("color_pipette");
  111. mPipetteBtn->setImages("eye_button_inactive.tga", "eye_button_active.tga");
  112. mPipetteBtn->setClickedCallback(onClickPipette);
  113. mPipetteBtn->setCallbackUserData(this);
  114. mApplyImmediateCheck = getChild<LLCheckBoxCtrl>("apply_immediate");
  115. mApplyImmediateCheck->set(gSavedSettings.getBool("ApplyColorImmediately"));
  116. mApplyImmediateCheck->setCommitCallback(onImmediateCheck);
  117. mApplyImmediateCheck->setCallbackUserData(this);
  118. childSetCommitCallback("rspin", onTextCommit, this);
  119. childSetCommitCallback("gspin", onTextCommit, this);
  120. childSetCommitCallback("bspin", onTextCommit, this);
  121. childSetCommitCallback("hspin", onTextCommit, this);
  122. childSetCommitCallback("sspin", onTextCommit, this);
  123. childSetCommitCallback("lspin", onTextCommit, this);
  124. // Create a RGB type area (not really RGB but it got R, G & B in it)
  125. LLPointer<LLImageRaw> raw = new LLImageRaw(mRGBViewerImageWidth,
  126. mRGBViewerImageHeight,
  127. mComponents);
  128. U8* bits = raw->getData();
  129. S32 linesize = mRGBViewerImageWidth * mComponents;
  130. for (S32 y = 0; y < mRGBViewerImageHeight; ++y)
  131. {
  132. for (S32 x = 0; x < linesize; x += mComponents)
  133. {
  134. F32 r, g, b;
  135. hslToRgb((F32)x / (F32)(linesize - 1),
  136. (F32)y / (F32)(mRGBViewerImageHeight - 1),
  137. 0.5f, r, g, b);
  138. *(bits + x + y * linesize) = (U8)(r * 255.f);
  139. *(bits + x + y * linesize + 1) = (U8)(g * 255.f);
  140. *(bits + x + y * linesize + 2) = (U8)(b * 255.f);
  141. }
  142. }
  143. mRGBImage = LLViewerTextureManager::getLocalTexture((LLImageRaw*)raw,
  144. false);
  145. gGL.getTexUnit(0)->bind(mRGBImage);
  146. mRGBImage->setAddressMode(LLTexUnit::TAM_CLAMP);
  147. // Create palette
  148. for (S32 each = 0; each < mPaletteCols * mPaletteRows; ++each)
  149. {
  150. std::ostringstream codec;
  151. codec << "ColorPaletteEntry" << std::setfill('0') << std::setw(2)
  152. << each + 1;
  153. mPalette.emplace_back(gSavedSettings.getColor4(codec.str().c_str()));
  154. }
  155. if (!mCanApplyImmediately)
  156. {
  157. mApplyImmediateCheck->setEnabled(false);
  158. mApplyImmediateCheck->set(false);
  159. }
  160. setVisible(false);
  161. return true;
  162. }
  163. void LLFloaterColorPicker::showUI()
  164. {
  165. mCancelled = false;
  166. setVisible(true);
  167. setFocus(true);
  168. open();
  169. }
  170. void LLFloaterColorPicker::initUI(F32 r, F32 g, F32 b)
  171. {
  172. // Start catching lose-focus events from entry widgets
  173. enableTextCallbacks(true);
  174. // Under some circumstances, we get rogue values that can be calmed by
  175. // clamping...
  176. r = llclamp(r, 0.f, 1.f);
  177. g = llclamp(g, 0.f, 1.f);
  178. b = llclamp(b, 0.f, 1.f);
  179. // Store initial value in case cancel or revert is selected
  180. setOrigRgb(r, g, b);
  181. // Starting point for current value to
  182. setCurRgb(r, g, b);
  183. // Update text entry fields
  184. updateTextEntry();
  185. }
  186. F32 LLFloaterColorPicker::hueToRgb(F32 val1, F32 val2, F32 hue)
  187. {
  188. if (hue < 0.f)
  189. {
  190. hue += 1.f;
  191. }
  192. else if (hue > 1.f)
  193. {
  194. hue -= 1.f;
  195. }
  196. if (6.f * hue < 1.f)
  197. {
  198. return val1 + (val2 - val1) * 6.f * hue;
  199. }
  200. if (2.f * hue < 1.f)
  201. {
  202. return val2;
  203. }
  204. if (3.f * hue < 2.f)
  205. {
  206. return val1 + (val2 - val1) * (4.f - hue * 6.f);
  207. }
  208. return val1;
  209. }
  210. void LLFloaterColorPicker::hslToRgb(F32 h, F32 s, F32 l, F32& r, F32& g,
  211. F32& b)
  212. {
  213. if (s < 0.00001f)
  214. {
  215. r = g = b = l;
  216. }
  217. else
  218. {
  219. F32 inter_val2;
  220. if (l < 0.5f)
  221. {
  222. inter_val2 = l * (1.f + s);
  223. }
  224. else
  225. {
  226. inter_val2 = l + s - s * l;
  227. }
  228. F32 inter_val1 = 2.f * l - inter_val2;
  229. r = hueToRgb(inter_val1, inter_val2, h + (1.f / 3.f));
  230. g = hueToRgb(inter_val1, inter_val2, h);
  231. b = hueToRgb(inter_val1, inter_val2, h - (1.f / 3.f));
  232. }
  233. }
  234. void LLFloaterColorPicker::setCurRgb(F32 r, F32 g, F32 b)
  235. {
  236. // Save current RGB
  237. mCurR = r;
  238. mCurG = g;
  239. mCurB = b;
  240. // Update corresponding HSL values and
  241. LLColor3(r, g, b).calcHSL(&mCurH, &mCurS, &mCurL);
  242. // Color changed so update text fields(fixes SL-16968)
  243. // *HACK: turn off the callback wilst we update the text or we recurse
  244. // ourselves into oblivion. CP: this was required when I first wrote the
  245. // code but this may not be necessary anymore; leaving it there just in
  246. // case
  247. enableTextCallbacks(false);
  248. updateTextEntry();
  249. enableTextCallbacks(true);
  250. }
  251. void LLFloaterColorPicker::setCurHsl(F32 h, F32 s, F32 l)
  252. {
  253. // Save current HSL
  254. mCurH = h;
  255. mCurS = s;
  256. mCurL = l;
  257. // Update corresponding RGB values and
  258. hslToRgb(h, s, l, mCurR, mCurG, mCurB);
  259. }
  260. void LLFloaterColorPicker::onClickCancel(void* data)
  261. {
  262. LLFloaterColorPicker* self = (LLFloaterColorPicker*)data;
  263. if (self)
  264. {
  265. self->cancelSelection();
  266. self->close();
  267. }
  268. }
  269. void LLFloaterColorPicker::onClickSelect(void* data)
  270. {
  271. LLFloaterColorPicker* self = (LLFloaterColorPicker*)data;
  272. if (self)
  273. {
  274. self->mCancelled = false;
  275. // Apply to selection
  276. LLColorSwatchCtrl::onColorChanged(self->mSwatch,
  277. LLColorSwatchCtrl::COLOR_SELECT);
  278. self->close();
  279. }
  280. }
  281. void LLFloaterColorPicker::onClickPipette(void* data)
  282. {
  283. LLFloaterColorPicker* self = (LLFloaterColorPicker*)data;
  284. if (self)
  285. {
  286. if (self->mPipetteBtn->getToggleState())
  287. {
  288. gToolMgr.clearTransientTool();
  289. }
  290. else
  291. {
  292. gToolPipette.setSelectCallback(onColorSelect, self);
  293. gToolMgr.setTransientTool(&gToolPipette);
  294. }
  295. }
  296. }
  297. void LLFloaterColorPicker::onTextCommit(LLUICtrl* ctrl, void* data)
  298. {
  299. LLFloaterColorPicker* self = (LLFloaterColorPicker*)data;
  300. if (self)
  301. {
  302. self->onTextEntryChanged(ctrl);
  303. }
  304. }
  305. void LLFloaterColorPicker::onImmediateCheck(LLUICtrl* ctrl, void* data)
  306. {
  307. LLFloaterColorPicker* self = (LLFloaterColorPicker*)data;
  308. if (self)
  309. {
  310. bool apply = self->mApplyImmediateCheck->get();
  311. gSavedSettings.setBool("ApplyColorImmediately", apply);
  312. if (apply)
  313. {
  314. self->mCancelled = false;
  315. LLColorSwatchCtrl::onColorChanged(self->mSwatch,
  316. LLColorSwatchCtrl::COLOR_CHANGE);
  317. }
  318. }
  319. }
  320. void LLFloaterColorPicker::onColorSelect(const LLTextureEntry& te, void* data)
  321. {
  322. LLFloaterColorPicker* self = (LLFloaterColorPicker*)data;
  323. if (self)
  324. {
  325. self->setCurRgb(te.getColor().mV[VRED], te.getColor().mV[VGREEN],
  326. te.getColor().mV[VBLUE]);
  327. if (self->mApplyImmediateCheck->get())
  328. {
  329. self->mCancelled = false;
  330. LLColorSwatchCtrl::onColorChanged(self->mSwatch,
  331. LLColorSwatchCtrl::COLOR_CHANGE);
  332. }
  333. }
  334. }
  335. //virtual
  336. void LLFloaterColorPicker::onMouseCaptureLost()
  337. {
  338. setMouseDownInHueRegion(false);
  339. setMouseDownInLumRegion(false);
  340. }
  341. //virtual
  342. void LLFloaterColorPicker::draw()
  343. {
  344. LLRect swatch_rect;
  345. mSwatch->localRectToOtherView(mSwatch->getLocalRect(), &swatch_rect, this);
  346. // Draw context cone connecting color picker with color swatch in parent
  347. // floater
  348. LLRect local_rect = getLocalRect();
  349. if (gFocusMgr.childHasKeyboardFocus(this) && mSwatch->isInVisibleChain() &&
  350. mContextConeOpacity > 0.001f)
  351. {
  352. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  353. LLGLEnable(GL_CULL_FACE);
  354. gGL.begin(LLRender::TRIANGLE_STRIP);
  355. gGL.color4f(0.f, 0.f, 0.f,
  356. CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
  357. gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
  358. gGL.color4f(0.f, 0.f, 0.f,
  359. CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
  360. gGL.vertex2i(swatch_rect.mLeft, swatch_rect.mTop);
  361. gGL.color4f(0.f, 0.f, 0.f,
  362. CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
  363. gGL.vertex2i(local_rect.mRight, local_rect.mTop);
  364. gGL.color4f(0.f, 0.f, 0.f,
  365. CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
  366. gGL.vertex2i(swatch_rect.mRight, swatch_rect.mTop);
  367. gGL.color4f(0.f, 0.f, 0.f,
  368. CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
  369. gGL.vertex2i(local_rect.mRight, local_rect.mBottom);
  370. gGL.color4f(0.f, 0.f, 0.f,
  371. CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
  372. gGL.vertex2i(swatch_rect.mRight, swatch_rect.mBottom);
  373. gGL.color4f(0.f, 0.f, 0.f,
  374. CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
  375. gGL.vertex2i(local_rect.mLeft, local_rect.mBottom);
  376. gGL.color4f(0.f, 0.f, 0.f,
  377. CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
  378. gGL.vertex2i(swatch_rect.mLeft, swatch_rect.mBottom);
  379. gGL.color4f(0.f, 0.f, 0.f,
  380. CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
  381. gGL.vertex2i(local_rect.mLeft, local_rect.mTop);
  382. gGL.color4f(0.f, 0.f, 0.f,
  383. CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
  384. gGL.vertex2i(swatch_rect.mLeft, swatch_rect.mTop);
  385. gGL.end();
  386. }
  387. F32 critical_damp = LLCriticalDamp::getInterpolant(CONTEXT_FADE_TIME);
  388. if (gFocusMgr.childHasMouseCapture(getDragHandle()))
  389. {
  390. static LLCachedControl<F32> picker_context_opacity(gSavedSettings,
  391. "PickerContextOpacity");
  392. mContextConeOpacity = lerp(mContextConeOpacity,
  393. (F32)picker_context_opacity, critical_damp);
  394. }
  395. else
  396. {
  397. mContextConeOpacity = lerp(mContextConeOpacity, 0.f, critical_damp);
  398. }
  399. mPipetteBtn->setToggleState(gToolMgr.isCurrentTool(&gToolPipette));
  400. mApplyImmediateCheck->setEnabled(mActive && mCanApplyImmediately);
  401. mSelectBtn->setEnabled(mActive);
  402. // Base floater stuff
  403. LLFloater::draw();
  404. // Draw image for RGB area(not really RGB but you'll see what I mean...
  405. gl_draw_image(mRGBViewerImageLeft,
  406. mRGBViewerImageTop - mRGBViewerImageHeight, mRGBImage,
  407. LLColor4::white);
  408. // Update 'cursor' into RGB Section
  409. S32 x = (S32)((F32)mRGBViewerImageWidth * mCurH) - 8;
  410. S32 y = (S32)((F32)mRGBViewerImageHeight * mCurS) - 8;
  411. gl_line_2d(mRGBViewerImageLeft + x,
  412. mRGBViewerImageTop - mRGBViewerImageHeight + y + 8,
  413. mRGBViewerImageLeft + x + 16,
  414. mRGBViewerImageTop - mRGBViewerImageHeight + y + 8,
  415. LLColor4::black);
  416. gl_line_2d(mRGBViewerImageLeft + x + 8,
  417. mRGBViewerImageTop - mRGBViewerImageHeight + y,
  418. mRGBViewerImageLeft + x + 8,
  419. mRGBViewerImageTop - mRGBViewerImageHeight + y + 16,
  420. LLColor4::black);
  421. // Create rgb area outline
  422. gl_rect_2d(mRGBViewerImageLeft, mRGBViewerImageTop - mRGBViewerImageHeight,
  423. mRGBViewerImageLeft + mRGBViewerImageWidth, mRGBViewerImageTop,
  424. LLColor4::black, false);
  425. // Draw luminance slider
  426. for (S32 y = 0; y < mLumRegionHeight; ++y)
  427. {
  428. F32 r, g, b;
  429. hslToRgb(mCurH, mCurS,(F32)y / (F32)mLumRegionHeight, r, g, b);
  430. gl_rect_2d(mLumRegionLeft, mLumRegionTop - mLumRegionHeight + y,
  431. mLumRegionLeft + mLumRegionWidth,
  432. mLumRegionTop - mLumRegionHeight + y - 1,
  433. LLColor4(r, g, b, 1.f));
  434. }
  435. // Draw luninance marker
  436. S32 start_x = mLumRegionLeft + mLumRegionWidth;
  437. S32 start_y = mLumRegionTop - mLumRegionHeight +
  438. (S32)(mLumRegionHeight * mCurL);
  439. gl_triangle_2d(start_x, start_y,
  440. start_x + mLumMarkerSize, start_y - mLumMarkerSize,
  441. start_x + mLumMarkerSize, start_y + mLumMarkerSize,
  442. LLColor4::black, true);
  443. // Draw luminance slider outline
  444. gl_rect_2d(mLumRegionLeft, mLumRegionTop - mLumRegionHeight,
  445. mLumRegionLeft + mLumRegionWidth, mLumRegionTop,
  446. LLColor4::black, false);
  447. // Draw selected color swatch
  448. gl_rect_2d(mSwatchRegionLeft, mSwatchRegionTop - mSwatchRegionHeight,
  449. mSwatchRegionLeft + mSwatchRegionWidth, mSwatchRegionTop,
  450. LLColor4(mCurR, mCurG, mCurB, 1.0f), true);
  451. // Draw selected color swatch outline
  452. gl_rect_2d(mSwatchRegionLeft, mSwatchRegionTop - mSwatchRegionHeight,
  453. mSwatchRegionLeft + mSwatchRegionWidth, mSwatchRegionTop,
  454. LLColor4::black, false);
  455. // Color palette code is a little more involved so break it out into its
  456. // own method
  457. drawPalette();
  458. }
  459. // Finds a complimentary color to the one passed in that can be used to
  460. // highlight
  461. const LLColor4& LLFloaterColorPicker::getComplementaryColor(const LLColor4& bg_col)
  462. {
  463. // Going to base calculation on luminance
  464. F32 h, s, l;
  465. bg_col.calcHSL(&h, &s, &l);
  466. // Fairly simple heuristic for now...
  467. return l < 0.005f ? LLColor4::white : LLColor4::black;
  468. }
  469. // Draws the color palette
  470. void LLFloaterColorPicker::drawPalette()
  471. {
  472. S32 cur_entry = 0;
  473. S32 palette_size = mPalette.size();
  474. for (S32 y = 0; y < mPaletteRows && cur_entry < palette_size; ++y)
  475. {
  476. for (S32 x = 0; x < mPaletteCols && cur_entry < palette_size; ++x)
  477. {
  478. // Calculate position
  479. S32 x1 = mPaletteRegionLeft + (mPaletteRegionWidth * x) /
  480. mPaletteCols;
  481. S32 y1 = mPaletteRegionTop - (mPaletteRegionHeight * y) /
  482. mPaletteRows;
  483. S32 x2 = mPaletteRegionLeft + (mPaletteRegionWidth * (x + 1)) /
  484. mPaletteCols;
  485. S32 y2 = mPaletteRegionTop - (mPaletteRegionHeight * (y + 1)) /
  486. mPaletteRows;
  487. // Draw palette entry color
  488. gl_rect_2d(x1 + 2, y1 - 2, x2 - 2, y2 + 2, mPalette[cur_entry++],
  489. true);
  490. gl_rect_2d(x1 + 1, y1 - 1, x2 - 1, y2 + 1, LLColor4::black, false);
  491. }
  492. }
  493. // If there is something to highlight(mouse down in swatch & hovering over
  494. // palette)
  495. if (mHighlightEntry >= 0)
  496. {
  497. // Extract row/column from palette index
  498. S32 col = mHighlightEntry % mPaletteCols;
  499. S32 row = mHighlightEntry / mPaletteCols;
  500. // Calculate position of this entry
  501. S32 x1 = mPaletteRegionLeft +
  502. (mPaletteRegionWidth * col) / mPaletteCols;
  503. S32 y1 = mPaletteRegionTop -
  504. (mPaletteRegionHeight * row) / mPaletteRows;
  505. S32 x2 = mPaletteRegionLeft +
  506. (mPaletteRegionWidth * (col + 1)) / mPaletteCols;
  507. S32 y2 = mPaletteRegionTop -
  508. (mPaletteRegionHeight * (row + 1)) / mPaletteRows;
  509. // Center position of entry
  510. S32 x0 = x1 + (x2 - x1) / 2;
  511. S32 y0 = y1 - (y1 - y2) / 2;
  512. // Find a color that works well as a highlight color
  513. LLColor4 hl_col(getComplementaryColor(mPalette[mHighlightEntry]));
  514. // Mark a cross for entry that is being hovered
  515. gl_line_2d(x0 - 4, y0 - 4, x0 + 4, y0 + 4, hl_col);
  516. gl_line_2d(x0 + 4, y0 - 4, x0 - 4, y0 + 4, hl_col);
  517. }
  518. }
  519. // Updates text entry values for RGB/HSL (cannot be done in draw() since this
  520. // overwrites input
  521. void LLFloaterColorPicker::updateTextEntry()
  522. {
  523. // Set values in spinners
  524. childSetValue("rspin", mCurR * 255.f);
  525. childSetValue("gspin", mCurG * 255.f);
  526. childSetValue("bspin", mCurB * 255.f);
  527. childSetValue("hspin", mCurH * 360.f);
  528. childSetValue("sspin", mCurS * 100.f);
  529. childSetValue("lspin", mCurL * 100.f);
  530. }
  531. // Turns on or off text entry commit call backs
  532. void LLFloaterColorPicker::enableTextCallbacks(bool stateIn)
  533. {
  534. if (stateIn)
  535. {
  536. childSetCommitCallback("rspin", onTextCommit, this);
  537. childSetCommitCallback("gspin", onTextCommit, this);
  538. childSetCommitCallback("bspin", onTextCommit, this);
  539. childSetCommitCallback("hspin", onTextCommit, this);
  540. childSetCommitCallback("sspin", onTextCommit, this);
  541. childSetCommitCallback("lspin", onTextCommit, this);
  542. }
  543. else
  544. {
  545. childSetCommitCallback("rspin", 0, this);
  546. childSetCommitCallback("gspin", 0, this);
  547. childSetCommitCallback("bspin", 0, this);
  548. childSetCommitCallback("hspin", 0, this);
  549. childSetCommitCallback("sspin", 0, this);
  550. childSetCommitCallback("lspin", 0, this);
  551. }
  552. }
  553. void LLFloaterColorPicker::onTextEntryChanged(LLUICtrl* ctrl)
  554. {
  555. std::string name = ctrl->getName();
  556. if (name == "rspin" || name == "gspin" || name == "bspin")
  557. {
  558. // A value in RGB boxes changed
  559. F32 r = mCurR;
  560. F32 g = mCurG;
  561. F32 b = mCurB;
  562. // Update component value with new value from text
  563. if (name == "rspin")
  564. {
  565. r = (F32)ctrl->getValue().asReal() / 255.f;
  566. }
  567. else if (name == "gspin")
  568. {
  569. g = (F32)ctrl->getValue().asReal() / 255.f;
  570. }
  571. else if (name == "bspin")
  572. {
  573. b = (F32)ctrl->getValue().asReal() / 255.f;
  574. }
  575. // Update current RGB and sync current HSL
  576. setCurRgb(r, g, b);
  577. }
  578. else if (name == "hspin" || name == "sspin" || name == "lspin")
  579. {
  580. // A value in HSL boxes changed
  581. F32 h = mCurH;
  582. F32 s = mCurS;
  583. F32 l = mCurL;
  584. if (name == "hspin")
  585. {
  586. h = (F32)ctrl->getValue().asReal() / 360.f;
  587. }
  588. else if (name == "sspin")
  589. {
  590. s = (F32)ctrl->getValue().asReal() * 0.01f;
  591. }
  592. else if (name == "lspin")
  593. {
  594. l = (F32)ctrl->getValue().asReal() * 0.01f;
  595. }
  596. // Update current HSL and sync current RGB
  597. setCurHsl(h, s, l);
  598. }
  599. else
  600. {
  601. llwarns << "Unknown control name: " << name << llendl;
  602. return;
  603. }
  604. // *HACK: turn off the call back wilst we update the text or we recurse
  605. // ourselves into oblivion
  606. enableTextCallbacks(false);
  607. updateTextEntry();
  608. enableTextCallbacks(true);
  609. if (mApplyImmediateCheck->get())
  610. {
  611. mCancelled = false;
  612. LLColorSwatchCtrl::onColorChanged(mSwatch,
  613. LLColorSwatchCtrl::COLOR_CHANGE);
  614. }
  615. }
  616. bool LLFloaterColorPicker::updateRgbHslFromPoint(S32 x, S32 y)
  617. {
  618. if (x >= mRGBViewerImageLeft &&
  619. x <= mRGBViewerImageLeft + mRGBViewerImageWidth &&
  620. y <= mRGBViewerImageTop &&
  621. y >= mRGBViewerImageTop - mRGBViewerImageHeight)
  622. {
  623. if (mCurL >= 1.f)
  624. {
  625. // Give the user a minimum of feedback on the hue, when adjustment
  626. // is started from pure white... The rationale is that if they are
  627. // trying to adjust the hue, it is obviously because they do not
  628. // want a pure white. A luminance of 0.99 is "99" (for a maximum of
  629. // 100) in the corresponding spinner. HB
  630. mCurL = 0.99f;
  631. }
  632. // Update HSL (and therefore RGB) based on new H & S and current L
  633. setCurHsl((F32)(x - mRGBViewerImageLeft) / (F32)mRGBViewerImageWidth,
  634. (F32)(y - mRGBViewerImageTop + mRGBViewerImageHeight) /
  635. (F32)mRGBViewerImageHeight, mCurL);
  636. // Indicate a value changed
  637. return true;
  638. }
  639. else if (x >= mLumRegionLeft && y <= mLumRegionTop &&
  640. x <= mLumRegionLeft + mLumRegionWidth &&
  641. y >= mLumRegionTop - mLumRegionHeight)
  642. {
  643. // Update HSL(and therefore RGB) based on current HS and new L
  644. setCurHsl(mCurH, mCurS,
  645. (F32)(y - mRGBViewerImageTop + mRGBViewerImageHeight) /
  646. (F32)mRGBViewerImageHeight);
  647. // Indicate a value changed
  648. return true;
  649. }
  650. return false;
  651. }
  652. //virtual
  653. bool LLFloaterColorPicker::handleMouseDown(S32 x, S32 y, MASK mask)
  654. {
  655. // Make it the frontmost
  656. if (gFloaterViewp)
  657. {
  658. gFloaterViewp->bringToFront(this);
  659. }
  660. // Rectangle containing RGB area
  661. LLRect rgb_rect(mRGBViewerImageLeft, mRGBViewerImageTop,
  662. mRGBViewerImageLeft + mRGBViewerImageWidth,
  663. mRGBViewerImageTop - mRGBViewerImageHeight);
  664. if (rgb_rect.pointInRect(x, y))
  665. {
  666. gFocusMgr.setMouseCapture(this);
  667. // Mouse button down
  668. setMouseDownInHueRegion(true);
  669. // Update all values based on initial click
  670. updateRgbHslFromPoint(x, y);
  671. // Required: do not drag floater here.
  672. return true;
  673. }
  674. // Rectangle luminosity RGB area
  675. LLRect lum_rect(mLumRegionLeft, mLumRegionTop,
  676. mLumRegionLeft + mLumRegionWidth + mLumMarkerSize,
  677. mLumRegionTop - mLumRegionHeight);
  678. if (lum_rect.pointInRect(x, y))
  679. {
  680. gFocusMgr.setMouseCapture(this);
  681. // Mouse button down
  682. setMouseDownInLumRegion(true);
  683. // Required: do not drag floater here.
  684. return true;
  685. }
  686. // Rectangle containing swatch area
  687. LLRect swatch_rect(mSwatchRegionLeft, mSwatchRegionTop,
  688. mSwatchRegionLeft + mSwatchRegionWidth,
  689. mSwatchRegionTop - mSwatchRegionHeight);
  690. if (swatch_rect.pointInRect(x, y))
  691. {
  692. setMouseDownInSwatch(true);
  693. // Required: do not drag floater here.
  694. return true;
  695. }
  696. setMouseDownInSwatch(false);
  697. // Rectangle containing palette area
  698. LLRect rect(mPaletteRegionLeft, mPaletteRegionTop,
  699. mPaletteRegionLeft + mPaletteRegionWidth,
  700. mPaletteRegionTop - mPaletteRegionHeight);
  701. if (rect.pointInRect(x, y))
  702. {
  703. // Release keyboard focus so we can change text values
  704. if (gFocusMgr.childHasKeyboardFocus(this))
  705. {
  706. mSelectBtn->setFocus(true);
  707. }
  708. // Calculate which palette index we selected
  709. S32 c = ((x - mPaletteRegionLeft) * mPaletteCols) /
  710. mPaletteRegionWidth;
  711. S32 r = (y - mPaletteRegionTop + mPaletteRegionHeight) *
  712. mPaletteRows / mPaletteRegionHeight;
  713. U32 index = (mPaletteRows - r - 1) * mPaletteCols + c;
  714. if (index <= mPalette.size())
  715. {
  716. const LLColor4& selected = mPalette[index];
  717. setCurRgb(selected[0], selected[1], selected[2]);
  718. if (mApplyImmediateCheck->get())
  719. {
  720. mCancelled = false;
  721. LLColorSwatchCtrl::onColorChanged(mSwatch,
  722. LLColorSwatchCtrl::COLOR_CHANGE);
  723. }
  724. // *HACK: turn off the call back wilst we update the text or we
  725. // recurse ourselves into oblivion
  726. enableTextCallbacks(false);
  727. updateTextEntry();
  728. enableTextCallbacks(true);
  729. }
  730. return true;
  731. }
  732. // Dispatch to base class for the rest of things
  733. return LLFloater::handleMouseDown(x, y, mask);
  734. }
  735. //virtual
  736. bool LLFloaterColorPicker::handleHover(S32 x, S32 y, MASK mask)
  737. {
  738. // If we are the front most window
  739. if (isFrontmost())
  740. {
  741. // Mouse was pressed within region
  742. if (mMouseDownInHueRegion || mMouseDownInLumRegion)
  743. {
  744. S32 clamped_x, clamped_y;
  745. if (mMouseDownInHueRegion)
  746. {
  747. clamped_x = llclamp(x, mRGBViewerImageLeft,
  748. mRGBViewerImageLeft + mRGBViewerImageWidth);
  749. clamped_y = llclamp(y, mRGBViewerImageTop - mRGBViewerImageHeight,
  750. mRGBViewerImageTop);
  751. }
  752. else
  753. {
  754. clamped_x = llclamp(x, mLumRegionLeft,
  755. mLumRegionLeft + mLumRegionWidth);
  756. clamped_y = llclamp(y, mLumRegionTop - mLumRegionHeight,
  757. mLumRegionTop);
  758. }
  759. // Update the stored RGB/HSL values using the mouse position.
  760. // Returns true if RGB was updated
  761. if (updateRgbHslFromPoint(clamped_x, clamped_y))
  762. {
  763. // Update text entry fields
  764. updateTextEntry();
  765. #if 0 // RN: apparently changing color when dragging generates too
  766. // much traffic and results in sporadic updates
  767. if (mApplyImmediateCheck->get())
  768. {
  769. // Commit changed color to swatch subject
  770. mCancelled = false;
  771. LLColorSwatchCtrl::onColorChanged(mSwatch);
  772. }
  773. #endif
  774. }
  775. }
  776. mHighlightEntry = -1;
  777. if (mMouseDownInSwatch)
  778. {
  779. gWindowp->setCursor(UI_CURSOR_ARROWDRAG);
  780. // If cursor if over a palette entry
  781. LLRect rect(mPaletteRegionLeft, mPaletteRegionTop,
  782. mPaletteRegionLeft + mPaletteRegionWidth,
  783. mPaletteRegionTop - mPaletteRegionHeight);
  784. if (rect.pointInRect(x, y))
  785. {
  786. // Find row/column in palette
  787. S32 x_delta = ((x - mPaletteRegionLeft) * mPaletteCols) /
  788. mPaletteRegionWidth;
  789. S32 y_delta = ((mPaletteRegionTop - y - 1) * mPaletteRows) /
  790. mPaletteRegionHeight;
  791. // Calculate the entry 0...n-1 to highlight and set variable to
  792. // next draw() picks it up
  793. mHighlightEntry = x_delta + y_delta * mPaletteCols;
  794. }
  795. return true;
  796. }
  797. }
  798. // Dispatch to base class for the rest of things
  799. return LLFloater::handleHover(x, y, mask);
  800. }
  801. //virtual
  802. void LLFloaterColorPicker::onClose(bool app_quitting)
  803. {
  804. // RN: this is consistent with texture picker in that closing the window
  805. // leaves the current selection to change this to "close to cancel",
  806. // uncomment the following line
  807. LLFloater::onClose(app_quitting);
  808. }
  809. // Reverts state once mouse button is released
  810. //virtual
  811. bool LLFloaterColorPicker::handleMouseUp(S32 x, S32 y, MASK mask)
  812. {
  813. gWindowp->setCursor(UI_CURSOR_ARROW);
  814. if (mMouseDownInHueRegion || mMouseDownInLumRegion)
  815. {
  816. if (mApplyImmediateCheck->get())
  817. {
  818. mCancelled = false;
  819. LLColorSwatchCtrl::onColorChanged(mSwatch,
  820. LLColorSwatchCtrl::COLOR_CHANGE);
  821. }
  822. }
  823. // Rectangle containing palette area
  824. LLRect rect(mPaletteRegionLeft, mPaletteRegionTop,
  825. mPaletteRegionLeft + mPaletteRegionWidth,
  826. mPaletteRegionTop - mPaletteRegionHeight);
  827. if (rect.pointInRect(x, y))
  828. {
  829. if (mMouseDownInSwatch)
  830. {
  831. S32 palette_size = mPalette.size();
  832. S32 cur_entry = 0;
  833. for (S32 row = 0; row < mPaletteRows && cur_entry < palette_size;
  834. ++row)
  835. {
  836. for (S32 column = 0;
  837. column < mPaletteCols && cur_entry < palette_size;
  838. ++column)
  839. {
  840. S32 left = mPaletteRegionLeft +
  841. (mPaletteRegionWidth * column) / mPaletteCols;
  842. S32 top = mPaletteRegionTop -
  843. (mPaletteRegionHeight * row) / mPaletteRows;
  844. S32 right = mPaletteRegionLeft +
  845. (mPaletteRegionWidth * (column + 1)) /
  846. mPaletteCols;
  847. S32 bottom = mPaletteRegionTop -
  848. (mPaletteRegionHeight * (row + 1)) /
  849. mPaletteRows;
  850. // Rect is flipped vertically when testing here
  851. LLRect dropRect(left, top, right, bottom);
  852. if (dropRect.pointInRect(x, y))
  853. {
  854. mPalette[cur_entry].set(mCurR, mCurG, mCurB, 1.f);
  855. std::ostringstream codec;
  856. codec << "ColorPaletteEntry" << std::setfill('0')
  857. << std::setw(2) << cur_entry + 1;
  858. const std::string s(codec.str());
  859. gSavedSettings.setColor4(s.c_str(),
  860. mPalette[cur_entry]);
  861. }
  862. ++cur_entry;
  863. }
  864. }
  865. }
  866. }
  867. // Mouse button not down anymore
  868. setMouseDownInHueRegion(false);
  869. setMouseDownInLumRegion(false);
  870. // Mouse button not down in color swatch anymore
  871. mMouseDownInSwatch = false;
  872. if (hasMouseCapture())
  873. {
  874. gFocusMgr.setMouseCapture(NULL);
  875. }
  876. // Dispatch to base class for the rest of things
  877. return LLFloater::handleMouseUp(x, y, mask);
  878. }
  879. // Cancels current color selection, reverts to original and closes picker
  880. void LLFloaterColorPicker::cancelSelection()
  881. {
  882. // Avoid potential infinite loop since LLColorSwatchCtrl::onColorChanged()
  883. // could re-trigger a cancelSelection() call via its callback. HB
  884. if (mCancelled)
  885. {
  886. return;
  887. }
  888. mCancelled = true;
  889. // Restore the previous color selection
  890. setCurRgb(mOrigR, mOrigG, mOrigB);
  891. // We are going away and when we do and the entry widgets lose focus, they
  892. // do bad things so turn them off
  893. enableTextCallbacks(false);
  894. // Update in world item with original color via current swatch
  895. LLColorSwatchCtrl::onColorChanged(mSwatch, LLColorSwatchCtrl::COLOR_CANCEL);
  896. // Hide picker dialog
  897. setVisible(false);
  898. }
  899. void LLFloaterColorPicker::setMouseDownInHueRegion(bool mouse_down_in_region)
  900. {
  901. mMouseDownInHueRegion = mouse_down_in_region;
  902. if (mouse_down_in_region && gFocusMgr.childHasKeyboardFocus(this))
  903. {
  904. // Get focus out of spinners so that they can update freely
  905. mSelectBtn->setFocus(true);
  906. }
  907. }
  908. void LLFloaterColorPicker::setMouseDownInLumRegion(bool mouse_down_in_region)
  909. {
  910. mMouseDownInLumRegion = mouse_down_in_region;
  911. if (mouse_down_in_region && gFocusMgr.childHasKeyboardFocus(this))
  912. {
  913. // Get focus out of spinners so that they can update freely
  914. mSelectBtn->setFocus(true);
  915. }
  916. }
  917. void LLFloaterColorPicker::setMouseDownInSwatch(bool mouse_down_in_swatch)
  918. {
  919. mMouseDownInSwatch = mouse_down_in_swatch;
  920. if (mouse_down_in_swatch && gFocusMgr.childHasKeyboardFocus(this))
  921. {
  922. // Get focus out of spinners so that they can update freely
  923. mSelectBtn->setFocus(true);
  924. }
  925. }
  926. void LLFloaterColorPicker::setActive(bool active)
  927. {
  928. // Shut down pipette tool if active
  929. if (!active && mPipetteBtn->getToggleState())
  930. {
  931. stopUsingPipette();
  932. }
  933. mActive = active;
  934. }
  935. void LLFloaterColorPicker::stopUsingPipette()
  936. {
  937. if (gToolMgr.isCurrentTool(&gToolPipette))
  938. {
  939. gToolMgr.clearTransientTool();
  940. }
  941. }