PageRenderTime 74ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/MinimalComps/src/com/bit101/components/RangeSlider.as

https://bitbucket.org/ign/ormtree
ActionScript | 498 lines | 347 code | 40 blank | 111 comment | 40 complexity | da818ad0c1d6926be806d1f30e9d85be MD5 | raw file
  1. /**
  2. * RangeSlider.as
  3. * Keith Peters
  4. * version 0.9.10
  5. *
  6. * Abstract base class for HRangeSlider and VRangeSlider.
  7. *
  8. * Copyright (c) 2011 Keith Peters
  9. *
  10. * Permission is hereby granted, free of charge, to any person obtaining a copy
  11. * of this software and associated documentation files (the "Software"), to deal
  12. * in the Software without restriction, including without limitation the rights
  13. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  14. * copies of the Software, and to permit persons to whom the Software is
  15. * furnished to do so, subject to the following conditions:
  16. *
  17. * The above copyright notice and this permission notice shall be included in
  18. * all copies or substantial portions of the Software.
  19. *
  20. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  23. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  24. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  25. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  26. * THE SOFTWARE.
  27. */
  28. package com.bit101.components
  29. {
  30. import flash.display.DisplayObjectContainer;
  31. import flash.display.Sprite;
  32. import flash.events.Event;
  33. import flash.events.MouseEvent;
  34. import flash.geom.Rectangle;
  35. [Event(name="change", type="flash.events.Event")]
  36. public class RangeSlider extends Component
  37. {
  38. protected var _back:Sprite;
  39. protected var _highLabel:Label;
  40. protected var _highValue:Number = 100;
  41. protected var _labelMode:String = ALWAYS;
  42. protected var _labelPosition:String;
  43. protected var _labelPrecision:int = 0;
  44. protected var _lowLabel:Label;
  45. protected var _lowValue:Number = 0;
  46. protected var _maximum:Number = 100;
  47. protected var _maxHandle:Sprite;
  48. protected var _minimum:Number = 0;
  49. protected var _minHandle:Sprite;
  50. protected var _orientation:String = VERTICAL;
  51. protected var _tick:Number = 1;
  52. public static const ALWAYS:String = "always";
  53. public static const BOTTOM:String = "bottom";
  54. public static const HORIZONTAL:String = "horizontal";
  55. public static const LEFT:String = "left";
  56. public static const MOVE:String = "move";
  57. public static const NEVER:String = "never";
  58. public static const RIGHT:String = "right";
  59. public static const TOP:String = "top";
  60. public static const VERTICAL:String = "vertical";
  61. /**
  62. * Constructor
  63. * @param orientation Whether the slider will be horizontal or vertical.
  64. * @param parent The parent DisplayObjectContainer on which to add this Slider.
  65. * @param xpos The x position to place this component.
  66. * @param ypos The y position to place this component.
  67. * @param defaultHandler The event handling function to handle the default event for this component (change in this case).
  68. */
  69. public function RangeSlider(orientation:String, parent:DisplayObjectContainer=null, xpos:Number=0, ypos:Number=0, defaultHandler:Function = null)
  70. {
  71. _orientation = orientation;
  72. super(parent, xpos, ypos);
  73. if(defaultHandler != null)
  74. {
  75. addEventListener(Event.CHANGE, defaultHandler);
  76. }
  77. }
  78. /**
  79. * Initializes the component.
  80. */
  81. protected override function init():void
  82. {
  83. super.init();
  84. if(_orientation == HORIZONTAL)
  85. {
  86. setSize(110, 10);
  87. _labelPosition = TOP;
  88. }
  89. else
  90. {
  91. setSize(10, 110);
  92. _labelPosition = RIGHT;
  93. }
  94. }
  95. /**
  96. * Creates and adds the child display objects of this component.
  97. */
  98. protected override function addChildren():void
  99. {
  100. super.addChildren();
  101. _back = new Sprite();
  102. _back.filters = [getShadow(2, true)];
  103. addChild(_back);
  104. _minHandle = new Sprite();
  105. _minHandle.filters = [getShadow(1)];
  106. _minHandle.addEventListener(MouseEvent.MOUSE_DOWN, onDragMin);
  107. _minHandle.buttonMode = true;
  108. _minHandle.useHandCursor = true;
  109. addChild(_minHandle);
  110. _maxHandle = new Sprite();
  111. _maxHandle.filters = [getShadow(1)];
  112. _maxHandle.addEventListener(MouseEvent.MOUSE_DOWN, onDragMax);
  113. _maxHandle.buttonMode = true;
  114. _maxHandle.useHandCursor = true;
  115. addChild(_maxHandle);
  116. _lowLabel = new Label(this);
  117. _highLabel = new Label(this);
  118. _lowLabel.visible = (_labelMode == ALWAYS);
  119. }
  120. /**
  121. * Draws the back of the slider.
  122. */
  123. protected function drawBack():void
  124. {
  125. _back.graphics.clear();
  126. _back.graphics.beginFill(Style.BACKGROUND);
  127. _back.graphics.drawRect(0, 0, _width, _height);
  128. _back.graphics.endFill();
  129. }
  130. /**
  131. * Draws the handles of the slider.
  132. */
  133. protected function drawHandles():void
  134. {
  135. _minHandle.graphics.clear();
  136. _minHandle.graphics.beginFill(Style.BUTTON_FACE);
  137. _maxHandle.graphics.clear();
  138. _maxHandle.graphics.beginFill(Style.BUTTON_FACE);
  139. if(_orientation == HORIZONTAL)
  140. {
  141. _minHandle.graphics.drawRect(1, 1, _height - 2, _height - 2);
  142. _maxHandle.graphics.drawRect(1, 1, _height - 2, _height - 2);
  143. }
  144. else
  145. {
  146. _minHandle.graphics.drawRect(1, 1, _width - 2, _width - 2);
  147. _maxHandle.graphics.drawRect(1, 1, _width - 2, _width - 2);
  148. }
  149. _minHandle.graphics.endFill();
  150. positionHandles();
  151. }
  152. /**
  153. * Adjusts positions of handles when value, maximum or minimum have changed.
  154. * TODO: Should also be called when slider is resized.
  155. */
  156. protected function positionHandles():void
  157. {
  158. var range:Number;
  159. if(_orientation == HORIZONTAL)
  160. {
  161. range = _width - _height * 2;
  162. _minHandle.x = (_lowValue - _minimum) / (_maximum - _minimum) * range;
  163. _maxHandle.x = _height + (_highValue - _minimum) / (_maximum - _minimum) * range;
  164. }
  165. else
  166. {
  167. range = _height - _width * 2;
  168. _minHandle.y = _height - _width - (_lowValue - _minimum) / (_maximum - _minimum) * range;
  169. _maxHandle.y = _height - _width * 2 - (_highValue - _minimum) / (_maximum - _minimum) * range;
  170. }
  171. updateLabels();
  172. }
  173. /**
  174. * Sets the text and positions the labels.
  175. */
  176. protected function updateLabels():void
  177. {
  178. _lowLabel.text = getLabelForValue(lowValue);
  179. _highLabel.text = getLabelForValue(highValue);
  180. _lowLabel.draw();
  181. _highLabel.draw();
  182. if(_orientation == VERTICAL)
  183. {
  184. _lowLabel.y = _minHandle.y + (_width - _lowLabel.height) * 0.5;
  185. _highLabel.y = _maxHandle.y + (_width - _highLabel.height) * 0.5;
  186. if(_labelPosition == LEFT)
  187. {
  188. _lowLabel.x = -_lowLabel.width - 5;
  189. _highLabel.x = -_highLabel.width - 5;
  190. }
  191. else
  192. {
  193. _lowLabel.x = _width + 5;
  194. _highLabel.x = _width + 5;
  195. }
  196. }
  197. else
  198. {
  199. _lowLabel.x = _minHandle.x - _lowLabel.width + _height;
  200. _highLabel.x = _maxHandle.x;
  201. if(_labelPosition == BOTTOM)
  202. {
  203. _lowLabel.y = _height + 2;
  204. _highLabel.y = _height + 2;
  205. }
  206. else
  207. {
  208. _lowLabel.y = -_lowLabel.height;
  209. _highLabel.y = -_highLabel.height;
  210. }
  211. }
  212. }
  213. /**
  214. * Generates a label string for the given value.
  215. * @param value The number to create a label for.
  216. */
  217. protected function getLabelForValue(value:Number):String
  218. {
  219. var str:String = (Math.round(value * Math.pow(10, _labelPrecision)) / Math.pow(10, _labelPrecision)).toString();
  220. if(_labelPrecision > 0)
  221. {
  222. var decimal:String = str.split(".")[1] || "";
  223. if(decimal.length == 0) str += ".";
  224. for(var i:int = decimal.length; i < _labelPrecision; i++)
  225. {
  226. str += "0";
  227. }
  228. }
  229. return str;
  230. }
  231. ///////////////////////////////////
  232. // public methods
  233. ///////////////////////////////////
  234. /**
  235. * Draws the visual ui of the component.
  236. */
  237. override public function draw():void
  238. {
  239. super.draw();
  240. drawBack();
  241. drawHandles();
  242. }
  243. ///////////////////////////////////
  244. // event handlers
  245. ///////////////////////////////////
  246. /**
  247. * Internal mouseDown handler for the low value handle. Starts dragging the handle.
  248. * @param event The MouseEvent passed by the system.
  249. */
  250. protected function onDragMin(event:MouseEvent):void
  251. {
  252. stage.addEventListener(MouseEvent.MOUSE_UP, onDrop);
  253. stage.addEventListener(MouseEvent.MOUSE_MOVE, onMinSlide);
  254. if(_orientation == HORIZONTAL)
  255. {
  256. _minHandle.startDrag(false, new Rectangle(0, 0, _maxHandle.x - _height, 0));
  257. }
  258. else
  259. {
  260. _minHandle.startDrag(false, new Rectangle(0, _maxHandle.y + _width, 0, _height - _maxHandle.y - _width * 2));
  261. }
  262. if(_labelMode == MOVE)
  263. {
  264. _lowLabel.visible = true;
  265. _highLabel.visible = true;
  266. }
  267. }
  268. /**
  269. * Internal mouseDown handler for the high value handle. Starts dragging the handle.
  270. * @param event The MouseEvent passed by the system.
  271. */
  272. protected function onDragMax(event:MouseEvent):void
  273. {
  274. stage.addEventListener(MouseEvent.MOUSE_UP, onDrop);
  275. stage.addEventListener(MouseEvent.MOUSE_MOVE, onMaxSlide);
  276. if(_orientation == HORIZONTAL)
  277. {
  278. _maxHandle.startDrag(false, new Rectangle(_minHandle.x + _height, 0, _width - _height - _minHandle.x - _height, 0));
  279. }
  280. else
  281. {
  282. _maxHandle.startDrag(false, new Rectangle(0, 0, 0, _minHandle.y - _width));
  283. }
  284. if(_labelMode == MOVE)
  285. {
  286. _lowLabel.visible = true;
  287. _highLabel.visible = true;
  288. }
  289. }
  290. /**
  291. * Internal mouseUp handler. Stops dragging the handle.
  292. * @param event The MouseEvent passed by the system.
  293. */
  294. protected function onDrop(event:MouseEvent):void
  295. {
  296. stage.removeEventListener(MouseEvent.MOUSE_UP, onDrop);
  297. stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMinSlide);
  298. stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMaxSlide);
  299. stopDrag();
  300. if(_labelMode == MOVE)
  301. {
  302. _lowLabel.visible = false;
  303. _highLabel.visible = false;
  304. }
  305. }
  306. /**
  307. * Internal mouseMove handler for when the low value handle is being moved.
  308. * @param event The MouseEvent passed by the system.
  309. */
  310. protected function onMinSlide(event:MouseEvent):void
  311. {
  312. var oldValue:Number = _lowValue;
  313. if(_orientation == HORIZONTAL)
  314. {
  315. _lowValue = _minHandle.x / (_width - _height * 2) * (_maximum - _minimum) + _minimum;
  316. }
  317. else
  318. {
  319. _lowValue = (_height - _width - _minHandle.y) / (height - _width * 2) * (_maximum - _minimum) + _minimum;
  320. }
  321. if(_lowValue != oldValue)
  322. {
  323. dispatchEvent(new Event(Event.CHANGE));
  324. }
  325. updateLabels();
  326. }
  327. /**
  328. * Internal mouseMove handler for when the high value handle is being moved.
  329. * @param event The MouseEvent passed by the system.
  330. */
  331. protected function onMaxSlide(event:MouseEvent):void
  332. {
  333. var oldValue:Number = _highValue;
  334. if(_orientation == HORIZONTAL)
  335. {
  336. _highValue = (_maxHandle.x - _height) / (_width - _height * 2) * (_maximum - _minimum) + _minimum;
  337. }
  338. else
  339. {
  340. _highValue = (_height - _width * 2 - _maxHandle.y) / (_height - _width * 2) * (_maximum - _minimum) + _minimum;
  341. }
  342. if(_highValue != oldValue)
  343. {
  344. dispatchEvent(new Event(Event.CHANGE));
  345. }
  346. updateLabels();
  347. }
  348. /**
  349. * Gets / sets the minimum value of the slider.
  350. */
  351. public function set minimum(value:Number):void
  352. {
  353. _minimum = value;
  354. _maximum = Math.max(_maximum, _minimum);
  355. _lowValue = Math.max(_lowValue, _minimum);
  356. _highValue = Math.max(_highValue, _minimum);
  357. positionHandles();
  358. }
  359. public function get minimum():Number
  360. {
  361. return _minimum;
  362. }
  363. /**
  364. * Gets / sets the maximum value of the slider.
  365. */
  366. public function set maximum(value:Number):void
  367. {
  368. _maximum = value;
  369. _minimum = Math.min(_minimum, _maximum);
  370. _lowValue = Math.min(_lowValue, _maximum);
  371. _highValue = Math.min(_highValue, _maximum);
  372. positionHandles();
  373. }
  374. public function get maximum():Number
  375. {
  376. return _maximum;
  377. }
  378. /**
  379. * Gets / sets the low value of this slider.
  380. */
  381. public function set lowValue(value:Number):void
  382. {
  383. _lowValue = value;
  384. _lowValue = Math.min(_lowValue, _highValue);
  385. _lowValue = Math.max(_lowValue, _minimum);
  386. positionHandles();
  387. dispatchEvent(new Event(Event.CHANGE));
  388. }
  389. public function get lowValue():Number
  390. {
  391. return Math.round(_lowValue / _tick) * _tick;
  392. }
  393. /**
  394. * Gets / sets the high value for this slider.
  395. */
  396. public function set highValue(value:Number):void
  397. {
  398. _highValue = value;
  399. _highValue = Math.max(_highValue, _lowValue);
  400. _highValue = Math.min(_highValue, _maximum);
  401. positionHandles();
  402. dispatchEvent(new Event(Event.CHANGE));
  403. }
  404. public function get highValue():Number
  405. {
  406. return Math.round(_highValue / _tick) * _tick;
  407. }
  408. /**
  409. * Sets / gets when the labels will appear. Can be "never", "move", or "always"
  410. */
  411. public function set labelMode(value:String):void
  412. {
  413. _labelMode = value;
  414. _highLabel.visible = (_labelMode == ALWAYS);
  415. _lowLabel.visible = (_labelMode == ALWAYS);
  416. }
  417. public function get labelMode():String
  418. {
  419. return _labelMode;
  420. }
  421. /**
  422. * Sets / gets where the labels will appear. "left" or "right" for vertical sliders, "top" or "bottom" for horizontal.
  423. */
  424. public function set labelPosition(value:String):void
  425. {
  426. _labelPosition = value;
  427. updateLabels();
  428. }
  429. public function get labelPosition():String
  430. {
  431. return _labelPosition;
  432. }
  433. /**
  434. * Sets / gets how many decimal points of precisions will be displayed on the labels.
  435. */
  436. public function set labelPrecision(value:int):void
  437. {
  438. _labelPrecision = value;
  439. updateLabels();
  440. }
  441. public function get labelPrecision():int
  442. {
  443. return _labelPrecision;
  444. }
  445. /**
  446. * Gets / sets the tick value of this slider. This round the value to the nearest multiple of this number.
  447. */
  448. public function set tick(value:Number):void
  449. {
  450. _tick = value;
  451. updateLabels();
  452. }
  453. public function get tick():Number
  454. {
  455. return _tick;
  456. }
  457. }
  458. }