PageRenderTime 5300ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 1ms

/client/lib/haxegui/controls/ScrollBar.hx

http://github.com/ericmuyser/mmo
Haxe | 583 lines | 284 code | 121 blank | 178 comment | 34 complexity | 04bb41e610888564bf723c562ef473de MD5 | raw file
  1. // Copyright (c) 2009 The haxegui developers
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of
  4. // this software and associated documentation files (the "Software"), to deal in
  5. // the Software without restriction, including without limitation the rights to
  6. // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  7. // the Software, and to permit persons to whom the Software is furnished to do so,
  8. // subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in all
  11. // copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  15. // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  16. // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  17. // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  18. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. package haxegui.controls;
  20. //{{{ Import
  21. import feffects.Tween;
  22. import flash.display.DisplayObject;
  23. import flash.display.DisplayObjectContainer;
  24. import flash.display.Sprite;
  25. import flash.events.Event;
  26. import flash.events.MouseEvent;
  27. import flash.filters.BitmapFilter;
  28. import flash.filters.BitmapFilterQuality;
  29. import flash.filters.DropShadowFilter;
  30. import flash.geom.Point;
  31. import flash.geom.Rectangle;
  32. import flash.geom.Transform;
  33. import flash.text.TextField;
  34. import haxegui.controls.AbstractButton;
  35. import haxegui.controls.Component;
  36. import haxegui.controls.IAdjustable;
  37. import haxegui.events.DragEvent;
  38. import haxegui.events.MoveEvent;
  39. import haxegui.events.ResizeEvent;
  40. import haxegui.managers.CursorManager;
  41. import haxegui.managers.StyleManager;
  42. import haxegui.toys.Socket;
  43. import haxegui.utils.Color;
  44. import haxegui.utils.Opts;
  45. import haxegui.utils.Size;
  46. //}}}
  47. using haxegui.controls.Component;
  48. //{{{ ScrollBarUpButton
  49. /**
  50. *
  51. * Button for scrolling up.<br/>
  52. *
  53. * @version 0.1
  54. * @author Omer Goshen <gershon@goosemoose.com>
  55. * @author Russell Weir <damonsbane@gmail.com>
  56. */
  57. class ScrollBarUpButton extends AbstractButton, implements IAggregate {
  58. //{{{ init
  59. override public function init(opts:Dynamic=null) {
  60. box.width = parent.asComponent().box.width;
  61. box.height = 20;
  62. minSize = new Size(15, 20);
  63. super.init(opts);
  64. color = parent.asComponent().color;
  65. }
  66. //}}}
  67. //{{{ __init__
  68. static function __init__() {
  69. haxegui.Haxegui.register(ScrollBarUpButton);
  70. }
  71. //}}}
  72. }
  73. //}}}
  74. //{{{ ScrollBarDownButton
  75. /**
  76. *
  77. * Button for scrolling down.<br/>
  78. *
  79. * @version 0.1
  80. * @author Omer Goshen <gershon@goosemoose.com>
  81. * @author Russell Weir <damonsbane@gmail.com>
  82. */
  83. class ScrollBarDownButton extends AbstractButton, implements IAggregate {
  84. //{{{ init
  85. override public function init(opts:Dynamic=null) {
  86. box.height = 20;
  87. box.width = parent.asComponent().box.width;
  88. minSize = new Size(15, 20);
  89. super.init(opts);
  90. color = parent.asComponent().color;
  91. }
  92. //}}}
  93. //{{{ __init__
  94. static function __init__() {
  95. haxegui.Haxegui.register(ScrollBarDownButton);
  96. }
  97. //}}}
  98. }
  99. //}}}
  100. //{{{ ScrollBarHandle
  101. /**
  102. *
  103. * Draggable handle for a [ScrollBar].<br/>
  104. *
  105. * @version 0.1
  106. * @author Omer Goshen <gershon@goosemoose.com>
  107. * @author Russell Weir <damonsbane@gmail.com>
  108. */
  109. class ScrollBarHandle extends AbstractButton, implements IComposite {
  110. //{{{ onResize
  111. override public function onResize(e:ResizeEvent) : Void {
  112. if(!visible) return;
  113. box.height = Math.max(20, box.height);
  114. box.height = Math.min(box.height, parent.asComponent().box.height - y);
  115. box.width = parent.asComponent().box.width;
  116. color = parent.asComponent().color;
  117. }
  118. //}}}
  119. //{{ onMouseMove
  120. public function onMouseMove(e:MouseEvent) {
  121. this.x = parent.x;
  122. }
  123. //}}}
  124. //{{{ __init__
  125. static function __init__() {
  126. haxegui.Haxegui.register(ScrollBarHandle);
  127. }
  128. //}}}
  129. }
  130. //}}}
  131. //{{{ ScrollBarFrame
  132. /**
  133. *
  134. * ScrollBarFrame Class
  135. *
  136. * @version 0.1
  137. * @author Omer Goshen <gershon@goosemoose.com>
  138. * @author Russell Weir <damonsbane@gmail.com>
  139. */
  140. class ScrollBarFrame extends Component, implements IComposite {
  141. //{{{ init
  142. override public function init(opts:Dynamic=null) {
  143. box = parent.asComponent().box.clone();
  144. super.init(opts);
  145. color = parent.asComponent().color;
  146. }
  147. //}}}
  148. //{{{ __init__
  149. static function __init__() {
  150. haxegui.Haxegui.register(ScrollBarFrame);
  151. }
  152. //}}}
  153. }
  154. //}}}
  155. //{{{ ScrollBar
  156. /**
  157. *
  158. * ScrollBar with handle and buttons.<br/>
  159. * <p></p>
  160. * <pre class="code haxe">
  161. * var scrollbar = new ScrollBar();
  162. * scrollbar.init();
  163. * </pre>
  164. *
  165. * <i>note: When parented to anything other than a [ScrollPane], it does not resize automatically.</i>
  166. *
  167. * @todo fix problems when adding extra buttons
  168. * @todo handle resizing to reflect content's height
  169. * @todo adjustment...
  170. *
  171. * @author Omer Goshen <gershon@goosemoose.com>
  172. * @author Russell Weir <damonsbane@gmail.com>
  173. * @version 0.1
  174. */
  175. class ScrollBar extends Component, implements IAdjustable {
  176. //{{{ Members
  177. public var frame : ScrollBarFrame;
  178. public var handle : ScrollBarHandle;
  179. public var up : ScrollBarUpButton;
  180. public var down : ScrollBarDownButton;
  181. /** The scroll target **/
  182. public var scrollee : Dynamic;
  183. /** Percent of scroll **/
  184. public var scroll : Float;
  185. /** the adjustment for this scrollbar **/
  186. public var adjustment : Adjustment;
  187. /** true for horizontal scrollbar **/
  188. public var horizontal : Bool;
  189. /** @todo socket **/
  190. public var slot : Socket;
  191. //}}}
  192. static var xml = Xml.parse('
  193. <haxegui:Layout name="ScrollBar">
  194. <haxegui:controls:ScrollBarFrame/>
  195. <haxegui:controls:ScrollBarHandle
  196. color="{parent.color}"
  197. disabled="{parent.disabled}"
  198. width="{parent.box.width}"
  199. height="40"
  200. horizontal="{parent.horizontal}"
  201. y="{20 + parent.scroll * (parent.box.height - 40)}"
  202. />
  203. <haxegui:controls:ScrollBarUpButton
  204. color="{parent.color}"
  205. disabled="{parent.disabled}"
  206. />
  207. <haxegui:controls:ScrollBarDownButton
  208. color="{parent.color}"
  209. disabled="{parent.disabled}"
  210. />
  211. </haxegui:Layout>
  212. ').firstElement();
  213. //{{{ Functions
  214. //{{{ init
  215. /**
  216. * @param opts.target Object to scroll, either a [DisplayObject] or [TextField]
  217. */
  218. override public function init(opts:Dynamic=null) {
  219. adjustment = new Adjustment({ value: .0, min: Math.NEGATIVE_INFINITY, max: Math.POSITIVE_INFINITY, step: 3, page: 40});
  220. box = new Size(20,80).toRect();
  221. color = DefaultStyle.BACKGROUND;
  222. horizontal = false;
  223. scroll = 0;
  224. scrollee = null;
  225. minSize = new Size(15, 40);
  226. super.init(opts);
  227. // adjustment.object.value = Opts.optFloat(opts, "value", adjustment.object.value);
  228. // adjustment.object.min = Opts.optFloat(opts, "min", adjustment.object.min);
  229. // adjustment.object.max = Opts.optFloat(opts, "max", adjustment.object.max);
  230. // adjustment.object.step = Opts.optFloat(opts, "step", adjustment.object.step);
  231. // adjustment.object.page = Opts.optFloat(opts, "page", adjustment.object.page);
  232. description = null;
  233. scroll = Opts.optFloat(opts, "scroll", scroll);
  234. xml.set("name", name);
  235. haxegui.XmlParser.apply(ScrollBar.xml, this);
  236. // horizontal scrollbar
  237. horizontal = Opts.optBool(opts, "horizontal", horizontal);
  238. if(horizontal)
  239. rotation = -90;
  240. // Silently notify only when target is missing
  241. try {
  242. scrollee = Opts.classInstance(opts, "target", untyped [TextField, DisplayObject]);
  243. if(Std.is(scrollee, TextField))
  244. scrollee.addEventListener(Event.SCROLL, onTextFieldScrolled, false, 0, true);
  245. else
  246. if(Std.is(scrollee, DisplayObject))
  247. scrollee.addEventListener(ResizeEvent.RESIZE, onTargetResized, false, 0, true);
  248. }
  249. catch(s:String) {
  250. trace(s);
  251. //~ var a = new haxegui.Alert();
  252. //~ a.init({label: this+"."+here.methodName+":\n\n\n"+s});
  253. }
  254. // frame
  255. // frame = new ScrollBarFrame(this);
  256. // frame.init({color: this.color});
  257. frame = this.getElementsByClass(ScrollBarFrame).next();
  258. frame.focusRect = false;
  259. frame.tabEnabled = false;
  260. frame.description = null;
  261. frame.filters = [new DropShadowFilter (4, 45, DefaultStyle.DROPSHADOW, 0.5, 8, 8, disabled ? .35 : .75, BitmapFilterQuality.HIGH, true, false, false)];
  262. // handle
  263. // handle = new ScrollBarHandle(this);
  264. handle = this.getElementsByClass(ScrollBarHandle).next();
  265. // handle.init({
  266. // color: this.color,
  267. // disabled: this.disabled,
  268. // width: this.box.width,
  269. // height : 40,
  270. // horizontal: this.horizontal,
  271. // y: 20 + scroll * (box.height - 40 - 40),
  272. // });
  273. handle.filters = [new DropShadowFilter (0, 0, DefaultStyle.DROPSHADOW, 0.75, horizontal ? 8 : 0, horizontal ? 0 : 8, disabled ? .35 : .75, BitmapFilterQuality.LOW, false, false, false)];
  274. // up button
  275. up = this.getElementsByClass(ScrollBarUpButton).next();
  276. // down button
  277. down = this.getElementsByClass(ScrollBarDownButton).next();
  278. down.move(0, box.height - 20);
  279. // external listeners
  280. frame.addEventListener(MoveEvent.MOVE, onFrameMoved, false, 0, true);
  281. frame.addEventListener(ResizeEvent.RESIZE, onFrameResized, false, 0, true);
  282. parent.addEventListener(ResizeEvent.RESIZE, onParentResize, false, 0, true);
  283. // listeners
  284. addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel, false, 0, true);
  285. adjust();
  286. }
  287. //}}}
  288. //{{{ onTextFieldScrolled
  289. public function onTextFieldScrolled(e:Event) {
  290. if(disabled) return;
  291. handle.updatePositionTween();
  292. scroll = scrollee.scrollV / scrollee.maxScrollV;
  293. var rowHeight = 1 / scrollee.maxScrollV;
  294. handle.box.height = Math.max(20, box.height*rowHeight);
  295. handle.visible = handle.box.height < (box.height-20);
  296. handle.dirty = true;
  297. var d = scroll*(box.height-handle.box.height-20) - handle.y;
  298. if(handle.y+d<20) d = 20 - handle.y;
  299. // if(scroll==0 && handle.y>20) d = 20-handle.y;
  300. handle.updatePositionTween( new Tween( 0, 1, 2000, feffects.easing.Expo.easeOut ), new Point(0, d));
  301. // adjust();
  302. }
  303. //}}}
  304. //{{{ onTargetResized
  305. public function onTargetResized(e:ResizeEvent) {
  306. var h = e.target.scrollRect.height / e.target.transform.pixelBounds.height;
  307. // var h = e.target.scrollRect.height/e.target.height;
  308. if(horizontal)
  309. h = e.target.scrollRect.width / e.target.transform.pixelBounds.width;
  310. // h = e.target.scrollRect.width/e.target.width;
  311. handle.box.height = Math.max(20, box.height * h);
  312. handle.visible = handle.box.height < (box.height-20);
  313. if(handle.visible)
  314. handle.dirty = true;
  315. // handle.visible = !(handle.box.height >= box.height);
  316. }
  317. //}}}
  318. //{{{ onFrameMoved
  319. private function onFrameMoved(e:MoveEvent) {
  320. // move(e.target.x, e.target.y);
  321. x = e.target.x;
  322. y = e.target.y;
  323. e.target.x = 0;
  324. e.target.y = 0;
  325. }
  326. //}}}
  327. //{{{ onFrameResized
  328. private function onFrameResized(e:ResizeEvent) {
  329. box = frame.box.clone();
  330. handle.box.width = box.width;
  331. handle.y = Math.max( 20, Math.min( handle.y, this.box.height - handle.box.height - 20));
  332. if(handle.visible)
  333. handle.dirty = true;
  334. up.box.width = box.width;
  335. up.dirty = true;
  336. down.box.width = box.width;
  337. down.y = Math.max( 20, box.height - 20);
  338. down.dirty = true;
  339. }
  340. //}}}
  341. //{{{ onParentResize
  342. /**
  343. * When parented to a [ScrollPane] scrollbars with stretch to fit.
  344. */
  345. public function onParentResize(e:ResizeEvent) {
  346. // if(!Std.is(parent, haxegui.containers.ScrollPane)) return;
  347. if(!Std.is(parent, haxegui.containers.ScrollPane) && !Std.is(parent, haxegui.controls.UiList)) return;
  348. if(Std.is(parent, Component))
  349. if(horizontal) {
  350. this.y = parent.asComponent().box.height + 20;
  351. box.height = parent.asComponent().box.width;
  352. }
  353. else {
  354. box.height = parent.asComponent().box.height;
  355. this.x = parent.asComponent().box.width ;
  356. }
  357. dispatchEvent(new ResizeEvent(ResizeEvent.RESIZE));
  358. }
  359. //}}}
  360. //{{{ onResize
  361. public override function onResize(e:ResizeEvent) {
  362. frame.dirty = true;
  363. frame.box = box.clone();
  364. handle.y = Math.max( 20, Math.min( handle.y, this.box.height - handle.box.height - 20));
  365. handle.dirty = true;
  366. down.y = Math.max( 20, box.height - 20);
  367. dirty = true;
  368. }
  369. //}}}
  370. //{{{ onMouseWheel
  371. public override function onMouseWheel(e:MouseEvent) {
  372. if(disabled) return;
  373. var self = this;
  374. var handleMotionTween = new Tween( 0, 1, 2000, feffects.easing.Expo.easeOut );
  375. var offset = e.delta * (horizontal ? 1 : -1)* adjustment.object.page;
  376. handle.updatePositionTween( handleMotionTween, new Point(0, offset), function(v) { self.adjust(); } );
  377. // handle.updatePositionTween( handleMotionTween, new Point(0, offset));
  378. // adjust();
  379. super.onMouseWheel(e);
  380. }
  381. //}}}
  382. //{{{ onMouseMove
  383. public function onMouseMove(e:MouseEvent) {
  384. //~ if(e.buttonDown)
  385. adjust();
  386. }
  387. //}}}
  388. //{{{ adjust
  389. /**
  390. * Adjust the scroll on the bar.
  391. */
  392. public function adjust(?v:Float) : Float {
  393. if(scrollee==null) return scroll;
  394. if(handle==null || handle.visible==false) return scroll;
  395. haxegui.Profiler.begin(here.className.split(".").pop()+"."+here.methodName);
  396. if(v<0 || scroll<0 || handle.y < 20) {
  397. adjustment.setValue(scroll=0);
  398. handle.updatePositionTween();
  399. handle.x = 0;
  400. handle.y = 20;
  401. haxegui.Profiler.end();
  402. return scroll;
  403. }
  404. if(v>1 || scroll>1 || handle.y > (box.height - handle.box.height - 20)) {
  405. adjustment.setValue(scroll=1);
  406. handle.updatePositionTween();
  407. handle.x = 0;
  408. handle.y = box.height - handle.box.height - 20;
  409. haxegui.Profiler.end();
  410. return scroll;
  411. }
  412. // handle textfields
  413. if(Std.is(scrollee, TextField)) {
  414. if(scrollee.hasEventListener(Event.SCROLL))
  415. scrollee.removeEventListener(Event.SCROLL, onTextFieldScrolled);
  416. scroll = (handle.y-20) / (frame.height - handle.height + 2) ;
  417. if(horizontal)
  418. scrollee.scrollH = (scrollee.maxScrollH) * scroll;
  419. else
  420. scrollee.scrollV = (scrollee.maxScrollV+2) * scroll;
  421. if(!scrollee.hasEventListener(Event.SCROLL))
  422. scrollee.addEventListener(Event.SCROLL, onTextFieldScrolled, false, 0, true);
  423. if(!horizontal)
  424. if( scrollee.scrollV==1 && scrollee.bottomScrollV >= scrollee.numLines ) {
  425. // if(scrollee.scrollV==1) {
  426. handle.box.height = box.height-20;
  427. handle.visible = false;
  428. scroll=0;
  429. }
  430. haxegui.Profiler.end();
  431. return scroll;
  432. }
  433. if(scrollee.scrollRect==null || scrollee.scrollRect.isEmpty()) {
  434. haxegui.Profiler.end();
  435. return scroll;
  436. }
  437. var rect = scrollee.scrollRect.clone();
  438. var transform = scrollee.transform;
  439. var bounds = transform.pixelBounds.clone();
  440. scroll = (handle.y-20) / (frame.height - handle.height + 2) ;
  441. if(horizontal)
  442. rect.x = ( bounds.width - rect.width ) * scroll ;
  443. else
  444. rect.y = ( bounds.height - rect.height ) * scroll ;
  445. scrollee.scrollRect = rect;
  446. // dispatchEvent(new Event(Event.CHANGE));
  447. haxegui.Profiler.end();
  448. return scroll;
  449. }
  450. //}}}
  451. public override function destroy() {
  452. frame.removeEventListener(MoveEvent.MOVE, onFrameMoved);
  453. super.destroy();
  454. }
  455. //{{{ __init__
  456. static function __init__() {
  457. haxegui.Haxegui.register(ScrollBar);
  458. }
  459. //}}}
  460. //}}}
  461. }
  462. //}}}