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 }