1 module treemapwidget;
2 
3 import tm = treemap;
4 import std.file;
5 import std.variant;
6 import std.path;
7 import std.stdio;
8 import std.algorithm;
9 import std.range;
10 import dlangui;
11 import std.datetime;
12 
13 Rect toUiRect(tm.Rect r) {
14   return Rect(cast(int)r.left(),
15               cast(int)r.top(),
16               cast(int)r.right(),
17               cast(int)r.bottom());
18 }
19 
20 string humanize(ulong v) {
21   auto units = ["", "k", "m", "g"];
22   int idx = 0;
23   while (v / 1024 > 0) {
24     idx++;
25     v /= 1024;
26   }
27   return v.to!string ~ units[idx];
28 }
29 
30 class TreeMapWidget(Node) : Widget {
31   alias NodeTreeMap = tm.TreeMap!Node;
32   alias Maybe = NodeTreeMap.Maybe;
33   NodeTreeMap treeMap;
34   interface OnTreeMapHandler {
35     void onTreeMap(Maybe node);
36   }
37 
38 
39   Maybe lastSelected;
40   MouseEvent lastMouseEvent;
41   int depth;
42   Node[] lastNodes;
43   int delta = -10;
44   Node rootNode;
45   void up() {
46     if ((lastNodes != null) && (lastNodes.length >= 1)) {
47       auto node = lastNodes[$-1];
48       lastNodes = lastNodes[0..$-1];
49       treeMap = new tm.TreeMap!Node(node, delta);
50       treeMap.layout(tm.Rect(0, 0, pos.width, pos.height), depth);
51       invalidate();
52     }
53   }
54 
55   void changeDepth(int delta, Widget w) {
56     this.depth += delta;
57     doRedraw(rootNode, w);
58     invalidate();
59   }
60 
61   void doRedraw(Node node, Widget w) {
62     treeMap = new tm.TreeMap!Node(node, delta);
63     treeMap.layout(tm.Rect(0, 0, w.pos.width, w.pos.height), depth);
64     invalidate();
65   }
66 
67   this(string id, Node rootNode, int depth=3) {
68     super(id);
69     this.treeMap = new tm.TreeMap!Node(rootNode, delta);
70     this.depth = depth;
71     this.rootNode = rootNode;
72     clickable = true;
73     focusable = true;
74 
75     click.connect(
76       delegate(Widget w) {
77         auto r = treeMap.findFor(lastMouseEvent.pos.x, lastMouseEvent.pos.y);
78         r.visit!(
79           (Node node) {
80             if (node.childs != null) {
81               lastNodes ~= treeMap.rootNode;
82               doRedraw(node, w);
83             } else {
84             }
85           },
86           (typeof(null)) {}
87         )();
88         return true;
89       }
90     );
91 
92     keyEvent.connect(
93       delegate(Widget source, KeyEvent event) {
94         //writeln("KeyEvent: ", event.text, ", ", event.keyCode, ", ", event.action);
95         //writeln("KEY_ADD: ", KeyCode.KEY_ADD.to!int);
96         //writeln("KEY_UP: ", KeyAction.KeyUp.to!int);
97         if ((event.keyCode == 8) && (event.action == KeyAction.KeyUp)) {
98           up();
99           return true;
100         }
101         
102         if ((event.keyCode == 65579) && (event.action == KeyAction.KeyUp)) {
103           changeDepth(+1, source);
104           return true;
105         }
106 
107         if ((event.keyCode == 65581) && (event.action == KeyAction.KeyUp)) {
108           changeDepth(-1, source);
109           return true;
110         }
111 
112         return false;
113       }
114     );
115 
116     mouseEvent.connect(
117       delegate(Widget w, MouseEvent me) {
118         lastMouseEvent = me;
119         auto r = treeMap.findFor(me.pos.x, me.pos.y);
120         if (r != lastSelected) {
121           lastSelected = r;
122           onTreeMapFocused(lastSelected);
123           return true;
124         }
125         return false;
126       });
127   }
128 
129   public Signal!OnTreeMapHandler onTreeMapFocused;
130   public auto addTreeMapFocusedListener(void delegate (Maybe) listener) {
131     onTreeMapFocused.connect(listener);
132     return this;
133   }
134 
135   override void layout(Rect r) {
136     StopWatch sw;
137     sw.start();
138     treeMap.layout(tm.Rect(0, 0, r.width, r.height), depth);
139     sw.stop();
140     super.layout(r);
141   }
142 
143   override void onDraw(DrawBuf buf) {
144     super.onDraw(buf);
145     if (visibility != Visibility.Visible) {
146       return;
147     }
148 
149     auto rc = _pos;
150     auto saver = ClipRectSaver(buf, rc);
151 
152     auto font = FontManager.instance.getFont(25, FontWeight.Normal, false, FontFamily.SansSerif, "Arial");
153 
154     drawNode(treeMap.rootNode, buf, 0);
155   }
156   private void drawNode(Node n, DrawBuf buf, int depth) {
157     auto r = treeMap.get(n);
158     if (r) {
159       auto uiRect = (*r).toUiRect();
160       if (!uiRect.empty()) {
161         buf.drawFrame(uiRect, 0xff00ff, Rect(1, 1, 1, 1), 0xa0a0a0);
162         foreach (child; n.childs) {
163           drawNode(child, buf, depth+1);
164         }
165       }
166     }
167   }
168 }