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.fragment.ruler; 17 18 import ohos.devtools.views.trace.bean.FlagBean; 19 import ohos.devtools.views.trace.component.AnalystPanel; 20 import ohos.devtools.views.trace.util.Final; 21 import ohos.devtools.views.trace.util.TimeUtils; 22 import ohos.devtools.views.trace.util.Utils; 23 24 import javax.swing.JComponent; 25 import java.awt.AlphaComposite; 26 import java.awt.BasicStroke; 27 import java.awt.Graphics2D; 28 import java.awt.event.MouseEvent; 29 import java.awt.geom.Rectangle2D; 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.Optional; 33 34 /** 35 * Timeline zoom size 36 * 37 * @since 2021/4/22 12:25 38 */ 39 public class RulerFragment extends AbstractFragment implements FlagBean.IEventListener { 40 private static final BasicStroke BOLD_STORE = new BasicStroke(2); 41 42 private final long[] scales = 43 new long[] {50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000, 50_000, 100_000, 200_000, 500_000, 44 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, 45 1_000_000_000, 2_000_000_000, 5_000_000_000L, 10_000_000_000L, 20_000_000_000L, 50_000_000_000L, 46 100_000_000_000L, 200_000_000_000L, 500_000_000_000L}; 47 private long leftNS; 48 private long rightNS; 49 50 /** 51 * The current time selection range is based on 20 units. 52 * The zoom level obtained by the position in the scales array is 70ns if calculated 53 */ 54 private long min; 55 56 /** 57 * Then in the middle of 50L 100L min=50L 58 * The current time selection range The position in the zoom level scales array based on 20 units. 59 * If calculated, it is 70ns 60 */ 61 private long max; 62 63 // Then it is in the middle of 50L 100L max = 100L 64 private long l20; // The current time selection range is based on 20 units 65 66 // When the weight ratio is greater than 24.3%, scale=max; otherwise, scale=min schematic diagram 67 private long scale; 68 69 // min---l20-------max 70 private long centerNS; // Select from left to right is true 71 72 // From right to left is false; 73 // when moving from left to right, use the left as the reference, 74 // and fill the cell to the left; when moving from right to left, fill the cell to the right 75 private double weight; // (l20-min)/(max-min) to get the proportion 76 private int extendHeight; 77 private int selectX; 78 private int selectY; 79 private FlagBean focusFlag = new FlagBean(); 80 private List<FlagBean> flags = new ArrayList<>(); 81 private double realW; 82 private double startX; 83 private IChange changeListener; 84 85 /** 86 * Constructor 87 * 88 * @param root Parent component 89 * @param listener monitor 90 */ RulerFragment(final JComponent root, final IChange listener)91 public RulerFragment(final JComponent root, final IChange listener) { 92 this.changeListener = listener; 93 this.setRoot(root); 94 getRect().setBounds(200, 94, root.getWidth(), 40); 95 setRange(0, AnalystPanel.getDURATION(), 0); 96 } 97 98 /** 99 * Set time range 100 * 101 * @param left left 102 * @param right right 103 * @param center center 104 */ setRange(final long left, final long right, final long center)105 public void setRange(final long left, final long right, final long center) { 106 this.centerNS = center; 107 this.leftNS = left; 108 this.rightNS = right; 109 l20 = (this.rightNS - left) / 20; 110 for (int index = 0; index < scales.length; index++) { 111 if (scales[index] > l20) { 112 if (index > 0) { 113 min = scales[index - 1]; 114 } else { 115 min = 0; 116 } 117 max = scales[index]; 118 weight = (l20 - min) * 1.0 / (max - min); 119 if (weight > 0.243) { 120 scale = max; 121 } else { 122 scale = min; 123 } 124 break; 125 } 126 } 127 if (scale == 0) { 128 scale = scales[0]; 129 } 130 for (FlagBean flag : flags) { 131 Utils.setX(flag.rect, getX(flag.getNs())); 132 } 133 repaint(); 134 } 135 136 /** 137 * clear the flags 138 */ recycle()139 public void recycle() { 140 flags.clear(); 141 } 142 143 /** 144 * Drawing method 145 * 146 * @param graphics graphics 147 */ 148 @Override draw(final Graphics2D graphics)149 public void draw(final Graphics2D graphics) { 150 getRect().width = getRoot().getWidth() - Utils.getX(getRect()); 151 graphics.setFont(Final.SMALL_FONT); 152 graphics.setColor(getRoot().getForeground()); 153 if (rightNS - leftNS == 0) { 154 return; 155 } 156 if (scale == 0) { 157 setRange(0, AnalystPanel.getDURATION(), 0); 158 } 159 if (changeListener != null) { 160 changeListener.change(leftNS, rightNS); 161 } 162 if (centerNS == leftNS) { // Fill from left to right 163 drawLeft2Right(graphics); 164 } 165 if (centerNS == rightNS) { // Fill from right to left 166 drawRight2Left(graphics); 167 } 168 for (FlagBean flagBean : flags) { 169 graphics.setColor(flagBean.getColor()); 170 graphics.setStroke(new BasicStroke(2)); 171 int xAxis = Utils.getX(flagBean.rect); 172 if (xAxis > 0) { 173 graphics.drawLine(xAxis + Utils.getX(getRect()), Utils.getY(getRect()) + getRect().height / 2, 174 Utils.getX(getRect()) + xAxis, Utils.getY(getRect()) + getRect().height - 2); 175 graphics.fillRect(xAxis + Utils.getX(getRect()), Utils.getY(getRect()) + getRect().height / 2, 10, 10); 176 graphics 177 .fillRect(xAxis + Utils.getX(getRect()) + 7, Utils.getY(getRect()) + getRect().height / 2 + 2, 7, 178 7); 179 flagBean.draw(graphics); 180 } 181 } 182 drawFocusFlag(graphics); 183 } 184 drawRight2Left(Graphics2D graphics)185 private void drawRight2Left(Graphics2D graphics) { 186 graphics.setColor(getRoot().getForeground()); 187 final AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f); 188 graphics.setComposite(alpha); 189 graphics.drawLine(Utils.getX(getRect()), Utils.getY(getRect()), Utils.getX(getRect()) + getRect().width, 190 Utils.getY(getRect())); 191 long tmpNs = rightNS - leftNS; 192 startX = Utils.getX(getRect()) + getRect().width; 193 realW = (scale * getRect().width) / (rightNS - leftNS); 194 String str; 195 while (tmpNs > 0) { 196 str = TimeUtils.getSecondFromNSecond(tmpNs); 197 if (str.isEmpty()) { 198 str = "0s"; 199 } 200 graphics.setColor(getRoot().getForeground()); 201 final AlphaComposite alpha50 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f); 202 graphics.setComposite(alpha50); 203 graphics.drawLine((int) startX, Utils.getY(getRect()), (int) startX, 204 Utils.getY(getRect()) + getRect().height + extendHeight); 205 graphics.setColor(getRoot().getForeground()); 206 final AlphaComposite alphaFul = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f); 207 graphics.setComposite(alphaFul); 208 graphics.drawString("+" + str, (int) startX, 209 Utils.getY(getRect()) + (int) (graphics.getFontMetrics().getStringBounds("+" + str, graphics) 210 .getHeight())); 211 startX -= realW; 212 tmpNs -= scale; 213 } 214 } 215 drawLeft2Right(Graphics2D graphics)216 private void drawLeft2Right(Graphics2D graphics) { 217 if (scale == 0) { 218 return; 219 } 220 long tmpNs = 0L; 221 long yu = leftNS % scale; 222 realW = (scale * getRect().width) / (rightNS - leftNS); 223 startX = Utils.getX(getRect()); 224 if (yu != 0) { 225 float firstNodeWidth = (float) ((yu * 1.0) / scale * realW); 226 startX += firstNodeWidth; 227 tmpNs += yu; 228 graphics.setColor(getRoot().getForeground()); 229 final AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f); 230 graphics.setComposite(alpha); 231 graphics 232 .drawLine((int) startX, Utils.getY(getRect()), (int) startX, Utils.getY(getRect()) + getRect().height); 233 } 234 graphics.setColor(getRoot().getForeground()); 235 graphics.drawLine(Utils.getX(getRect()), Utils.getY(getRect()), Utils.getX(getRect()) + getRect().width, 236 Utils.getY(getRect())); 237 String str; 238 while (tmpNs < rightNS - leftNS) { 239 graphics.setColor(getRoot().getForeground()); 240 final AlphaComposite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f); 241 graphics.setComposite(alpha); 242 graphics.drawLine((int) startX, Utils.getY(getRect()), (int) startX, 243 Utils.getY(getRect()) + getRect().height + extendHeight); 244 str = TimeUtils.getSecondFromNSecond(tmpNs); 245 if (str.isEmpty()) { 246 str = "0s"; 247 } 248 graphics.setColor(getRoot().getForeground()); 249 final AlphaComposite alphaFull = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f); 250 graphics.setComposite(alphaFull); 251 String timS = "+" + str; 252 Rectangle2D bounds = graphics.getFontMetrics(Final.SMALL_FONT).getStringBounds(timS, graphics); 253 graphics.drawString(timS, (int) startX, (int) (Utils.getY(getRect()) + bounds.getHeight())); 254 startX += realW; 255 tmpNs += scale; 256 } 257 } 258 drawFocusFlag(Graphics2D graphics)259 private void drawFocusFlag(Graphics2D graphics) { 260 if (focusFlag != null && focusFlag.isVisible()) { 261 final int side = 10; 262 graphics.setColor(focusFlag.getColor()); 263 graphics.setStroke(BOLD_STORE); 264 graphics.drawLine(Utils.getX(focusFlag.rect) + Utils.getX(getRect()), 265 Utils.getY(getRect()) + getRect().height / 2, Utils.getX(getRect()) + Utils.getX(focusFlag.rect), 266 Utils.getY(getRect()) + getRect().height + extendHeight); 267 graphics.fillRect(Utils.getX(focusFlag.rect) + Utils.getX(getRect()), 268 Utils.getY(getRect()) + getRect().height / 2, side, side); 269 final int offset = 7; 270 graphics.fillRect(Utils.getX(focusFlag.rect) + Utils.getX(getRect()) + offset, 271 Utils.getY(getRect()) + getRect().height / 2 + 2, side, side); 272 } 273 } 274 275 /** 276 * Convert x coordinate according to time 277 * 278 * @param ns ns 279 * @return int int 280 */ getX(final long ns)281 public int getX(final long ns) { 282 return (int) ((ns - leftNS) * getRect().width / ((rightNS - leftNS) * 1.0)); 283 } 284 285 /** 286 * Mouse movement event 287 * 288 * @param event event 289 */ mouseMoved(final MouseEvent event)290 public void mouseMoved(final MouseEvent event) { 291 final int leftW = 200; 292 if (selectY > Utils.getY(getRect()) && selectY < Utils.getY(getRect()) + getRect().height 293 && event.getX() >= leftW) { 294 Optional<FlagBean> first = flags.stream().filter( 295 bean -> event.getX() >= Utils.getX(bean.rect) + Utils.getX(getRect()) 296 && event.getX() <= Utils.getX(bean.rect) + Utils.getX(getRect()) + bean.rect.width).findFirst(); 297 if (first.isPresent()) { 298 focusFlag.setVisible(false); 299 } else { 300 focusFlag.setVisible(true); 301 focusFlag.rect 302 .setLocation(event.getX() - Utils.getX(getRect()), Utils.getY(getRect()) + getRect().height / 2); 303 focusFlag.rect.width = 17; 304 focusFlag.rect.height = getRect().height / 2; 305 } 306 } else { 307 focusFlag.setVisible(false); 308 } 309 getRoot().repaint(); 310 } 311 312 /** 313 * Mouse click event 314 * 315 * @param event event 316 */ mouseClicked(final MouseEvent event)317 public void mouseClicked(final MouseEvent event) { 318 if (edgeInspect(event)) { 319 final int leftW = 200; 320 if (selectY > Utils.getY(getRect()) && selectY < Utils.getY(getRect()) + getRect().height 321 && event.getX() >= leftW) { 322 Optional<FlagBean> first = flags.stream().filter( 323 bean -> event.getX() >= Utils.getX(bean.rect) + Utils.getX(getRect()) 324 && event.getX() <= Utils.getX(bean.rect) + Utils.getX(getRect()) + bean.rect.width).findFirst(); 325 if (first.isPresent()) { 326 FlagBean flagBean = first.get(); 327 flagBean.onClick(event); 328 } else { 329 FlagBean flagBean = new FlagBean(); 330 flagBean.root = getRoot(); 331 flagBean.rect.setLocation(event.getX() - Utils.getX(getRect()), 332 Utils.getY(getRect()) + getRect().height / 2); 333 flagBean.setEventListener(this); 334 flagBean.rect.width = 17; 335 flagBean.rect.height = getRect().height / 2; 336 flagBean.setNs( 337 (long) ((rightNS - leftNS) / (getRect().width * 1.0) * Utils.getX(flagBean.rect)) + leftNS); 338 flagBean.setVisible(true); 339 flagBean.onClick(event); 340 flags.add(flagBean); 341 } 342 repaint(); 343 } 344 } 345 } 346 347 /** 348 * FlagBean object click event 349 * 350 * @param event event 351 * @param data data 352 */ 353 @Override click(final MouseEvent event, final FlagBean data)354 public void click(final MouseEvent event, final FlagBean data) { 355 if (AnalystPanel.getiFlagClick() != null) { 356 AnalystPanel.getiFlagClick().click(data); 357 } 358 } 359 360 /** 361 * Loss of focus event 362 * 363 * @param event event 364 * @param data data 365 */ 366 @Override blur(final MouseEvent event, final FlagBean data)367 public void blur(final MouseEvent event, final FlagBean data) { 368 } 369 370 /** 371 * Get focus event 372 * 373 * @param event event 374 * @param data data 375 */ 376 @Override focus(final MouseEvent event, final FlagBean data)377 public void focus(final MouseEvent event, final FlagBean data) { 378 } 379 380 /** 381 * Mouse move event 382 * 383 * @param event event 384 * @param data data 385 */ 386 @Override mouseMove(final MouseEvent event, final FlagBean data)387 public void mouseMove(final MouseEvent event, final FlagBean data) { 388 } 389 390 /** 391 * Remove the flag mark 392 * 393 * @param data data 394 */ 395 @Override delete(final FlagBean data)396 public void delete(final FlagBean data) { 397 flags.removeIf(bean -> bean.getNs() == data.getNs()); 398 repaint(); 399 } 400 401 /** 402 * Gets the value of leftNS . 403 * 404 * @return the value of long 405 */ getLeftNS()406 public long getLeftNS() { 407 return leftNS; 408 } 409 410 /** 411 * Sets the leftNS . 412 * <p>You can use getLeftNS() to get the value of leftNS</p> 413 * 414 * @param ns ns 415 */ setLeftNS(final long ns)416 public void setLeftNS(final long ns) { 417 this.leftNS = ns; 418 } 419 420 /** 421 * Gets the value of rightNS . 422 * 423 * @return the value of long 424 */ getRightNS()425 public long getRightNS() { 426 return rightNS; 427 } 428 429 /** 430 * Sets the rightNS . 431 * <p>You can use getRightNS() to get the value of rightNS</p> 432 * 433 * @param ns ns 434 */ setRightNS(final long ns)435 public void setRightNS(final long ns) { 436 this.rightNS = ns; 437 } 438 439 /** 440 * Gets the value of scale . 441 * 442 * @return the value of long 443 */ getScale()444 public long getScale() { 445 return scale; 446 } 447 448 /** 449 * Sets the scale . 450 * <p>You can use getScale() to get the value of scale</p> 451 * 452 * @param scale scale 453 */ setScale(final long scale)454 public void setScale(final long scale) { 455 this.scale = scale; 456 } 457 458 /** 459 * Gets the value of centerNS . 460 * 461 * @return the value of long 462 */ getCenterNS()463 public long getCenterNS() { 464 return centerNS; 465 } 466 467 /** 468 * Sets the centerNS . 469 * <p>You can use getCenterNS() to get the value of centerNS</p> 470 * 471 * @param ns ns 472 */ setCenterNS(final long ns)473 public void setCenterNS(final long ns) { 474 this.centerNS = ns; 475 } 476 477 /** 478 * Gets the value of extendHeight . 479 * 480 * @return the value of int 481 */ getExtendHeight()482 public int getExtendHeight() { 483 return extendHeight; 484 } 485 486 /** 487 * Sets the extendHeight . 488 * <p>You can use getExtendHeight() to get the value of extendHeight</p> 489 * 490 * @param height height 491 */ setExtendHeight(final int height)492 public void setExtendHeight(final int height) { 493 this.extendHeight = height; 494 } 495 496 /** 497 * Gets the value of selectX . 498 * 499 * @return the value of int 500 */ getSelectX()501 public int getSelectX() { 502 return selectX; 503 } 504 505 /** 506 * Sets the selectX . 507 * <p>You can use getSelectX() to get the value of selectX</p> 508 * 509 * @param select select 510 */ setSelectX(final int select)511 public void setSelectX(final int select) { 512 this.selectX = select; 513 } 514 515 /** 516 * Gets the value of selectY . 517 * 518 * @return the value of int 519 */ getSelectY()520 public int getSelectY() { 521 return selectY; 522 } 523 524 /** 525 * Sets the selectY . 526 * <p>You can use getSelectY() to get the value of selectY</p> 527 * 528 * @param select select 529 */ setSelectY(final int select)530 public void setSelectY(final int select) { 531 this.selectY = select; 532 } 533 534 /** 535 * time range change listener 536 */ 537 public interface IChange { 538 /** 539 * Time range change monitoring 540 * 541 * @param startNS Starting time 542 * @param endNS End Time 543 */ change(long startNS, long endNS)544 void change(long startNS, long endNS); 545 } 546 } 547