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 }