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         if (row < 0 || row >= _list.length)
91             return ""d;
92         FunctionNode entry = _list[row];
93         switch (col) {
94             case 0:
95                 string fn = entry.name;
96                 if (fn.length > 256)
97                     fn = fn[0..256] ~ "...";
98                 return fn.to!dstring;
99             case 1:
100                 return formatNumber(entry.number_of_calls, _numberFormatBuf);
101             case 2:
102                 return formatDurationTicks(entry.function_time);
103             case 3:
104                 return formatDurationTicks(entry.function_and_descendant_time);
105             default:
106                 return ""d;
107         }
108     }
109     /// set cell text
110     override StringGridWidgetBase setCellText(int col, int row, dstring text) {
111         // do nothing
112         return this;
113     }
114     /// returns row header title
115     override dstring rowTitle(int row) {
116         return ""d;
117     }
118     /// set row header title
119     override StringGridWidgetBase setRowTitle(int row, dstring title) {
120         return this;
121     }
122 
123     /// returns row header title
124     override dstring colTitle(int col) {
125         return _colTitles[col];
126     }
127 
128     /// set col header title
129     override StringGridWidgetBase setColTitle(int col, dstring title) {
130         _colTitles[col] = title;
131         return this;
132     }
133 
134     void autofit() {
135         autoFitColumnWidths();
136         fillColumnWidth(0);
137     }
138 
139     /// set new size
140     override void resize(int c, int r) {
141         if (c == cols && r == rows)
142             return;
143         int oldcols = cols;
144         int oldrows = rows;
145         super.resize(c, r);
146         _colTitles.length = c;
147     }
148 
149     protected override Point measureCell(int x, int y) {
150         if (_customCellAdapter && _customCellAdapter.isCustomCell(x, y)) {
151             return _customCellAdapter.measureCell(x, y);
152         }
153         //Log.d("measureCell ", x, ", ", y);
154         FontRef fnt = font;
155         dstring txt;
156         if (x >= 0 && y >= 0)
157             txt = cellText(x, y);
158         else if (y < 0 && x >= 0)
159             txt = colTitle(x);
160         else if (y >= 0 && x < 0)
161             txt = rowTitle(y);
162         Point sz = fnt.textSize(txt);
163         if (sz.y < fnt.height)
164             sz.y = fnt.height;
165         return sz;
166     }
167 
168 
169     /// draw cell content
170     protected override void drawCell(DrawBuf buf, Rect rc, int col, int row) {
171         if (_customCellAdapter && _customCellAdapter.isCustomCell(col, row)) {
172             return _customCellAdapter.drawCell(buf, rc, col, row);
173         }
174         if (BACKEND_GUI)
175             rc.shrink(2, 1);
176         else
177             rc.right--;
178         FontRef fnt = font;
179         dstring txt = cellText(col, row);
180         Point sz = fnt.textSize(txt);
181         Align ha = Align.Right;
182         //if (sz.y < rc.height)
183         applyAlign(rc, sz, ha, Align.VCenter);
184         int offset = BACKEND_CONSOLE ? 0 : 1;
185         fnt.drawText(buf, rc.left + offset, rc.top + offset, txt, textColor);
186     }
187 
188     /// draw cell content
189     protected override void drawHeaderCell(DrawBuf buf, Rect rc, int col, int row) {
190         if (BACKEND_GUI)
191             rc.shrink(2, 1);
192         else
193             rc.right--;
194         FontRef fnt = font;
195         dstring txt;
196         if (row < 0 && col >= 0)
197             txt = colTitle(col);
198         else if (row >= 0 && col < 0)
199             txt = rowTitle(row);
200         if (!txt.length)
201             return;
202         Point sz = fnt.textSize(txt);
203         Align ha = Align.Left;
204         if (col < 0)
205             ha = Align.Right;
206         //if (row < 0)
207         //    ha = Align.HCenter;
208         applyAlign(rc, sz, ha, Align.VCenter);
209         int offset = BACKEND_CONSOLE ? 0 : 1;
210         uint cl = textColor;
211         cl = style.customColor("grid_cell_text_color_header", cl);
212         fnt.drawText(buf, rc.left + offset, rc.top + offset, txt, cl);
213     }
214 
215     /// draw cell background
216     protected override void drawHeaderCellBackground(DrawBuf buf, Rect rc, int c, int r) {
217         bool selectedCol = (c == col) && !_rowSelect;
218         bool selectedRow = r == row;
219         bool selectedCell = selectedCol && selectedRow;
220         if (_rowSelect && selectedRow)
221             selectedCell = true;
222         if (!selectedCell && _multiSelect) {
223             selectedCell = Point(c, r) in _selection || (_rowSelect && Point(0, r) in _selection);
224         }
225         // draw header cell background
226         DrawableRef dw = c < 0 ? _cellRowHeaderBackgroundDrawable : _cellHeaderBackgroundDrawable;
227         uint cl = _cellHeaderBackgroundColor;
228         if (c >= 0 || r >= 0) {
229             if (c < 0 && selectedRow) {
230                 cl = _cellHeaderSelectedBackgroundColor;
231                 dw = _cellRowHeaderSelectedBackgroundDrawable;
232             } else if (r < 0 && selectedCol) {
233                 cl = _cellHeaderSelectedBackgroundColor;
234                 dw = _cellHeaderSelectedBackgroundDrawable;
235             }
236         }
237         if (!dw.isNull)
238             dw.drawTo(buf, rc);
239         else
240             buf.fillRect(rc, cl);
241         static if (BACKEND_GUI) {
242             uint borderColor = _cellHeaderBorderColor;
243             buf.drawLine(Point(rc.right - 1, rc.bottom), Point(rc.right - 1, rc.top), _cellHeaderBorderColor); // vertical
244             buf.drawLine(Point(rc.left, rc.bottom - 1), Point(rc.right - 1, rc.bottom - 1), _cellHeaderBorderColor); // horizontal
245         }
246     }
247 
248     /// draw cell background
249     protected override void drawCellBackground(DrawBuf buf, Rect rc, int c, int r) {
250         bool selectedCol = c == col;
251         bool selectedRow = r == row;
252         bool selectedCell = selectedCol && selectedRow;
253         if (_rowSelect && selectedRow)
254             selectedCell = true;
255         if (!selectedCell && _multiSelect) {
256             selectedCell = Point(c, r) in _selection || (_rowSelect && Point(0, r) in _selection);
257         }
258         uint borderColor = _cellBorderColor;
259         if (c < fixedCols || r < fixedRows) {
260             // fixed cell background
261             buf.fillRect(rc, _fixedCellBackgroundColor);
262             borderColor = _fixedCellBorderColor;
263         }
264         static if (BACKEND_GUI) {
265             buf.drawLine(Point(rc.left, rc.bottom + 1), Point(rc.left, rc.top), borderColor); // vertical
266             buf.drawLine(Point(rc.left, rc.bottom - 1), Point(rc.right - 1, rc.bottom - 1), borderColor); // horizontal
267         }
268         if (selectedCell) {
269             static if (BACKEND_GUI) {
270                 if (_rowSelect)
271                     buf.drawFrame(rc, _selectionColorRowSelect, Rect(0,1,0,1), _cellBorderColor);
272                 else
273                     buf.drawFrame(rc, _selectionColor, Rect(1,1,1,1), _cellBorderColor);
274             } else {
275                 if (_rowSelect)
276                     buf.fillRect(rc, _selectionColorRowSelect);
277                 else
278                     buf.fillRect(rc, _selectionColor);
279             }
280         }
281     }
282 
283     /// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
284     override void layout(Rect rc) {
285         super.layout(rc);
286         autofit();
287     }
288 }
289 
290 class TraceFunctionList : VerticalLayout {
291     TraceFuncionGrid _grid;
292 
293     this(string ID, dstring title, FunctionNode[] list, ulong ticks_per_second) {
294         super(ID);
295         addChild(new TextWidget("gridTitle", title).layoutWidth(FILL_PARENT));
296         _grid = new TraceFuncionGrid("FUNCTION_LIST", list, ticks_per_second);
297         addChild(_grid);
298         layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
299     }
300 }