/indra/llui/llslider.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 384 lines · 294 code · 51 blank · 39 comment · 38 complexity · 97a0e6e13dcfa227c5440a8a7c016ce0 MD5 · raw file

  1. /**
  2. * @file llslider.cpp
  3. * @brief LLSlider base class
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "llslider.h"
  28. #include "llui.h"
  29. #include "llgl.h"
  30. #include "llwindow.h"
  31. #include "llfocusmgr.h"
  32. #include "llkeyboard.h" // for the MASK constants
  33. #include "llcontrol.h"
  34. #include "lluictrlfactory.h"
  35. static LLDefaultChildRegistry::Register<LLSlider> r1("slider_bar");
  36. //FIXME: make this into an unregistered template so that code constructed sliders don't
  37. // have ambigious template lookup problem
  38. LLSlider::Params::Params()
  39. : orientation ("orientation", std::string ("horizontal")),
  40. track_color("track_color"),
  41. thumb_outline_color("thumb_outline_color"),
  42. thumb_center_color("thumb_center_color"),
  43. thumb_image("thumb_image"),
  44. thumb_image_pressed("thumb_image_pressed"),
  45. thumb_image_disabled("thumb_image_disabled"),
  46. track_image_horizontal("track_image_horizontal"),
  47. track_image_vertical("track_image_vertical"),
  48. track_highlight_horizontal_image("track_highlight_horizontal_image"),
  49. track_highlight_vertical_image("track_highlight_vertical_image"),
  50. mouse_down_callback("mouse_down_callback"),
  51. mouse_up_callback("mouse_up_callback")
  52. {}
  53. LLSlider::LLSlider(const LLSlider::Params& p)
  54. : LLF32UICtrl(p),
  55. mMouseOffset( 0 ),
  56. mOrientation ((p.orientation() == "horizontal") ? HORIZONTAL : VERTICAL),
  57. mTrackColor(p.track_color()),
  58. mThumbOutlineColor(p.thumb_outline_color()),
  59. mThumbCenterColor(p.thumb_center_color()),
  60. mThumbImage(p.thumb_image),
  61. mThumbImagePressed(p.thumb_image_pressed),
  62. mThumbImageDisabled(p.thumb_image_disabled),
  63. mTrackImageHorizontal(p.track_image_horizontal),
  64. mTrackImageVertical(p.track_image_vertical),
  65. mTrackHighlightHorizontalImage(p.track_highlight_horizontal_image),
  66. mTrackHighlightVerticalImage(p.track_highlight_vertical_image),
  67. mMouseDownSignal(NULL),
  68. mMouseUpSignal(NULL)
  69. {
  70. mViewModel->setValue(p.initial_value);
  71. updateThumbRect();
  72. mDragStartThumbRect = mThumbRect;
  73. setControlName(p.control_name, NULL);
  74. setValue(getValueF32());
  75. if (p.mouse_down_callback.isProvided())
  76. {
  77. setMouseDownCallback(initCommitCallback(p.mouse_down_callback));
  78. }
  79. if (p.mouse_up_callback.isProvided())
  80. {
  81. setMouseUpCallback(initCommitCallback(p.mouse_up_callback));
  82. }
  83. }
  84. LLSlider::~LLSlider()
  85. {
  86. delete mMouseDownSignal;
  87. delete mMouseUpSignal;
  88. }
  89. void LLSlider::setValue(F32 value, BOOL from_event)
  90. {
  91. value = llclamp( value, mMinValue, mMaxValue );
  92. // Round to nearest increment (bias towards rounding down)
  93. value -= mMinValue;
  94. value += mIncrement/2.0001f;
  95. value -= fmod(value, mIncrement);
  96. value += mMinValue;
  97. if (!from_event && getValueF32() != value)
  98. {
  99. setControlValue(value);
  100. }
  101. LLF32UICtrl::setValue(value);
  102. updateThumbRect();
  103. }
  104. void LLSlider::updateThumbRect()
  105. {
  106. const S32 DEFAULT_THUMB_SIZE = 16;
  107. F32 t = (getValueF32() - mMinValue) / (mMaxValue - mMinValue);
  108. S32 thumb_width = mThumbImage ? mThumbImage->getWidth() : DEFAULT_THUMB_SIZE;
  109. S32 thumb_height = mThumbImage ? mThumbImage->getHeight() : DEFAULT_THUMB_SIZE;
  110. if ( mOrientation == HORIZONTAL )
  111. {
  112. S32 left_edge = (thumb_width / 2);
  113. S32 right_edge = getRect().getWidth() - (thumb_width / 2);
  114. S32 x = left_edge + S32( t * (right_edge - left_edge) );
  115. mThumbRect.mLeft = x - (thumb_width / 2);
  116. mThumbRect.mRight = mThumbRect.mLeft + thumb_width;
  117. mThumbRect.mBottom = getLocalRect().getCenterY() - (thumb_height / 2);
  118. mThumbRect.mTop = mThumbRect.mBottom + thumb_height;
  119. }
  120. else
  121. {
  122. S32 top_edge = (thumb_height / 2);
  123. S32 bottom_edge = getRect().getHeight() - (thumb_height / 2);
  124. S32 y = top_edge + S32( t * (bottom_edge - top_edge) );
  125. mThumbRect.mLeft = getLocalRect().getCenterX() - (thumb_width / 2);
  126. mThumbRect.mRight = mThumbRect.mLeft + thumb_width;
  127. mThumbRect.mBottom = y - (thumb_height / 2);
  128. mThumbRect.mTop = mThumbRect.mBottom + thumb_height;
  129. }
  130. }
  131. void LLSlider::setValueAndCommit(F32 value)
  132. {
  133. F32 old_value = getValueF32();
  134. setValue(value);
  135. if (getValueF32() != old_value)
  136. {
  137. onCommit();
  138. }
  139. }
  140. BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask)
  141. {
  142. if( hasMouseCapture() )
  143. {
  144. if ( mOrientation == HORIZONTAL )
  145. {
  146. S32 thumb_half_width = mThumbImage->getWidth()/2;
  147. S32 left_edge = thumb_half_width;
  148. S32 right_edge = getRect().getWidth() - (thumb_half_width);
  149. x += mMouseOffset;
  150. x = llclamp( x, left_edge, right_edge );
  151. F32 t = F32(x - left_edge) / (right_edge - left_edge);
  152. setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
  153. }
  154. else // mOrientation == VERTICAL
  155. {
  156. S32 thumb_half_height = mThumbImage->getHeight()/2;
  157. S32 top_edge = thumb_half_height;
  158. S32 bottom_edge = getRect().getHeight() - (thumb_half_height);
  159. y += mMouseOffset;
  160. y = llclamp(y, top_edge, bottom_edge);
  161. F32 t = F32(y - top_edge) / (bottom_edge - top_edge);
  162. setValueAndCommit(t * (mMaxValue - mMinValue) + mMinValue );
  163. }
  164. getWindow()->setCursor(UI_CURSOR_ARROW);
  165. lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
  166. }
  167. else
  168. {
  169. getWindow()->setCursor(UI_CURSOR_ARROW);
  170. lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
  171. }
  172. return TRUE;
  173. }
  174. BOOL LLSlider::handleMouseUp(S32 x, S32 y, MASK mask)
  175. {
  176. BOOL handled = FALSE;
  177. if( hasMouseCapture() )
  178. {
  179. gFocusMgr.setMouseCapture( NULL );
  180. if (mMouseUpSignal)
  181. (*mMouseUpSignal)( this, getValueF32() );
  182. handled = TRUE;
  183. make_ui_sound("UISndClickRelease");
  184. }
  185. else
  186. {
  187. handled = TRUE;
  188. }
  189. return handled;
  190. }
  191. BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask)
  192. {
  193. // only do sticky-focus on non-chrome widgets
  194. if (!getIsChrome())
  195. {
  196. setFocus(TRUE);
  197. }
  198. if (mMouseDownSignal)
  199. (*mMouseDownSignal)( this, getValueF32() );
  200. if (MASK_CONTROL & mask) // if CTRL is modifying
  201. {
  202. setValueAndCommit(mInitialValue);
  203. }
  204. else
  205. {
  206. // Find the offset of the actual mouse location from the center of the thumb.
  207. if (mThumbRect.pointInRect(x,y))
  208. {
  209. mMouseOffset = (mOrientation == HORIZONTAL)
  210. ? (mThumbRect.mLeft + mThumbImage->getWidth()/2) - x
  211. : (mThumbRect.mBottom + mThumbImage->getHeight()/2) - y;
  212. }
  213. else
  214. {
  215. mMouseOffset = 0;
  216. }
  217. // Start dragging the thumb
  218. // No handler needed for focus lost since this class has no state that depends on it.
  219. gFocusMgr.setMouseCapture( this );
  220. mDragStartThumbRect = mThumbRect;
  221. }
  222. make_ui_sound("UISndClick");
  223. return TRUE;
  224. }
  225. BOOL LLSlider::handleKeyHere(KEY key, MASK mask)
  226. {
  227. BOOL handled = FALSE;
  228. switch(key)
  229. {
  230. case KEY_DOWN:
  231. case KEY_LEFT:
  232. setValueAndCommit(getValueF32() - getIncrement());
  233. handled = TRUE;
  234. break;
  235. case KEY_UP:
  236. case KEY_RIGHT:
  237. setValueAndCommit(getValueF32() + getIncrement());
  238. handled = TRUE;
  239. break;
  240. default:
  241. break;
  242. }
  243. return handled;
  244. }
  245. BOOL LLSlider::handleScrollWheel(S32 x, S32 y, S32 clicks)
  246. {
  247. if ( mOrientation == VERTICAL )
  248. {
  249. F32 new_val = getValueF32() - clicks * getIncrement();
  250. setValueAndCommit(new_val);
  251. return TRUE;
  252. }
  253. return LLF32UICtrl::handleScrollWheel(x,y,clicks);
  254. }
  255. void LLSlider::draw()
  256. {
  257. F32 alpha = getDrawContext().mAlpha;
  258. // since thumb image might still be decoding, need thumb to accomodate image size
  259. updateThumbRect();
  260. // Draw background and thumb.
  261. // drawing solids requires texturing be disabled
  262. gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);
  263. // Track
  264. LLPointer<LLUIImage>& trackImage = ( mOrientation == HORIZONTAL )
  265. ? mTrackImageHorizontal
  266. : mTrackImageVertical;
  267. LLPointer<LLUIImage>& trackHighlightImage = ( mOrientation == HORIZONTAL )
  268. ? mTrackHighlightHorizontalImage
  269. : mTrackHighlightVerticalImage;
  270. LLRect track_rect;
  271. LLRect highlight_rect;
  272. if ( mOrientation == HORIZONTAL )
  273. {
  274. track_rect.set(mThumbImage->getWidth() / 2,
  275. getLocalRect().getCenterY() + (trackImage->getHeight() / 2),
  276. getRect().getWidth() - mThumbImage->getWidth() / 2,
  277. getLocalRect().getCenterY() - (trackImage->getHeight() / 2) );
  278. highlight_rect.set(track_rect.mLeft, track_rect.mTop, mThumbRect.getCenterX(), track_rect.mBottom);
  279. }
  280. else
  281. {
  282. track_rect.set(getLocalRect().getCenterX() - (trackImage->getWidth() / 2),
  283. getRect().getHeight(),
  284. getLocalRect().getCenterX() + (trackImage->getWidth() / 2),
  285. 0);
  286. highlight_rect.set(track_rect.mLeft, track_rect.mTop, track_rect.mRight, track_rect.mBottom);
  287. }
  288. trackImage->draw(track_rect, LLColor4::white % alpha);
  289. trackHighlightImage->draw(highlight_rect, LLColor4::white % alpha);
  290. // Thumb
  291. if (hasFocus())
  292. {
  293. // Draw focus highlighting.
  294. mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor() % alpha, gFocusMgr.getFocusFlashWidth());
  295. }
  296. if( hasMouseCapture() ) // currently clicking on slider
  297. {
  298. // Show ghost where thumb was before dragging began.
  299. if (mThumbImage.notNull())
  300. {
  301. mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor.get() % (0.3f * alpha));
  302. }
  303. if (mThumbImagePressed.notNull())
  304. {
  305. mThumbImagePressed->draw(mThumbRect, mThumbOutlineColor % alpha);
  306. }
  307. }
  308. else if (!isInEnabledChain())
  309. {
  310. if (mThumbImageDisabled.notNull())
  311. {
  312. mThumbImageDisabled->draw(mThumbRect, mThumbCenterColor % alpha);
  313. }
  314. }
  315. else
  316. {
  317. if (mThumbImage.notNull())
  318. {
  319. mThumbImage->draw(mThumbRect, mThumbCenterColor % alpha);
  320. }
  321. }
  322. LLUICtrl::draw();
  323. }
  324. boost::signals2::connection LLSlider::setMouseDownCallback( const commit_signal_t::slot_type& cb )
  325. {
  326. if (!mMouseDownSignal) mMouseDownSignal = new commit_signal_t();
  327. return mMouseDownSignal->connect(cb);
  328. }
  329. boost::signals2::connection LLSlider::setMouseUpCallback( const commit_signal_t::slot_type& cb )
  330. {
  331. if (!mMouseUpSignal) mMouseUpSignal = new commit_signal_t();
  332. return mMouseUpSignal->connect(cb);
  333. }