1 module dlangide.ui.dmdprofilerview; 2 3 import dlangui.widgets.layouts; 4 import dlangui.widgets.widget; 5 import dlangui.widgets.grid; 6 import dlangui.widgets.scroll; 7 import dlangui.widgets.controls; 8 import dlangide.ui.frame; 9 import dlangide.ui.commands; 10 import dlangui.core.i18n; 11 import dlangide.tools.d.dmdtrace; 12 13 class DMDProfilerView : WidgetGroupDefaultDrawing { 14 protected IDEFrame _frame; 15 protected DMDTraceLogParser _data; 16 protected TraceFunctionList _fullFunctionList; 17 this(string ID, IDEFrame frame, DMDTraceLogParser data) { 18 super(ID); 19 _frame = frame; 20 _data = data; 21 layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); 22 _fullFunctionList = new TraceFunctionList("FULL_FUNCTION_LIST", "All functions"d, _data.nodesByTotalTime, _data.ticks_per_second); // new TextWidget(null, "DMD profiler view"d); 23 addChild(_fullFunctionList); 24 } 25 /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). 26 override void layout(Rect rc) { 27 super.layout(rc); 28 applyMargins(rc); 29 applyPadding(rc); 30 Rect rc1 = rc; 31 rc1.right = rc1.left + rc.width / 2; 32 _fullFunctionList.layout(rc1); 33 } 34 /** 35 Measure widget according to desired width and height constraints. (Step 1 of two phase layout). 36 37 */ 38 override void measure(int parentWidth, int parentHeight) { 39 _fullFunctionList.measure(parentWidth, parentHeight); 40 measuredContent(parentWidth, parentHeight, _fullFunctionList.measuredWidth, _fullFunctionList.measuredHeight); 41 } 42 } 43 44 class TraceFuncionGrid : StringGridWidgetBase { 45 protected FunctionNode[] _list; 46 protected dstring[] _colTitles; 47 protected ulong _ticksPerSecond; 48 this(string ID, FunctionNode[] list, ulong ticks_per_second) { 49 super(ID); 50 _ticksPerSecond = ticks_per_second; 51 _list = list; 52 layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); 53 fullColumnOnLeft(false); 54 fullRowOnTop(false); 55 resize(4, cast(int)list.length); 56 setColTitle(0, "Function name"d); 57 setColTitle(1, "Called"d); 58 setColTitle(2, "F us"d); 59 setColTitle(3, "F+D us"d); 60 showRowHeaders = false; 61 rowSelect = true; 62 minVisibleRows = 10; 63 minVisibleCols = 4; 64 } 65 66 private dchar[128] _numberFormatBuf; 67 dstring formatNumber(ulong v, dchar[] buffer) { 68 dchar[64] buf; 69 int k = 0; 70 if (!v) { 71 buf[k++] = '0'; 72 } else { 73 while (v) { 74 buf[k++] = '0' + (cast(int)(v % 10)); 75 v /= 10; 76 } 77 } 78 // reverse order 79 for (int i = 0; i < k; i++) 80 buffer[i] = buf[k - i - 1]; 81 return cast(dstring)buffer[0..k]; 82 } 83 dstring formatDurationTicks(ulong n) { 84 ulong v = n * 1000000 / _ticksPerSecond; 85 return formatNumber(v, _numberFormatBuf[]); 86 } 87 88 /// get cell text 89 override dstring cellText(int col, int row) { 90 import std.conv : to; 91 if (row < 0 || row >= _list.length) 92 return ""d; 93 FunctionNode entry = _list[row]; 94 switch (col) { 95 case 0: 96 string fn = entry.name; 97 if (fn.length > 256) 98 fn = fn[0..256] ~ "..."; 99 return fn.to!dstring; 100 case 1: 101 return formatNumber(entry.number_of_calls, _numberFormatBuf); 102 case 2: 103 return formatDurationTicks(entry.function_time); 104 case 3: 105 return formatDurationTicks(entry.function_and_descendant_time); 106 default: 107 return ""d; 108 } 109 } 110 /// set cell text 111 override StringGridWidgetBase setCellText(int col, int row, dstring text) { 112 // do nothing 113 return this; 114 } 115 /// returns row header title 116 override dstring rowTitle(int row) { 117 return ""d; 118 } 119 /// set row header title 120 override StringGridWidgetBase setRowTitle(int row, dstring title) { 121 return this; 122 } 123 124 /// returns row header title 125 override dstring colTitle(int col) { 126 return _colTitles[col]; 127 } 128 129 /// set col header title 130 override StringGridWidgetBase setColTitle(int col, dstring title) { 131 _colTitles[col] = title; 132 return this; 133 } 134 135 void autofit() { 136 autoFitColumnWidths(); 137 fillColumnWidth(0); 138 } 139 140 /// set new size 141 override void resize(int c, int r) { 142 if (c == cols && r == rows) 143 return; 144 int oldcols = cols; 145 int oldrows = rows; 146 super.resize(c, r); 147 _colTitles.length = c; 148 } 149 150 protected override Point measureCell(int x, int y) { 151 if (_customCellAdapter && _customCellAdapter.isCustomCell(x, y)) { 152 return _customCellAdapter.measureCell(x, y); 153 } 154 //Log.d("measureCell ", x, ", ", y); 155 FontRef fnt = font; 156 dstring txt; 157 if (x >= 0 && y >= 0) 158 txt = cellText(x, y); 159 else if (y < 0 && x >= 0) 160 txt = colTitle(x); 161 else if (y >= 0 && x < 0) 162 txt = rowTitle(y); 163 Point sz = fnt.textSize(txt); 164 if (sz.y < fnt.height) 165 sz.y = fnt.height; 166 return sz; 167 } 168 169 170 /// draw cell content 171 protected override void drawCell(DrawBuf buf, Rect rc, int col, int row) { 172 if (_customCellAdapter && _customCellAdapter.isCustomCell(col, row)) { 173 return _customCellAdapter.drawCell(buf, rc, col, row); 174 } 175 if (BACKEND_GUI) 176 rc.shrink(2, 1); 177 else 178 rc.right--; 179 FontRef fnt = font; 180 dstring txt = cellText(col, row); 181 Point sz = fnt.textSize(txt); 182 Align ha = Align.Right; 183 //if (sz.y < rc.height) 184 applyAlign(rc, sz, ha, Align.VCenter); 185 int offset = BACKEND_CONSOLE ? 0 : 1; 186 fnt.drawText(buf, rc.left + offset, rc.top + offset, txt, textColor); 187 } 188 189 /// draw cell content 190 protected override void drawHeaderCell(DrawBuf buf, Rect rc, int col, int row) { 191 if (BACKEND_GUI) 192 rc.shrink(2, 1); 193 else 194 rc.right--; 195 FontRef fnt = font; 196 dstring txt; 197 if (row < 0 && col >= 0) 198 txt = colTitle(col); 199 else if (row >= 0 && col < 0) 200 txt = rowTitle(row); 201 if (!txt.length) 202 return; 203 Point sz = fnt.textSize(txt); 204 Align ha = Align.Left; 205 if (col < 0) 206 ha = Align.Right; 207 //if (row < 0) 208 // ha = Align.HCenter; 209 applyAlign(rc, sz, ha, Align.VCenter); 210 int offset = BACKEND_CONSOLE ? 0 : 1; 211 uint cl = textColor; 212 cl = style.customColor("grid_cell_text_color_header", cl); 213 fnt.drawText(buf, rc.left + offset, rc.top + offset, txt, cl); 214 } 215 216 /// draw cell background 217 protected override void drawHeaderCellBackground(DrawBuf buf, Rect rc, int c, int r) { 218 bool selectedCol = (c == col) && !_rowSelect; 219 bool selectedRow = r == row; 220 bool selectedCell = selectedCol && selectedRow; 221 if (_rowSelect && selectedRow) 222 selectedCell = true; 223 if (!selectedCell && _multiSelect) { 224 selectedCell = Point(c, r) in _selection || (_rowSelect && Point(0, r) in _selection); 225 } 226 // draw header cell background 227 DrawableRef dw = c < 0 ? _cellRowHeaderBackgroundDrawable : _cellHeaderBackgroundDrawable; 228 uint cl = _cellHeaderBackgroundColor; 229 if (c >= 0 || r >= 0) { 230 if (c < 0 && selectedRow) { 231 cl = _cellHeaderSelectedBackgroundColor; 232 dw = _cellRowHeaderSelectedBackgroundDrawable; 233 } else if (r < 0 && selectedCol) { 234 cl = _cellHeaderSelectedBackgroundColor; 235 dw = _cellHeaderSelectedBackgroundDrawable; 236 } 237 } 238 if (!dw.isNull) 239 dw.drawTo(buf, rc); 240 else 241 buf.fillRect(rc, cl); 242 static if (BACKEND_GUI) { 243 uint borderColor = _cellHeaderBorderColor; 244 buf.drawLine(Point(rc.right - 1, rc.bottom), Point(rc.right - 1, rc.top), _cellHeaderBorderColor); // vertical 245 buf.drawLine(Point(rc.left, rc.bottom - 1), Point(rc.right - 1, rc.bottom - 1), _cellHeaderBorderColor); // horizontal 246 } 247 } 248 249 /// draw cell background 250 protected override void drawCellBackground(DrawBuf buf, Rect rc, int c, int r) { 251 bool selectedCol = c == col; 252 bool selectedRow = r == row; 253 bool selectedCell = selectedCol && selectedRow; 254 if (_rowSelect && selectedRow) 255 selectedCell = true; 256 if (!selectedCell && _multiSelect) { 257 selectedCell = Point(c, r) in _selection || (_rowSelect && Point(0, r) in _selection); 258 } 259 uint borderColor = _cellBorderColor; 260 if (c < fixedCols || r < fixedRows) { 261 // fixed cell background 262 buf.fillRect(rc, _fixedCellBackgroundColor); 263 borderColor = _fixedCellBorderColor; 264 } 265 static if (BACKEND_GUI) { 266 buf.drawLine(Point(rc.left, rc.bottom + 1), Point(rc.left, rc.top), borderColor); // vertical 267 buf.drawLine(Point(rc.left, rc.bottom - 1), Point(rc.right - 1, rc.bottom - 1), borderColor); // horizontal 268 } 269 if (selectedCell) { 270 static if (BACKEND_GUI) { 271 if (_rowSelect) 272 buf.drawFrame(rc, _selectionColorRowSelect, Rect(0,1,0,1), _cellBorderColor); 273 else 274 buf.drawFrame(rc, _selectionColor, Rect(1,1,1,1), _cellBorderColor); 275 } else { 276 if (_rowSelect) 277 buf.fillRect(rc, _selectionColorRowSelect); 278 else 279 buf.fillRect(rc, _selectionColor); 280 } 281 } 282 } 283 284 /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout). 285 override void layout(Rect rc) { 286 super.layout(rc); 287 autofit(); 288 } 289 } 290 291 class TraceFunctionList : VerticalLayout { 292 TraceFuncionGrid _grid; 293 294 this(string ID, dstring title, FunctionNode[] list, ulong ticks_per_second) { 295 super(ID); 296 addChild(new TextWidget("gridTitle", title).layoutWidth(FILL_PARENT)); 297 _grid = new TraceFuncionGrid("FUNCTION_LIST", list, ticks_per_second); 298 addChild(_grid); 299 layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT); 300 } 301 }