PageRenderTime 106ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/client/lib/haxegui/controls/Tree.hx

http://github.com/ericmuyser/mmo
Haxe | 689 lines | 368 code | 146 blank | 175 comment | 49 complexity | 6fd89dbdaba33e3a05bfbc76e5ac43b3 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. //{{{ Imports
  21. import flash.display.DisplayObject;
  22. import flash.display.DisplayObjectContainer;
  23. import flash.display.Sprite;
  24. import flash.events.Event;
  25. import flash.events.MouseEvent;
  26. import flash.geom.Rectangle;
  27. import haxegui.DataSource;
  28. import haxegui.controls.Component;
  29. import haxegui.controls.Expander;
  30. import haxegui.controls.Image;
  31. import haxegui.events.DragEvent;
  32. import haxegui.events.ResizeEvent;
  33. import haxegui.events.TreeEvent;
  34. import haxegui.managers.CursorManager;
  35. import haxegui.managers.DragManager;
  36. import haxegui.managers.StyleManager;
  37. import haxegui.utils.Color;
  38. import haxegui.utils.Opts;
  39. import haxegui.utils.Size;
  40. import haxegui.XmlParser;
  41. //}}}
  42. using haxegui.controls.Component;
  43. //{{{ TreeLeaf
  44. /**
  45. *
  46. * TreeLeaf class
  47. *
  48. *
  49. *
  50. * @author Omer Goshen <gershon@goosemoose.com>
  51. * @author Russell Weir <damonsbane@gmail.com>
  52. * @version 0.1
  53. */
  54. class TreeLeaf extends Component, implements IAggregate, implements IData {
  55. /** Leaf icon **/
  56. public var icon : Icon;
  57. /** Defaults to the name property **/
  58. public var label : Label;
  59. public var data : Dynamic;
  60. public var selected : Bool;
  61. static var styleXml = Xml.parse('
  62. <haxegui:Style name="TreeLeaf">
  63. <haxegui:controls:TreeLeaf>
  64. <events>
  65. <script type="text/hscript" action="mouseClick"><![CDATA[
  66. this.selected = !this.selected;
  67. this.color = this.selected ? DefaultStyle.FOCUS : DefaultStyle.INPUT_BACK;
  68. this.redraw();
  69. return;
  70. ]]>
  71. </script>
  72. </events>
  73. </haxegui:controls:TreeLeaf>
  74. </haxegui:Style>
  75. ').firstElement();
  76. static var layoutXml = Xml.parse('
  77. <haxegui:Layout name="TreeLeaf">
  78. <haxegui:controls:Icon x="22" y="1"/>
  79. <haxegui:controls:Label x="24" y="3" text="{parent.name}"/>
  80. </haxegui:Layout>
  81. ').firstElement();
  82. //{{{ init
  83. override public function init(opts:Dynamic=null) {
  84. box = new Size(140,24).toRect();
  85. color = DefaultStyle.INPUT_BACK;
  86. // icon = null;
  87. super.init(opts);
  88. layoutXml.firstElement().set("src", Opts.optString(opts, "icon", Icon.STOCK_DOCUMENT));
  89. layoutXml.set("name", name);
  90. XmlParser.apply(TreeLeaf.layoutXml, this);
  91. XmlParser.apply(TreeLeaf.styleXml, true);
  92. // label = new Label(this);
  93. // label.init({text: this.name});
  94. // label.move(48, 4);
  95. label = getElementsByClass(Label).next();
  96. label.setText(Opts.optString(opts, "label", name));
  97. // icon = new Icon(this, 24, 4);
  98. // icon.init ({src: Opts.optString(opts, "icon", Icon.STOCK_DOCUMENT) });
  99. }
  100. //}}}
  101. //{{{ onMouseClick
  102. public override function onMouseClick(e:MouseEvent) {
  103. if(disabled) return;
  104. e.stopImmediatePropagation();
  105. super.onMouseClick(e);
  106. }
  107. //}}}
  108. //{{{ getPath
  109. public function getPath() : Array<Dynamic>{
  110. var nodes : Array<Dynamic> = [this.label.getText()];
  111. for(a in ancestors()) {
  112. if(Std.is(a, Tree)) break;
  113. if(Std.is(a, TreeNode))
  114. nodes.push(untyped a.expander.label.getText());
  115. }
  116. nodes.reverse();
  117. return nodes;
  118. }
  119. //}}}
  120. //{{{ __init__
  121. static function __init__() {
  122. haxegui.Haxegui.register(TreeLeaf);
  123. }
  124. //}}}
  125. }
  126. //}}}
  127. //{{{ TreeNode
  128. /**
  129. * Expandable tree node<br/>
  130. *
  131. *
  132. * @version 0.1
  133. * @author Omer Goshen <gershon@goosemoose.com>
  134. * @author Russell Weir <damonsbane@gmail.com>
  135. */
  136. class TreeNode extends Component, implements IAggregate, implements IData {
  137. public var expander : Expander;
  138. public var expanded : Bool;
  139. public var selected : Bool;
  140. public var selectable : Bool;
  141. public var data : Dynamic;
  142. static var layoutXml = Xml.parse('
  143. <haxegui:Layout name="TreeNode">
  144. <haxegui:controls:Expander label="{parent.name}" style="arrow_and_icon" expanded="false"/>
  145. </haxegui:Layout>
  146. ').firstElement();
  147. static var styleXml = Xml.parse('
  148. <haxegui:Style name="TreeNode">
  149. <haxegui:controls:TreeNode>
  150. <events>
  151. <script type="text/hscript" action="mouseClick"><![CDATA[
  152. event.stopImmediatePropagation();
  153. if(event.target==this) {
  154. this.selected = !this.selected;
  155. this.color = this.selected ? DefaultStyle.FOCUS : DefaultStyle.INPUT_BACK;
  156. this.redraw();
  157. return;
  158. }
  159. this.expander.__setExpanded(!this.expander.__getExpanded());
  160. if(this.expander.expanded)
  161. this.expand();
  162. else
  163. this.collapse();
  164. for(i in 2...this.expander.numChildren)
  165. this.expander.getChildAt(i).visible = this.expander.expanded;
  166. // this.redraw();
  167. // for(a in this.ancestors())
  168. // if(Reflect.hasField(a, "redraw"))
  169. // a.redraw();
  170. ]]>
  171. </script>
  172. </events>
  173. </haxegui:controls:TreeNode>
  174. </haxegui:Style>
  175. ').firstElement();
  176. //{{{ init
  177. override public function init(opts:Dynamic=null) {
  178. haxegui.Profiler.begin(here.className.split(".").pop()+"."+here.methodName);
  179. color = DefaultStyle.INPUT_BACK;
  180. box = new Size(140, 24).toRect();
  181. super.init(opts);
  182. layoutXml.set("name", name);
  183. XmlParser.apply(TreeNode.styleXml, true);
  184. XmlParser.apply(TreeNode.layoutXml, this);
  185. expander = cast firstChild();
  186. expander.label.mouseEnabled = true;
  187. expander.removeEventListener(MouseEvent.CLICK, expander.onMouseClick);
  188. expander.button.arrow.resize(new Size(8,8));
  189. expander.label.setText(Opts.optString(opts, "label", name));
  190. parent.addEventListener(ResizeEvent.RESIZE, onParentResize, false, 0, true);
  191. haxegui.Profiler.end();
  192. }
  193. //}}}
  194. //{{{ empty
  195. public function empty() {
  196. return expander.numChildren<=2;
  197. }
  198. //}}}
  199. //{{{ removeItems
  200. public function removeItems() {
  201. for(i in expander)
  202. if(!Std.is(i, Label) && !Std.is(i, ExpanderButton))
  203. // expander.removeChild(i);
  204. i.asComponent().destroy();
  205. // expander.expanded = false;
  206. }
  207. ///}}}
  208. //{{{ addChild
  209. public override function addChild(o:DisplayObject) : DisplayObject {
  210. if(expander==null) return super.addChild(o);
  211. var c = expander.addChild(o);
  212. if(Std.is(c, TreeNode)) {
  213. c.addEventListener(TreeEvent.ITEM_OPENING, onChildNodeExpand, false, 0, true);
  214. }
  215. return c;
  216. }
  217. //}}}
  218. //{{{ onChildNodeExpand
  219. public function onChildNodeExpand(e:TreeEvent) {
  220. var i = parent.getChildIndex(this) + 1;
  221. if(parent.numChildren<i) return;
  222. var v = expander.getVisibleChildren().length;
  223. if(expander.label!=null) v--;
  224. if(expander.button!=null) v--;
  225. var h = 24*v;
  226. for(j in i...parent.numChildren) {
  227. parent.getChildAt(j).y += (e.type==TreeEvent.ITEM_OPENING?1:-1)*h;
  228. }
  229. parentTree(this).box.height += (e.type==TreeEvent.ITEM_OPENING?1:-1)*h;
  230. parentTree(this).dirty = true;
  231. // for(a in ancestors())
  232. // if(Std.is(a, TreeNode)) { a.dispatchEvent(e); break; }
  233. }
  234. //}}}
  235. //{{{ getChildIndex
  236. public override function getChildIndex(o:DisplayObject) : Int {
  237. if(expander==null) return super.getChildIndex(o);
  238. return expander.getChildIndex(o);
  239. // return super.addChild(o);
  240. }
  241. //}}}
  242. //{{{ onParentResize
  243. public function onParentResize(e:ResizeEvent) : Void {
  244. if(parent==null || parent.asComponent().box==null) return;
  245. box.width = parent.asComponent().box.width;
  246. expander.box = box.clone();
  247. for(i in expander) {
  248. i.asComponent().box.width = box.width;
  249. i.asComponent().dirty=true;
  250. }
  251. dirty = true;
  252. }
  253. //}}}
  254. //{{{ expand
  255. public function expand() {
  256. var h = 24*(this.expander.numChildren-2);
  257. var i = parent.getChildIndex(this) + 1;
  258. for(j in i...parent.numChildren)
  259. parent.getChildAt(j).y += h;
  260. dispatchEvent(new TreeEvent(TreeEvent.ITEM_OPENING));
  261. this.parent.dispatchEvent(new ResizeEvent(ResizeEvent.RESIZE));
  262. }
  263. //}}}
  264. //{{{ collapse
  265. public function collapse() {
  266. var i = parent.getChildIndex(this) + 1;
  267. for(j in i...parent.numChildren)
  268. parent.getChildAt(j).y = 24*j - 24;
  269. dispatchEvent(new TreeEvent(TreeEvent.ITEM_CLOSING));
  270. this.parent.dispatchEvent(new ResizeEvent(ResizeEvent.RESIZE));
  271. }
  272. //}}}
  273. //{{{ getPath
  274. public function getPath() : Array<Dynamic>{
  275. var nodes : Array<Dynamic> = [this.expander.label.getText()];
  276. for(a in ancestors()) {
  277. if(Std.is(a, Tree)) break;
  278. if(Std.is(a, TreeNode))
  279. nodes.push(untyped a.expander.label.getText());
  280. }
  281. nodes.reverse();
  282. return nodes;
  283. }
  284. //}}}
  285. //{{{ parentTree
  286. public static function parentTree(node:TreeNode) : Tree {
  287. for(a in node.ancestors())
  288. if(Std.is(a, Tree)) return cast a;
  289. return null;
  290. }
  291. //}}}
  292. //{{{ __init__
  293. static function __init__() {
  294. haxegui.Haxegui.register(TreeNode);
  295. }
  296. //}}}
  297. }
  298. //}}}
  299. //{{{ Tree
  300. /**
  301. *
  302. * Tree Class
  303. *
  304. * @version 0.2
  305. * @author Omer Goshen <gershon@goosemoose.com>
  306. * @author Russell Weir <damonsbane@gmail.com>
  307. *
  308. */
  309. class Tree extends Component, implements IDataSource {
  310. public var dataSource(default, setDataSource) : DataSource;
  311. /** A list of selected items on the tree **/
  312. public var selected : List<DisplayObject>;
  313. public var expandedNodes : List<TreeNode>;
  314. public var collapsedNodes : List<TreeNode>;
  315. public var rootNode : TreeNode;
  316. /**
  317. * Set to [true] to show the root node
  318. * @todo showRoot
  319. */
  320. public var showRoot : Bool;
  321. //{{{ Functions
  322. //{{{ init
  323. override public function init(?opts:Dynamic=null) {
  324. box = new Size(140,24).toRect();
  325. color = DefaultStyle.INPUT_BACK;
  326. collapsedNodes = new List<TreeNode>();
  327. expandedNodes = new List<TreeNode>();
  328. selected = new List<DisplayObject>();
  329. super.init(opts);
  330. description = null;
  331. rootNode = new TreeNode(this, "rootNode");
  332. rootNode.init({width: box.width, color: this.color });
  333. rootNode.description = null;
  334. rootNode.expander.label.setText(Opts.optString(opts, "text", rootNode.expander.label.getText()));
  335. // var self = this;
  336. // addEventListener(ResizeEvent.RESIZE, function(e){ trace(e); self.redraw(); });
  337. parent.addEventListener(ResizeEvent.RESIZE, onParentResize, false, 0, true);
  338. }
  339. //}}}
  340. public override function addChild(o:DisplayObject) : DisplayObject {
  341. var c = super.addChild(o);
  342. if(Std.is(c, TreeNode)) {
  343. if((cast c).expanded) expandedNodes.add(cast c);
  344. else collapsedNodes.add(cast c);
  345. }
  346. return c;
  347. }
  348. public function count() : Int {
  349. return 0;
  350. }
  351. //{{{ redraw
  352. public override function redraw(opts:Dynamic=null) {
  353. // background
  354. this.graphics.clear();
  355. this.graphics.beginFill(this.color);
  356. this.graphics.drawRect(0, 0, box.width, box.height);
  357. this.graphics.endFill();
  358. for(i in 0...Std.int(box.height/20)) {
  359. this.graphics.beginFill( (i&1==0) ? this.color : Color.darken(this.color, 10));
  360. this.graphics.drawRect(0, 24*i, box.width, 24);
  361. this.graphics.endFill();
  362. }
  363. // this.graphics.lineStyle(1, Color.darken(DefaultStyle.BACKGROUND,30), 1);
  364. // this.graphics.moveTo(24, 12);
  365. // this.graphics.lineTo(24, 24*this.rootNode.expander.numChildren);
  366. // for(i in 0...this.rootNode.expander.numChildren) {
  367. // this.graphics.moveTo(24, 24*i-12);
  368. // this.graphics.lineTo(48, 24*i-12);
  369. // }
  370. super.redraw(opts);
  371. }
  372. //}}}
  373. //{{{
  374. public override function onRollOver(e:MouseEvent) {
  375. // redraw();
  376. // if(e.target!=this && this.contains(e.target)) {
  377. // var _y = e.target.localToGlobal(new flash.geom.Point()).y - localToGlobal(new flash.geom.Point()).y;
  378. // var i = Math.round(_y / 24);
  379. // this.graphics.beginFill( (i&1==0) ? DefaultStyle.FOCUS : Color.darken(DefaultStyle.FOCUS, 10));
  380. // this.graphics.drawRect(0, 24*i, box.width, 24);
  381. // this.graphics.endFill();
  382. // }
  383. super.onRollOver(e);
  384. }
  385. //}}}
  386. //{{{ process
  387. public function process(o:Dynamic, ?node:Dynamic=null) {
  388. haxegui.Profiler.begin(here.className.split(".").pop()+"."+here.methodName);
  389. if(o==null) return;
  390. if(node==null) node=this;
  391. for(f in Reflect.fields(o)) {
  392. if(Reflect.isObject(Reflect.field(o, f))) {
  393. if(Std.is(Reflect.field(o, f), String) || Reflect.fields(Reflect.field(o,f)).length==0 ) {
  394. var leaf = new TreeLeaf(node, f);
  395. leaf.init({
  396. x: x+12,
  397. y: 24*(node.getChildIndex(leaf)-1),
  398. width: box.width-(node.x),
  399. height: 24,
  400. visible: false,
  401. color: this.color
  402. });
  403. }
  404. else {
  405. var treenode = new TreeNode(node, f);
  406. treenode.init({
  407. width: box.width,
  408. height: 24,
  409. x: x+12,
  410. y: 24*(node.getChildIndex(treenode)-1),
  411. visible: false,
  412. color: this.color
  413. });
  414. process(Reflect.field(o,f), treenode);
  415. }
  416. }
  417. }
  418. haxegui.Profiler.end();
  419. }
  420. //}}}
  421. //{{{ processXml
  422. public function processXml(xml:Xml, ?node:Dynamic=null) {
  423. if(xml==null) return;
  424. if(node==null) node=this.rootNode;
  425. for(e in xml.elements()) {
  426. var fast = new haxe.xml.Fast(e);
  427. if(e.firstElement()==null) {
  428. var leaf = new TreeLeaf(node);
  429. leaf.init({
  430. x: x + 12,
  431. y: 24 * (node.getChildIndex(leaf)-1),
  432. width: box.width-(node.x),
  433. height: 24,
  434. visible: false,
  435. label:
  436. // (e.attributes().hasNext() && e.exists("name")) ? e.get("name") :
  437. // (e.exists("id")) ? e.get("id") : e.nodeName,
  438. fast.has.name ? fast.att.name : fast.has.id ? fast.att.id : fast.name,
  439. color: this.color
  440. });
  441. }
  442. else {
  443. var treenode = new TreeNode(node);
  444. treenode.init({
  445. width: box.width,
  446. height: 24,
  447. x: x+12,
  448. y: 24*(node.getChildIndex(treenode)-1),
  449. visible: false,
  450. // label: (e.attributes().hasNext() && e.exists("name")) ? e.get("name") : e.nodeName,
  451. label: fast.has.name ? fast.att.name : fast.has.id ? fast.att.id : fast.name,
  452. color: this.color
  453. });
  454. processXml(e, treenode);
  455. }
  456. }
  457. }
  458. //}}}
  459. //{{{ onParentResize
  460. public function onParentResize(e:ResizeEvent) : Void {
  461. box = parent.asComponent().box.clone();
  462. // if(parent.parent!=null) {
  463. // box.width = (cast parent.parent).box.width - x;
  464. // box.height = (cast parent.parent).box.height - y;
  465. // }
  466. // for(a in ancestors()) {
  467. // if(Std.is(a, haxegui.containers.Divider))
  468. // box.width -= 10;
  469. // }
  470. // redraw();
  471. for(i in this) {
  472. i.asComponent().box.width = box.width;
  473. i.asComponent().dirty=true;
  474. }
  475. dirty = true;
  476. dispatchEvent(e);
  477. }
  478. //}}}
  479. //{{{ onResize
  480. public override function onResize(e:ResizeEvent) : Void {
  481. redraw();
  482. }
  483. //}}}
  484. //{{{ expandNode
  485. public function expandNode(node:TreeNode) {
  486. node.expander.expanded = true;
  487. node.expand();
  488. for(i in 2...node.expander.numChildren)
  489. node.expander.getChildAt(i).visible = true;
  490. for(n in node.expander.getElementsByClass(TreeNode))
  491. expandNode(n);
  492. // box = transform.pixelBounds.clone();
  493. dirty = true;
  494. dispatchEvent(new ResizeEvent(ResizeEvent.RESIZE));
  495. }
  496. //}}}
  497. //{{{ expandFull
  498. public function expandFull() {
  499. expandNode(rootNode);
  500. box = getBounds(this);
  501. dirty = true;
  502. }
  503. //}}}
  504. //{{{ visibleRowCount
  505. public function visibleRowCount() : Int {
  506. // var count = function(o) { if(o==null) return; var i=o.numChildren; for(j in o) i+=count(j); return i; };
  507. return 0;
  508. }
  509. //}}}
  510. //{{{ totalRowCount
  511. public function totalRowCount() : Int {
  512. // return 0;
  513. return countNode(rootNode);
  514. }
  515. //}}}
  516. //{{{ countNode
  517. public function countNode(n:TreeNode) : Int {
  518. if(n==null) return 0;
  519. var i = n.expander.numChildren;
  520. for(c in n.expander)
  521. if(Std.is(c, TreeNode)) i+=countNode(cast c);
  522. return i;
  523. }
  524. //}}}
  525. //{{{ setDataSource
  526. public function setDataSource(d:DataSource) : DataSource {
  527. dataSource = d;
  528. // dataSource.addEventListener(Event.CHANGE, onData, false, 0, true);
  529. // var l = 0;
  530. // if(dataSource.data!=null)
  531. // l = dataSource.data.length;
  532. // dispatchEvent(new ListEvent(ListEvent.CHANGE, new IntIter(0, l)));
  533. return dataSource;
  534. }
  535. //}}}
  536. //{{{ __init__
  537. static function __init__() {
  538. haxegui.Haxegui.register(Tree);
  539. }
  540. //}}}
  541. //}}}
  542. }
  543. //}}}