• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package ohos.devtools.views.trace;
17 
18 import com.intellij.ui.JBColor;
19 import com.intellij.ui.components.JBPanel;
20 import com.intellij.util.Consumer;
21 import com.intellij.util.ui.JBUI;
22 import ohos.devtools.views.applicationtrace.util.TimeUtils;
23 import ohos.devtools.views.trace.util.Utils;
24 
25 import java.awt.AlphaComposite;
26 import java.awt.Cursor;
27 import java.awt.Graphics;
28 import java.awt.Graphics2D;
29 import java.awt.Rectangle;
30 import java.awt.event.ComponentAdapter;
31 import java.awt.event.ComponentEvent;
32 import java.awt.event.KeyEvent;
33 import java.awt.event.KeyListener;
34 import java.awt.event.MouseEvent;
35 import java.awt.event.MouseListener;
36 import java.awt.event.MouseMotionListener;
37 import java.text.DecimalFormat;
38 import java.util.Arrays;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Optional;
43 
44 import static java.awt.event.KeyEvent.VK_A;
45 import static java.awt.event.KeyEvent.VK_D;
46 import static java.awt.event.KeyEvent.VK_S;
47 import static java.awt.event.KeyEvent.VK_W;
48 import static ohos.devtools.views.trace.TracePanel.DURATION;
49 import static ohos.devtools.views.trace.TracePanel.endNS;
50 import static ohos.devtools.views.trace.TracePanel.startNS;
51 
52 /**
53  * The timescale
54  *
55  * @since 2021/5/12 16:39
56  */
57 public class TimeShaft extends JBPanel implements KeyListener, MouseListener, MouseMotionListener {
58     private static final int SELECT_BORDER_WIDTH = 3;
59 
60     private final ITimeRange rangeListener;
61     private final Consumer keyReleaseHandler;
62     private final Consumer mouseReleaseHandler;
63     private final AlphaComposite alpha20 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .2f);
64     private final AlphaComposite alpha40 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .4f);
65     private final AlphaComposite alpha100 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f);
66     private final long[] scales =
67         new long[] {50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000, 50_000, 100_000, 200_000, 500_000,
68             1_000_000, 2_000_000, 5_000_000, 10_000_000, 20_000_000, 50_000_000, 100_000_000, 200_000_000, 500_000_000,
69             1_000_000_000, 2_000_000_000, 5_000_000_000L, 10_000_000_000L, 20_000_000_000L, 50_000_000_000L,
70             100_000_000_000L, 200_000_000_000L, 500_000_000_000L};
71     private int startX;
72     private int endX;
73     private Rectangle selectRect = new Rectangle();
74     private Rectangle selectLeftRect = new Rectangle();
75     private Rectangle selectRightRect = new Rectangle();
76     private Rectangle selectTopRect = new Rectangle();
77     private Cursor wCursor = new Cursor(Cursor.W_RESIZE_CURSOR);
78     private Cursor eCursor = new Cursor(Cursor.E_RESIZE_CURSOR);
79     private Cursor handCursor = new Cursor(Cursor.HAND_CURSOR);
80     private Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
81     private Status status = Status.DRAG;
82     private int offset;
83     private int length;
84     private double ratio1;
85     private double ratio2;
86     private double wheelNS;
87     private long scale;
88     private boolean isInit;
89     private Consumer<Graphics2D> timeShaftConsumer;
90     private DecimalFormat formatter = new DecimalFormat("#.##%");
91     private Map<Integer, Double> rateMap = new HashMap<>();
92 
93     /**
94      * TimeShaft
95      *
96      * @param range range
97      * @param keyReleaseHandler keyReleaseHandler
98      * @param mouseReleaseHandler mouseReleaseHandler
99      */
TimeShaft(ITimeRange range, Consumer<KeyEvent> keyReleaseHandler, Consumer<MouseEvent> mouseReleaseHandler)100     public TimeShaft(ITimeRange range, Consumer<KeyEvent> keyReleaseHandler, Consumer<MouseEvent> mouseReleaseHandler) {
101         this.rangeListener = range;
102         this.keyReleaseHandler = keyReleaseHandler;
103         this.mouseReleaseHandler = mouseReleaseHandler;
104         this.setOpaque(true);
105         this.addComponentListener(new ComponentAdapter() {
106             @Override
107             public void componentResized(ComponentEvent event) {
108                 super.componentResized(event);
109                 startX = (int) (getWidth() * ratio1);
110                 endX = (int) (getWidth() * ratio2);
111                 Utils.setX(selectRect, Math.min(startX, endX));
112                 selectRect.width = Math.abs(endX - startX);
113                 setAllRect();
114             }
115         });
116         addMouseListener(this);
117         addKeyListener(this);
118         addMouseMotionListener(this);
119     }
120 
121     /**
122      * mouseClicked function
123      *
124      * @param event event
125      */
mouseClicked(MouseEvent event)126     public void mouseClicked(MouseEvent event) {
127         event.getX();
128     }
129 
130     /**
131      * set TimeShaft Consumer function
132      *
133      * @param consumer consumer
134      */
setTimeShaftConsumer(Consumer<Graphics2D> consumer)135     public void setTimeShaftConsumer(Consumer<Graphics2D> consumer) {
136         this.timeShaftConsumer = consumer;
137     }
138 
139     /**
140      * set TimeShaft Consumer function
141      *
142      * @param event event
143      */
mousePressed(MouseEvent event)144     public void mousePressed(MouseEvent event) {
145         if (getVisibleRect().contains(event.getPoint())) {
146             if (status == Status.DRAG) {
147                 startX = event.getX();
148                 endX = event.getX() + SELECT_BORDER_WIDTH * 3;
149                 Utils.setX(selectRect, event.getX());
150                 Utils.setY(selectRect, 0);
151                 selectRect.height = getHeight();
152                 selectRect.width = SELECT_BORDER_WIDTH * 3;
153                 setAllRect();
154                 notifyRangeChange(startX, endX);
155             } else if (status == Status.MOVE) {
156                 offset = Math.abs(event.getX() - Math.min(startX, endX));
157                 length = Math.abs(endX - startX);
158                 notifyRangeChange(startX, endX);
159             } else {
160                 notifyRangeChange(startX, endX);
161             }
162         }
163     }
164 
165     /**
166      * when the mouse released event
167      *
168      * @param event event
169      */
mouseReleased(MouseEvent event)170     public void mouseReleased(MouseEvent event) {
171         if (startX > endX) {
172             int tmp = startX;
173             startX = endX;
174             endX = tmp;
175         }
176         mouseReleaseHandler.consume(event);
177     }
178 
179     /**
180      * when the mouse Entered event
181      *
182      * @param event event
183      */
mouseEntered(MouseEvent event)184     public void mouseEntered(MouseEvent event) {
185         requestFocusInWindow();
186     }
187 
188     /**
189      * when the mouse Entered event
190      *
191      * @param event event
192      */
mouseExited(MouseEvent event)193     public void mouseExited(MouseEvent event) {
194         Tip.getInstance().hidden();
195     }
196 
197     /**
198      * when the mouse Entered event
199      *
200      * @param event event
201      */
mouseDragged(MouseEvent event)202     public void mouseDragged(MouseEvent event) {
203         Tip.getInstance().hidden();
204         if (status == Status.DRAG) {
205             endX = event.getX();
206         } else if (status == Status.LEFT) {
207             startX = event.getX();
208         } else if (status == Status.RIGHT) {
209             endX = event.getX();
210         } else if (status == Status.MOVE) {
211             if (event.getX() - offset < 0) {
212                 startX = 0;
213                 endX = startX + length + event.getX() - offset;
214             } else {
215                 startX = event.getX() - offset;
216                 if (startX + length > getWidth()) {
217                     endX = getWidth();
218                 } else {
219                     endX = startX + length;
220                 }
221             }
222         } else {
223             endX = endX;
224         }
225         if (startX < 0) {
226             startX = 0;
227         }
228         if (endX < 0) {
229             endX = 0;
230         }
231         if (startX > getWidth()) {
232             startX = getWidth();
233         }
234         if (endX > getWidth()) {
235             endX = getWidth();
236         }
237         Utils.setX(selectRect, Math.min(startX, endX));
238         selectRect.width = Math.abs(endX - startX);
239         setAllRect();
240         notifyRangeChange(Math.min(startX, endX), Math.max(startX, endX));
241     }
242 
243     /**
244      * when the mouse move event
245      *
246      * @param event event
247      */
mouseMoved(MouseEvent event)248     public void mouseMoved(MouseEvent event) {
249         if (this.getVisibleRect().contains(event.getPoint())) {
250             if (selectLeftRect.contains(event.getPoint())) {
251                 setCursor(wCursor);
252                 status = Status.LEFT;
253             } else if (selectRightRect.contains(event.getPoint())) {
254                 setCursor(eCursor);
255                 status = Status.RIGHT;
256             } else if (selectTopRect.contains(event.getPoint())) {
257                 setCursor(handCursor);
258                 status = Status.MOVE;
259             } else {
260                 status = Status.DRAG;
261                 setCursor(defaultCursor);
262                 tip(event);
263             }
264         }
265     }
266 
tip(MouseEvent event)267     private void tip(MouseEvent event) {
268         String timeString = TimeUtils.getTimeFormatString(Common.x2ns(event.getX(), this.getBounds()));
269         Double rate = rateMap.get(event.getX());
270         if (rate == null) {
271             rate = 0.0d;
272         }
273         List<String> strings = Arrays.asList(timeString, "CPU Usage    ", formatter.format(rate), "Select to inspect");
274         Tip.getInstance().display(this, event.getPoint(), strings);
275     }
276 
277     /**
278      * when the key Typed  event
279      *
280      * @param event event
281      */
keyTyped(KeyEvent event)282     public void keyTyped(KeyEvent event) {
283         event.getKeyCode();
284     }
285 
286     /**
287      * when the mouse press event
288      *
289      * @param event event
290      */
keyPressed(KeyEvent event)291     public void keyPressed(KeyEvent event) {
292         switch (event.getExtendedKeyCode()) {
293             case VK_A:
294                 wheelNS = (endNS - startNS) * -0.2;
295                 translation();
296                 break;
297             case VK_D:
298                 wheelNS = (endNS - startNS) * 0.2;
299                 translation();
300                 break;
301             case VK_W:
302                 wheelNS = (endNS - startNS) * 0.2;
303                 if (wheelNS == 0) {
304                     wheelNS = 50L;
305                 }
306                 scale();
307                 break;
308             case VK_S:
309                 wheelNS = (endNS - startNS) * -0.2;
310                 if (wheelNS == 0) {
311                     wheelNS = -50L;
312                 }
313                 scale();
314                 break;
315             default:
316                 break;
317         }
318     }
319 
320     /**
321      * on the key released
322      *
323      * @param event event
324      */
keyReleased(KeyEvent event)325     public void keyReleased(KeyEvent event) {
326         if (event.getExtendedKeyCode() == VK_A || event.getExtendedKeyCode() == VK_S
327             || event.getExtendedKeyCode() == VK_D || event.getExtendedKeyCode() == VK_W) {
328             keyReleaseHandler.consume(event);
329         }
330     }
331 
332     /**
333      * put Rate Map
334      *
335      * @param xPoint xPoint
336      * @param rate rate
337      */
putRateMap(int xPoint, double rate)338     public void putRateMap(int xPoint, double rate) {
339         if (rateMap.containsKey(xPoint)) {
340             if (rateMap.get(xPoint) == 0d) {
341                 rateMap.put(xPoint, rate);
342             }
343         } else {
344             rateMap.put(xPoint, rate);
345         }
346     }
347 
348     /**
349      * clear map
350      */
clearMap()351     public void clearMap() {
352         rateMap.clear();
353     }
354 
355     /**
356      * set the current range
357      *
358      * @param mStartNS mStartNS
359      * @param mEndNS mEndNS
360      */
setRange(long mStartNS, long mEndNS)361     public void setRange(long mStartNS, long mEndNS) {
362         startX = (int) (mStartNS * getWidth() * 1.0 / DURATION);
363         endX = (int) (mEndNS * getWidth() * 1.0 / DURATION);
364         double l20 = (mEndNS - mStartNS) * 1.0 / 20;
365         long min;
366         long max;
367         for (int index = 0; index < scales.length; index++) {
368             if (scales[index] > l20) {
369                 if (index > 0) {
370                     min = scales[index - 1];
371                 } else {
372                     min = 0;
373                 }
374                 max = scales[index];
375                 double weight = (l20 - min) * 1.0 / (max - min);
376                 if (weight > 0.243) {
377                     scale = max;
378                 } else {
379                     scale = min;
380                 }
381                 break;
382             }
383         }
384         if (scale == 0) {
385             scale = scales[0];
386         }
387         if (startX == 0 && endX == 0) {
388             endX = 1;
389         }
390         Utils.setX(selectRect, Math.min(startX, endX));
391         selectRect.width = Math.abs(endX - startX);
392         setAllRect();
393     }
394 
395     @Override
paintComponent(Graphics graphics)396     public void paintComponent(Graphics graphics) {
397         super.paintComponent(graphics);
398         if (graphics instanceof Graphics2D) {
399             Graphics2D g2 = (Graphics2D) graphics;
400             Optional.ofNullable(timeShaftConsumer).ifPresent(it -> it.consume(g2));
401             g2.setColor(JBColor.background().darker());
402             g2.setComposite(alpha40);
403             g2.fillRect(0, 0, getWidth(), getHeight());
404             g2.setComposite(alpha100);
405             g2.setColor(JBColor.foreground());
406             g2.drawString("CPU Usage", 3, 13);
407             g2.setComposite(alpha100);
408             if (startX == 0 && endX == 0) {
409                 startX = 0;
410                 endX = getWidth();
411                 Utils.setX(selectRect, 0);
412                 Utils.setY(selectRect, 0);
413                 selectRect.width = getWidth();
414                 selectRect.height = getHeight();
415                 setAllRect();
416             }
417             g2.setColor(JBUI.CurrentTheme.Link.linkColor());
418             g2.setComposite(alpha20);
419             g2.fillRect(Utils.getX(selectRect), Utils.getY(selectRect), selectRect.width, selectRect.height);
420             g2.setComposite(alpha40);
421             g2.fillRect(Utils.getX(selectTopRect), Utils.getY(selectTopRect), selectTopRect.width,
422                 selectTopRect.height);
423             g2.setComposite(alpha100);
424             g2.fillRect(Utils.getX(selectLeftRect), Utils.getY(selectLeftRect), selectLeftRect.width,
425                 selectLeftRect.height);
426             g2.fillRect(Utils.getX(selectRightRect), Utils.getY(selectRightRect), selectRightRect.width,
427                 selectRightRect.height);
428             if (!isInit) {
429                 isInit = true;
430                 setRange(0L, DURATION);
431                 notifyRangeChange(0L, DURATION);
432             }
433         }
434     }
435 
setAllRect()436     private void setAllRect() {
437         ratio1 = startX * 1.0 / getWidth();
438         if (ratio1 < 0) {
439             ratio1 = 0;
440         }
441         ratio2 = endX * 1.0 / getWidth();
442         if (ratio2 > 1) {
443             ratio2 = 1;
444         }
445         Utils.setX(selectTopRect, Utils.getX(selectRect));
446         Utils.setY(selectTopRect, Utils.getY(selectRect));
447         selectTopRect.width = selectRect.width;
448         selectTopRect.height = SELECT_BORDER_WIDTH * 5;
449         Utils.setX(selectLeftRect, Utils.getX(selectRect));
450         Utils.setY(selectLeftRect, Utils.getY(selectRect));
451         selectLeftRect.width = SELECT_BORDER_WIDTH;
452         selectLeftRect.height = selectRect.height;
453         Utils.setX(selectRightRect, Utils.getX(selectRect) + selectRect.width - SELECT_BORDER_WIDTH);
454         Utils.setY(selectRightRect, Utils.getY(selectRect));
455         selectRightRect.width = selectLeftRect.width;
456         selectRightRect.height = selectRect.height;
457     }
458 
translation()459     private void translation() {
460         if (startNS + wheelNS <= 0) {
461             startNS = 0;
462             endNS = endNS - startNS;
463             setRange(startNS, endNS);
464         } else if (endNS + wheelNS >= DURATION) {
465             startNS = DURATION - (endNS - startNS);
466             endNS = DURATION;
467             setRange(startNS, endNS);
468         } else {
469             startNS = (long) (startNS + wheelNS);
470             endNS = (long) (endNS + wheelNS);
471             setRange(startNS, endNS);
472         }
473         notifyRangeChange(startNS, endNS);
474     }
475 
scale()476     private void scale() {
477         startNS = (long) (startNS + wheelNS);
478         endNS = (long) (endNS - wheelNS);
479         if (startNS <= 0) {
480             startNS = 0L;
481         }
482         if (endNS >= DURATION) {
483             endNS = DURATION;
484         }
485         setRange(startNS, endNS);
486         notifyRangeChange(startNS, endNS);
487     }
488 
notifyRangeChange(int xPoint, int yPoint)489     private void notifyRangeChange(int xPoint, int yPoint) {
490         long ns1 = Common.x2ns(xPoint, getVisibleRect());
491         long ns2 = Common.x2ns(yPoint, getVisibleRect());
492         startNS = Math.min(ns1, ns2);
493         endNS = Math.max(ns1, ns2);
494         repaint();
495         Optional.ofNullable(rangeListener).ifPresent(range -> range.change(startNS, endNS, scale));
496     }
497 
notifyRangeChange(long ns1, long ns2)498     private void notifyRangeChange(long ns1, long ns2) {
499         startNS = Math.min(ns1, ns2);
500         endNS = Math.max(ns1, ns2);
501         repaint();
502         Optional.ofNullable(rangeListener).ifPresent(range -> range.change(startNS, endNS, scale));
503     }
504 
505     enum Status {
506         DRAG, LEFT, RIGHT, MOVE
507     }
508 }
509