llmultislider.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:15k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llmultisldr.cpp
  3.  * @brief LLMultiSlider base class
  4.  *
  5.  * $LicenseInfo:firstyear=2007&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2007-2010, 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 "linden_common.h"
  33. #include "llmultislider.h"
  34. #include "llui.h"
  35. #include "llgl.h"
  36. #include "llwindow.h"
  37. #include "llfocusmgr.h"
  38. #include "llkeyboard.h" // for the MASK constants
  39. #include "llcontrol.h"
  40. #include "lluictrlfactory.h"
  41. #include <sstream>
  42. static LLDefaultChildRegistry::Register<LLMultiSlider> r("multi_slider_bar");
  43. const F32 FLOAT_THRESHOLD = 0.00001f;
  44. S32 LLMultiSlider::mNameCounter = 0;
  45. LLMultiSlider::SliderParams::SliderParams()
  46. : name("name"),
  47. value("value", 0.f)
  48. {
  49. }
  50. LLMultiSlider::Params::Params()
  51. : max_sliders("max_sliders", 1),
  52. allow_overlap("allow_overlap", false),
  53. draw_track("draw_track", true),
  54. use_triangle("use_triangle", false),
  55. track_color("track_color"),
  56. thumb_disabled_color("thumb_disabled_color"),
  57. thumb_outline_color("thumb_outline_color"),
  58. thumb_center_color("thumb_center_color"),
  59. thumb_center_selected_color("thumb_center_selected_color"),
  60. triangle_color("triangle_color"),
  61. mouse_down_callback("mouse_down_callback"),
  62. mouse_up_callback("mouse_up_callback"),
  63. thumb_width("thumb_width"),
  64. sliders("slider")
  65. {
  66. name = "multi_slider_bar";
  67. mouse_opaque(true);
  68. follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP);
  69. }
  70. LLMultiSlider::LLMultiSlider(const LLMultiSlider::Params& p)
  71. : LLF32UICtrl(p),
  72. mMouseOffset( 0 ),
  73. mDragStartThumbRect( 0, getRect().getHeight(), p.thumb_width, 0 ),
  74. mMaxNumSliders(p.max_sliders),
  75. mAllowOverlap(p.allow_overlap),
  76. mDrawTrack(p.draw_track),
  77. mUseTriangle(p.use_triangle),
  78. mTrackColor(p.track_color()),
  79. mThumbOutlineColor(p.thumb_outline_color()),
  80. mThumbCenterColor(p.thumb_center_color()),
  81. mThumbCenterSelectedColor(p.thumb_center_selected_color()),
  82. mDisabledThumbColor(p.thumb_disabled_color()),
  83. mTriangleColor(p.triangle_color()),
  84. mThumbWidth(p.thumb_width),
  85. mMouseDownSignal(NULL),
  86. mMouseUpSignal(NULL)
  87. {
  88. mValue.emptyMap();
  89. mCurSlider = LLStringUtil::null;
  90. if (p.mouse_down_callback.isProvided())
  91. {
  92. setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
  93. }
  94. if (p.mouse_up_callback.isProvided())
  95. {
  96. setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
  97. }
  98. for (LLInitParam::ParamIterator<SliderParams>::const_iterator it = p.sliders().begin();
  99. it != p.sliders().end();
  100. ++it)
  101. {
  102. if (it->name.isProvided())
  103. {
  104. addSlider(it->value, it->name);
  105. }
  106. else
  107. {
  108. addSlider(it->value);
  109. }
  110. }
  111. }
  112. LLMultiSlider::~LLMultiSlider()
  113. {
  114. delete mMouseDownSignal;
  115. delete mMouseUpSignal;
  116. }
  117. void LLMultiSlider::setSliderValue(const std::string& name, F32 value, BOOL from_event)
  118. {
  119. // exit if not there
  120. if(!mValue.has(name)) {
  121. return;
  122. }
  123. value = llclamp( value, mMinValue, mMaxValue );
  124. // Round to nearest increment (bias towards rounding down)
  125. value -= mMinValue;
  126. value += mIncrement/2.0001f;
  127. value -= fmod(value, mIncrement);
  128. F32 newValue = mMinValue + value;
  129. // now, make sure no overlap
  130. // if we want that
  131. if(!mAllowOverlap) {
  132. bool hit = false;
  133. // look at the current spot
  134. // and see if anything is there
  135. LLSD::map_iterator mIt = mValue.beginMap();
  136. for(;mIt != mValue.endMap(); mIt++) {
  137. F32 testVal = (F32)mIt->second.asReal() - newValue;
  138. if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_THRESHOLD &&
  139. mIt->first != name) {
  140. hit = true;
  141. break;
  142. }
  143. }
  144. // if none found, stop
  145. if(hit) {
  146. return;
  147. }
  148. }
  149. // now set it in the map
  150. mValue[name] = newValue;
  151. // set the control if it's the current slider and not from an event
  152. if (!from_event && name == mCurSlider)
  153. {
  154. setControlValue(mValue);
  155. }
  156. F32 t = (newValue - mMinValue) / (mMaxValue - mMinValue);
  157. S32 left_edge = mThumbWidth/2;
  158. S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
  159. S32 x = left_edge + S32( t * (right_edge - left_edge) );
  160. mThumbRects[name].mLeft = x - (mThumbWidth/2);
  161. mThumbRects[name].mRight = x + (mThumbWidth/2);
  162. }
  163. void LLMultiSlider::setValue(const LLSD& value)
  164. {
  165. // only do if it's a map
  166. if(value.isMap()) {
  167. // add each value... the first in the map becomes the current
  168. LLSD::map_const_iterator mIt = value.beginMap();
  169. mCurSlider = mIt->first;
  170. for(; mIt != value.endMap(); mIt++) {
  171. setSliderValue(mIt->first, (F32)mIt->second.asReal(), TRUE);
  172. }
  173. }
  174. }
  175. F32 LLMultiSlider::getSliderValue(const std::string& name) const
  176. {
  177. return (F32)mValue[name].asReal();
  178. }
  179. void LLMultiSlider::setCurSlider(const std::string& name)
  180. {
  181. if(mValue.has(name)) {
  182. mCurSlider = name;
  183. }
  184. }
  185. const std::string& LLMultiSlider::addSlider()
  186. {
  187. return addSlider(mInitialValue);
  188. }
  189. const std::string& LLMultiSlider::addSlider(F32 val)
  190. {
  191. std::stringstream newName;
  192. F32 initVal = val;
  193. if(mValue.size() >= mMaxNumSliders) {
  194. return LLStringUtil::null;
  195. }
  196. // create a new name
  197. newName << "sldr" << mNameCounter;
  198. mNameCounter++;
  199. bool foundOne = findUnusedValue(initVal);
  200. if(!foundOne) {
  201. return LLStringUtil::null;
  202. }
  203. // add a new thumb rect
  204. mThumbRects[newName.str()] = LLRect( 0, getRect().getHeight(), mThumbWidth, 0 );
  205. // add the value and set the current slider to this one
  206. mValue.insert(newName.str(), initVal);
  207. mCurSlider = newName.str();
  208. // move the slider
  209. setSliderValue(mCurSlider, initVal, TRUE);
  210. return mCurSlider;
  211. }
  212. void LLMultiSlider::addSlider(F32 val, const std::string& name)
  213. {
  214. F32 initVal = val;
  215. if(mValue.size() >= mMaxNumSliders) {
  216. return;
  217. }
  218. bool foundOne = findUnusedValue(initVal);
  219. if(!foundOne) {
  220. return;
  221. }
  222. // add a new thumb rect
  223. mThumbRects[name] = LLRect( 0, getRect().getHeight(), mThumbWidth, 0 );
  224. // add the value and set the current slider to this one
  225. mValue.insert(name, initVal);
  226. mCurSlider = name;
  227. // move the slider
  228. setSliderValue(mCurSlider, initVal, TRUE);
  229. }
  230. bool LLMultiSlider::findUnusedValue(F32& initVal)
  231. {
  232. bool firstTry = true;
  233. // find the first open slot starting with
  234. // the initial value
  235. while(true) {
  236. bool hit = false;
  237. // look at the current spot
  238. // and see if anything is there
  239. LLSD::map_iterator mIt = mValue.beginMap();
  240. for(;mIt != mValue.endMap(); mIt++) {
  241. F32 testVal = (F32)mIt->second.asReal() - initVal;
  242. if(testVal > -FLOAT_THRESHOLD && testVal < FLOAT_THRESHOLD) {
  243. hit = true;
  244. break;
  245. }
  246. }
  247. // if we found one
  248. if(!hit) {
  249. break;
  250. }
  251. // increment and wrap if need be
  252. initVal += mIncrement;
  253. if(initVal > mMaxValue) {
  254. initVal = mMinValue;
  255. }
  256. // stop if it's filled
  257. if(initVal == mInitialValue && !firstTry) {
  258. llwarns << "Whoa! Too many multi slider elements to add one to" << llendl;
  259. return false;
  260. }
  261. firstTry = false;
  262. continue;
  263. }
  264. return true;
  265. }
  266. void LLMultiSlider::deleteSlider(const std::string& name)
  267. {
  268. // can't delete last slider
  269. if(mValue.size() <= 0) {
  270. return;
  271. }
  272. // get rid of value from mValue and its thumb rect
  273. mValue.erase(name);
  274. mThumbRects.erase(name);
  275. // set to the last created
  276. if(mValue.size() > 0) {
  277. std::map<std::string, LLRect>::iterator mIt = mThumbRects.end();
  278. mIt--;
  279. mCurSlider = mIt->first;
  280. }
  281. }
  282. void LLMultiSlider::clear()
  283. {
  284. while(mThumbRects.size() > 0) {
  285. deleteCurSlider();
  286. }
  287. LLF32UICtrl::clear();
  288. }
  289. BOOL LLMultiSlider::handleHover(S32 x, S32 y, MASK mask)
  290. {
  291. if( gFocusMgr.getMouseCapture() == this )
  292. {
  293. S32 left_edge = mThumbWidth/2;
  294. S32 right_edge = getRect().getWidth() - (mThumbWidth/2);
  295. x += mMouseOffset;
  296. x = llclamp( x, left_edge, right_edge );
  297. F32 t = F32(x - left_edge) / (right_edge - left_edge);
  298. setCurSliderValue(t * (mMaxValue - mMinValue) + mMinValue );
  299. onCommit();
  300. getWindow()->setCursor(UI_CURSOR_ARROW);
  301. lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
  302. }
  303. else
  304. {
  305. getWindow()->setCursor(UI_CURSOR_ARROW);
  306. lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
  307. }
  308. return TRUE;
  309. }
  310. BOOL LLMultiSlider::handleMouseUp(S32 x, S32 y, MASK mask)
  311. {
  312. BOOL handled = FALSE;
  313. if( gFocusMgr.getMouseCapture() == this )
  314. {
  315. gFocusMgr.setMouseCapture( NULL );
  316. if (mMouseUpSignal)
  317. (*mMouseUpSignal)( this, LLSD() );
  318. handled = TRUE;
  319. make_ui_sound("UISndClickRelease");
  320. }
  321. else
  322. {
  323. handled = TRUE;
  324. }
  325. return handled;
  326. }
  327. BOOL LLMultiSlider::handleMouseDown(S32 x, S32 y, MASK mask)
  328. {
  329. // only do sticky-focus on non-chrome widgets
  330. if (!getIsChrome())
  331. {
  332. setFocus(TRUE);
  333. }
  334. if (mMouseDownSignal)
  335. (*mMouseDownSignal)( this, LLSD() );
  336. if (MASK_CONTROL & mask) // if CTRL is modifying
  337. {
  338. setCurSliderValue(mInitialValue);
  339. onCommit();
  340. }
  341. else
  342. {
  343. // scroll through thumbs to see if we have a new one selected and select that one
  344. std::map<std::string, LLRect>::iterator mIt = mThumbRects.begin();
  345. for(; mIt != mThumbRects.end(); mIt++) {
  346. // check if inside.  If so, set current slider and continue
  347. if(mIt->second.pointInRect(x,y)) {
  348. mCurSlider = mIt->first;
  349. break;
  350. }
  351. }
  352. // Find the offset of the actual mouse location from the center of the thumb.
  353. if (mThumbRects[mCurSlider].pointInRect(x,y))
  354. {
  355. mMouseOffset = (mThumbRects[mCurSlider].mLeft + mThumbWidth/2) - x;
  356. }
  357. else
  358. {
  359. mMouseOffset = 0;
  360. }
  361. // Start dragging the thumb
  362. // No handler needed for focus lost since this class has no state that depends on it.
  363. gFocusMgr.setMouseCapture( this );
  364. mDragStartThumbRect = mThumbRects[mCurSlider];
  365. }
  366. make_ui_sound("UISndClick");
  367. return TRUE;
  368. }
  369. BOOL LLMultiSlider::handleKeyHere(KEY key, MASK mask)
  370. {
  371. BOOL handled = FALSE;
  372. switch(key)
  373. {
  374. case KEY_UP:
  375. case KEY_DOWN:
  376. // eat up and down keys to be consistent
  377. handled = TRUE;
  378. break;
  379. case KEY_LEFT:
  380. setCurSliderValue(getCurSliderValue() - getIncrement());
  381. onCommit();
  382. handled = TRUE;
  383. break;
  384. case KEY_RIGHT:
  385. setCurSliderValue(getCurSliderValue() + getIncrement());
  386. onCommit();
  387. handled = TRUE;
  388. break;
  389. default:
  390. break;
  391. }
  392. return handled;
  393. }
  394. void LLMultiSlider::draw()
  395. {
  396. static LLUICachedControl<S32> extra_triangle_height ("UIExtraTriangleHeight", 0);
  397. static LLUICachedControl<S32> extra_triangle_width ("UIExtraTriangleWidth", 0);
  398. LLColor4 curThumbColor;
  399. std::map<std::string, LLRect>::iterator mIt;
  400. std::map<std::string, LLRect>::iterator curSldrIt;
  401. // Draw background and thumb.
  402. // drawing solids requires texturing be disabled
  403. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  404. LLRect rect(mDragStartThumbRect);
  405. F32 opacity = getEnabled() ? 1.f : 0.3f;
  406. // Track
  407. LLUIImagePtr thumb_imagep = LLUI::getUIImage("Rounded_Square");
  408. static LLUICachedControl<S32> multi_track_height ("UIMultiTrackHeight", 0);
  409. S32 height_offset = (getRect().getHeight() - multi_track_height) / 2;
  410. LLRect track_rect(0, getRect().getHeight() - height_offset, getRect().getWidth(), height_offset );
  411. if(mDrawTrack)
  412. {
  413. track_rect.stretch(-1);
  414. thumb_imagep->draw(track_rect, mTrackColor.get() % opacity);
  415. }
  416. // if we're supposed to use a drawn triangle
  417. // simple gl call for the triangle
  418. if(mUseTriangle) {
  419. for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
  420. gl_triangle_2d(
  421. mIt->second.mLeft - extra_triangle_width, 
  422. mIt->second.mTop + extra_triangle_height,
  423. mIt->second.mRight + extra_triangle_width, 
  424. mIt->second.mTop + extra_triangle_height,
  425. mIt->second.mLeft + mIt->second.getWidth() / 2, 
  426. mIt->second.mBottom - extra_triangle_height,
  427. mTriangleColor.get(), TRUE);
  428. }
  429. }
  430. else if (!thumb_imagep)
  431. {
  432. // draw all the thumbs
  433. curSldrIt = mThumbRects.end();
  434. for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) {
  435. // choose the color
  436. curThumbColor = mThumbCenterColor.get();
  437. if(mIt->first == mCurSlider) {
  438. curSldrIt = mIt;
  439. continue;
  440. //curThumbColor = mThumbCenterSelectedColor;
  441. }
  442. // the draw command
  443. gl_rect_2d(mIt->second, curThumbColor, TRUE);
  444. }
  445. // now draw the current slider
  446. if(curSldrIt != mThumbRects.end()) {
  447. gl_rect_2d(curSldrIt->second, mThumbCenterSelectedColor.get(), TRUE);
  448. }
  449. // and draw the drag start
  450. if (gFocusMgr.getMouseCapture() == this)
  451. {
  452. gl_rect_2d(mDragStartThumbRect, mThumbCenterColor.get() % opacity, FALSE);
  453. }
  454. }
  455. else if( gFocusMgr.getMouseCapture() == this )
  456. {
  457. // draw drag start
  458. thumb_imagep->drawSolid(mDragStartThumbRect, mThumbCenterColor.get() % 0.3f);
  459. // draw the highlight
  460. if (hasFocus())
  461. {
  462. thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
  463. }
  464. // draw the thumbs
  465. curSldrIt = mThumbRects.end();
  466. for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) 
  467. {
  468. // choose the color
  469. curThumbColor = mThumbCenterColor.get();
  470. if(mIt->first == mCurSlider) 
  471. {
  472. // don't draw now, draw last
  473. curSldrIt = mIt;
  474. continue;
  475. }
  476. // the draw command
  477. thumb_imagep->drawSolid(mIt->second, curThumbColor);
  478. }
  479. // draw cur slider last
  480. if(curSldrIt != mThumbRects.end()) 
  481. {
  482. thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get());
  483. }
  484. }
  485. else
  486. // draw highlight
  487. if (hasFocus())
  488. {
  489. thumb_imagep->drawBorder(mThumbRects[mCurSlider], gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth());
  490. }
  491. // draw thumbs
  492. curSldrIt = mThumbRects.end();
  493. for(mIt = mThumbRects.begin(); mIt != mThumbRects.end(); mIt++) 
  494. {
  495. // choose the color
  496. curThumbColor = mThumbCenterColor.get();
  497. if(mIt->first == mCurSlider) 
  498. {
  499. curSldrIt = mIt;
  500. continue;
  501. //curThumbColor = mThumbCenterSelectedColor;
  502. }
  503. thumb_imagep->drawSolid(mIt->second, curThumbColor % opacity);
  504. }
  505. if(curSldrIt != mThumbRects.end()) 
  506. {
  507. thumb_imagep->drawSolid(curSldrIt->second, mThumbCenterSelectedColor.get() % opacity);
  508. }
  509. }
  510. LLF32UICtrl::draw();
  511. }
  512. boost::signals2::connection LLMultiSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb ) 
  513. if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
  514. return mMouseDownSignal->connect(cb); 
  515. }
  516. boost::signals2::connection LLMultiSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb )   
  517. if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
  518. return mMouseUpSignal->connect(cb); 
  519. }