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.components.JBPanel; 19 import com.intellij.ui.components.JBScrollPane; 20 import com.intellij.util.Consumer; 21 import net.miginfocom.swing.MigLayout; 22 import ohos.devtools.views.perftrace.bean.PrefFunc; 23 import ohos.devtools.views.trace.util.Utils; 24 25 import javax.swing.SwingUtilities; 26 import java.awt.Component; 27 import java.awt.Graphics2D; 28 import java.awt.Point; 29 import java.awt.Rectangle; 30 import java.awt.event.MouseAdapter; 31 import java.awt.event.MouseEvent; 32 import java.awt.event.MouseMotionAdapter; 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.List; 36 import java.util.Locale; 37 import java.util.Objects; 38 import java.util.Optional; 39 import java.util.concurrent.atomic.AtomicBoolean; 40 import java.util.stream.Collectors; 41 42 /** 43 * TracePanel 44 * 45 * @since 2021/5/13 13:06 46 */ 47 public class TracePanel extends JBPanel { 48 /** 49 * DURATION 50 */ 51 public static long DURATION = 10_000_000_000L; 52 53 /** 54 * currentSelectThreadIds 55 */ 56 public static List<Integer> currentSelectThreadIds = new ArrayList<>(); 57 58 /** 59 * time shaft start time 60 */ 61 public static long START_TS; 62 63 /** 64 * time shaft end time 65 */ 66 public static long END_TS; 67 68 /** 69 * root TracePanel 70 */ 71 public static TracePanel root; 72 73 /** 74 * current start time 75 */ 76 public static long startNS; 77 78 /** 79 * current end time 80 */ 81 public static long endNS; 82 83 /** 84 * range start time 85 */ 86 public static Long rangeStartNS; 87 88 /** 89 * range end time 90 */ 91 public static Long rangeEndNS; 92 93 private TimeShaft timeShaft; 94 private Ruler ruler; 95 private JBScrollPane scrollPane; 96 private JBPanel contentPanel; 97 private List<Component> componentList; 98 private Point startPoint; 99 private Point endPoint; 100 private List<Component> allComponent; 101 102 /** 103 * structure function 104 */ TracePanel()105 public TracePanel() { 106 this(true); 107 } 108 109 /** 110 * TracePanel constructor 111 * 112 * @param showTimeShaft showTimeShaft 113 */ TracePanel(boolean showTimeShaft)114 public TracePanel(boolean showTimeShaft) { 115 timeShaft = new TimeShaft((startNS, endNS, scale) -> { 116 EventDispatcher.dispatcherRange(startNS, endNS, scale); 117 Component[] components = contentPanel.getComponents(); 118 for (Component component : components) { 119 if (component instanceof ExpandPanel) { 120 ((ExpandPanel) component).refresh(startNS, endNS); 121 } 122 } 123 }, keyEvent -> timeShaftComplete(), mouseEvent -> timeShaftComplete()); 124 contentPanel = new JBPanel(); 125 contentPanel.setLayout(new MigLayout("inset 0,wrap 1", "[grow,fill]", "0[]0")); 126 contentPanel.setFocusable(true); 127 contentPanel.setBorder(null); 128 setLayout(new MigLayout("inset 0", "", "0[]0")); 129 scrollPane = new JBScrollPane(contentPanel); 130 scrollPane.setBorder(null); 131 ruler = new Ruler(TracePanel.DURATION); 132 if (showTimeShaft) { 133 add(ruler, "wrap,pushx,growx,h 20!"); 134 add(timeShaft, "wrap,pushx,growx,h 50!"); 135 } 136 add(scrollPane, "push,grow"); 137 setBorder(null); 138 scrollPane.getVerticalScrollBar().addAdjustmentListener(event -> { 139 Component[] components = contentPanel.getComponents(); 140 for (Component component : components) { 141 if (scrollPane.getViewport().getViewRect().intersects(component.getBounds())) { 142 if (component instanceof ExpandPanel) { 143 ((ExpandPanel) component).refresh(startNS, endNS); 144 } 145 } 146 } 147 }); 148 root = this; 149 contentPanelAddListen(); 150 } 151 contentPanelAddListen()152 private void contentPanelAddListen() { 153 contentPanel.addMouseListener(new MouseAdapter() { 154 @Override 155 public void mousePressed(MouseEvent event) { 156 super.mousePressed(event); 157 mousePressedThreadRow(event); 158 } 159 160 @Override 161 public void mouseClicked(MouseEvent event) { 162 super.mouseClicked(event); 163 PrefFunc.setSelectedPrefFunc(null); 164 mouseClickThreadRow(event); 165 } 166 167 @Override 168 public void mouseExited(MouseEvent event) { 169 super.mouseExited(event); 170 Tip.getInstance().hidden(); 171 } 172 173 @Override 174 public void mouseReleased(MouseEvent event) { 175 super.mouseReleased(event); 176 } 177 }); 178 contentPanel.addMouseMotionListener(new MouseMotionAdapter() { 179 @Override 180 public void mouseDragged(MouseEvent event) { 181 super.mouseDragged(event); 182 PrefFunc.setSelectedPrefFunc(null); 183 mouseDraggedThreadRow(event); 184 } 185 186 @Override 187 public void mouseMoved(MouseEvent event) { 188 super.mouseMoved(event); 189 tip(event); 190 } 191 }); 192 } 193 194 /** 195 * range end time 196 * 197 * @return get current contentPanel 198 */ getContentPanel()199 public JBPanel getContentPanel() { 200 return contentPanel; 201 } 202 timeShaftComplete()203 private void timeShaftComplete() { 204 Arrays.stream(contentPanel.getComponents()).filter(it -> it instanceof ExpandPanel) 205 .map(it -> ((ExpandPanel) it)).filter(it -> !it.isCollapsed()).forEach( 206 it -> Arrays.stream(it.getContent().getComponents()).filter(row -> row instanceof TraceSimpleRow) 207 .map(row -> ((TraceSimpleRow) row)) 208 .filter(row -> row.getRowName().toLowerCase(Locale.ENGLISH).startsWith("cpu")) 209 .forEach(row -> row.reload())); 210 } 211 tip(MouseEvent event)212 private void tip(MouseEvent event) { 213 if (Objects.isNull(allComponent)) { 214 allComponent = Arrays.stream(contentPanel.getComponents()).filter(ExpandPanel.class::isInstance) 215 .flatMap(component -> Arrays.stream(((ExpandPanel) component).getContent().getComponents())) 216 .collect(Collectors.toList()); 217 } 218 boolean flag = allComponent.stream().anyMatch(it -> { 219 if (it instanceof AbstractRow) { 220 AbstractRow row = (AbstractRow) it; 221 Rectangle rectangle = SwingUtilities.convertRectangle(row, row.getContentBounds(), contentPanel); 222 if (rectangle.contains(event.getPoint())) { 223 return true; 224 } 225 } 226 return false; 227 }); 228 if (flag) { 229 allComponent.forEach(component -> { 230 if (component instanceof AbstractRow) { 231 AbstractRow row = (AbstractRow) component; 232 Rectangle rectangle = SwingUtilities.convertRectangle(row, row.getContentBounds(), contentPanel); 233 if (rectangle.contains(event.getPoint())) { 234 Point point = SwingUtilities.convertPoint(contentPanel, event.getPoint(), row.content); 235 row.mouseMoveHandler(point); 236 } 237 } 238 }); 239 } else { 240 Tip.getInstance().hidden(); 241 } 242 } 243 244 /** 245 * structure function 246 * 247 * @param startNS startNS 248 * @param endNS endNS 249 */ setRange(long startNS, long endNS)250 public void setRange(long startNS, long endNS) { 251 Optional.ofNullable(timeShaft).ifPresent(tf -> tf.setRange(startNS, endNS)); 252 } 253 mouseDraggedThreadRow(MouseEvent event)254 private void mouseDraggedThreadRow(MouseEvent event) { 255 endPoint = SwingUtilities.convertPoint(contentPanel, event.getPoint(), componentList.get(0).getParent()); 256 int xPoint = Math.min(Utils.getX(startPoint), Utils.getX(endPoint)); 257 int yPoint = Math.min(Utils.getY(startPoint), Utils.getY(endPoint)); 258 int width = Math.abs(Utils.getX(startPoint) - Utils.getX(endPoint)) == 0 ? 1 259 : Math.abs(Utils.getX(startPoint) - Utils.getX(endPoint)); 260 int height = Math.abs(Utils.getY(startPoint) - Utils.getY(endPoint)); 261 Rectangle range = new Rectangle(xPoint, yPoint, width, height); 262 263 for (Component component : componentList) { 264 if (component instanceof TraceThreadRow) { 265 TraceThreadRow cp = (TraceThreadRow) component; 266 if (range.intersects(component.getBounds())) { 267 if (!currentSelectThreadIds.contains(cp.getTid())) { 268 currentSelectThreadIds.add(cp.getTid()); 269 } 270 cp.setSelect(true, xPoint - Utils.getX(cp.getContentBounds()), 271 xPoint + width - Utils.getX(cp.getContentBounds())); 272 } else { 273 if (currentSelectThreadIds.contains(cp.getTid())) { 274 currentSelectThreadIds.remove(cp.getTid()); 275 } 276 cp.setSelect(false, null, null); 277 } 278 } 279 } 280 notifySelectRangeChange(); 281 Tip.getInstance().hidden(); 282 } 283 mouseClickThreadRow(MouseEvent event)284 private void mouseClickThreadRow(MouseEvent event) { 285 TracePanel.rangeStartNS = null; 286 TracePanel.rangeEndNS = null; 287 AtomicBoolean flag = new AtomicBoolean(false); 288 componentList.forEach(component -> { 289 if (component instanceof TraceThreadRow) { 290 TraceThreadRow<?, ?> thread = (TraceThreadRow<?, ?>) component; 291 if (thread.getBounds().contains(startPoint) && Utils.getX(startPoint) < Utils 292 .getX(thread.getContentBounds())) { 293 if (!currentSelectThreadIds.contains(thread.getTid())) { 294 currentSelectThreadIds.add(thread.getTid()); 295 } 296 thread.setSelect(true, null, null); 297 } else { 298 Point point = SwingUtilities.convertPoint(event.getComponent(), event.getPoint(), thread.content); 299 if ((thread.getData() != null && thread.getData().stream() 300 .anyMatch(it -> it.getRect().contains(point))) || (thread.getData2() != null && thread 301 .getData2().stream().anyMatch(it -> it.getRect().contains(point)))) { 302 if (Objects.nonNull(thread.getData())) { 303 thread.getData().stream().filter(it -> it.getRect().contains(point)).findFirst() 304 .ifPresent(it -> it.onClick(event)); 305 } 306 if (Objects.nonNull(thread.getData2())) { 307 thread.getData2().stream().filter(it -> it.getRect().contains(point)).findFirst() 308 .ifPresent(it -> it.onClick(event)); 309 } 310 flag.set(true); 311 } 312 currentSelectThreadIds.remove(thread.getTid()); 313 thread.setSelect(false, null, null); 314 } 315 } 316 }); 317 if (!flag.get()) { 318 notifySelectRangeChange(); 319 } 320 } 321 notifySelectRangeChange()322 private void notifySelectRangeChange() { 323 if (Objects.isNull(TracePanel.rangeStartNS) && Objects.isNull(TracePanel.rangeEndNS)) { 324 EventDispatcher.dispatcherThreadRange(TracePanel.startNS, TracePanel.endNS, currentSelectThreadIds); 325 } else { 326 long st = TracePanel.rangeStartNS < TracePanel.startNS ? TracePanel.startNS : TracePanel.rangeStartNS; 327 long et = TracePanel.rangeEndNS > TracePanel.endNS ? TracePanel.endNS : TracePanel.rangeEndNS; 328 EventDispatcher.dispatcherThreadRange(st, et, currentSelectThreadIds); 329 } 330 } 331 mousePressedThreadRow(MouseEvent event)332 private void mousePressedThreadRow(MouseEvent event) { 333 if (Objects.isNull(componentList)) { 334 componentList = Arrays.stream(contentPanel.getComponents()).filter(component -> { 335 if (component instanceof ExpandPanel) { 336 ExpandPanel ep = (ExpandPanel) component; 337 if (!ep.getTitle().startsWith("Display") && !ep.getTitle().startsWith("CPU")) { 338 return true; 339 } 340 } 341 return false; 342 }).flatMap(component -> Arrays.stream(((ExpandPanel) component).getContent().getComponents())) 343 .collect(Collectors.toList()); 344 } 345 if (componentList.size() > 0) { 346 startPoint = SwingUtilities.convertPoint(contentPanel, event.getPoint(), componentList.get(0).getParent()); 347 } 348 } 349 350 /** 351 * paint the TimeShaft 352 * 353 * @param consumer consumer 354 */ paintTimeShaft(Consumer<Graphics2D> consumer)355 public void paintTimeShaft(Consumer<Graphics2D> consumer) { 356 timeShaft.setTimeShaftConsumer(consumer); 357 timeShaft.repaint(); 358 } 359 360 /** 361 * get the TimeShaft 362 * 363 * @return timeShaft timeShaft 364 */ getTimeShaft()365 public TimeShaft getTimeShaft() { 366 return timeShaft; 367 } 368 369 /** 370 * get the TimeRuler 371 * 372 * @return ruler timeRuler 373 */ getRuler()374 public Ruler getRuler() { 375 return ruler; 376 } 377 } 378