1/* 2 * Copyright (C) 2022 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 16import { BaseElement, element } from '../../../base-ui/BaseElement'; 17import { HdcDeviceManager } from '../../../hdc/HdcDeviceManager'; 18import { SpRecordTrace } from '../SpRecordTrace'; 19import { DataMessage } from '../../../hdc/message/DataMessage'; 20 21@element('sp-web-hdc-shell') 22export class SpWebHdcShell extends BaseElement { 23 private static MAX_DISPLAY_ROWS = 1000; 24 private static MAX_SAVE_SIZE = 2097152; 25 shellDiv: HTMLDivElement | null | undefined; 26 currentScreenRemain: number = 0; 27 private shellCanvas: HTMLCanvasElement | null | undefined; 28 private shellCanvasCtx: CanvasRenderingContext2D | null | undefined; 29 private resultStr = ''; 30 private sendCallBack: ((keyboardEvent: KeyboardEvent | string) => void | undefined) | undefined; 31 private startShellDevice = ''; 32 private intervalId: number | undefined; 33 private skipFlag: number[] = [7]; 34 private clearFlag: number[] = [27, 91, 50, 74, 27, 91, 72]; 35 private CRLFFlag: number[] = [13, 13, 10]; 36 private startRealTimeFlag: number[] = [27, 91, 115]; 37 private endRealTimeFlag: number[] = [27, 91, 117]; 38 private clearRealTimeFlag: number[] = [27, 91, 72, 27, 91, 74]; 39 private ctrlCFlag: number[] = [13, 10, 35, 32]; 40 private points: Point | undefined; 41 private forwardFlag: boolean = false; 42 private cursorIndex: number = 3; 43 private shellStrLength: number = 0; 44 private textY: number = 0; 45 private cursorRow: string = ''; 46 private textDecoder: TextDecoder = new TextDecoder(); 47 private isDragging: boolean = false; 48 private static TOP_OFFSET = 48; 49 private static FIRST_ROW_OFFSET = 32; 50 private static LAST_ROW_OFFSET = 40; 51 private static MULTI_LINE_FLAG = '<\b'; 52 private static LINE_BREAK_LENGTH = 2; 53 private static LEFT_OFFSET = 48; 54 private realTimeResult: string | null | undefined = ''; 55 private startRealTime: boolean = false; 56 private prevTextY: number = 0; 57 58 public initElements(): void { 59 this.shellCanvas = this.shadowRoot!.querySelector<HTMLCanvasElement>('#shell_cmd'); 60 this.shellCanvasCtx = this.shellCanvas!.getContext('2d'); 61 this.shellCanvasCtx!.fillStyle = '#000'; 62 63 this.shellCanvasCtx!.fillRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height); 64 this.shellDiv = this.shadowRoot!.querySelector<HTMLDivElement>('.shell_cmd_div'); 65 this.shellCanvasAddMouseListener(); 66 this.shellCanvas!.addEventListener('contextmenu', (event) => { 67 event.preventDefault(); 68 event.stopPropagation(); 69 }); 70 this.shellCanvas!.addEventListener('keydown', async (keyboardEvent) => { 71 keyboardEvent.preventDefault(); 72 if (keyboardEvent.ctrlKey && keyboardEvent.code === 'KeyC' && this.points) { 73 let rowText: string = this.getSelectedText(); 74 this.points = undefined; 75 await navigator.clipboard.writeText(rowText); 76 } else { 77 if (this.sendCallBack) { 78 this.sendCallBack(keyboardEvent); 79 } 80 } 81 }); //@ts-ignore 82 window.subscribe(window.SmartEvent.UI.DeviceConnect, (deviceName: string) => { 83 if (deviceName) { 84 this.hdcShellFocus(); 85 } 86 }); 87 window.subscribe(window.SmartEvent.UI.DeviceDisConnect, () => { 88 this.clear(); 89 }); 90 this.shellCanvas!.addEventListener('blur', () => { 91 if (this.intervalId) { 92 window.clearInterval(this.intervalId); 93 } 94 this.shellCanvasCtx!.clearRect(this.shellStrLength, this.textY, 12, 3); 95 }); 96 new ResizeObserver(() => { 97 this.resizeCanvas(); 98 this.refreshShellPage(true); 99 }).observe(this); 100 } 101 102 resizeCanvas(): void { 103 if (this.shellCanvas !== null && this.shellCanvas !== undefined) { 104 this.shellCanvas.width = this.shellCanvas.clientWidth; 105 this.shellCanvas.height = this.shellCanvas.clientHeight; 106 } 107 } 108 109 clear(): void { 110 this.sendCallBack = undefined; 111 this.resultStr = ''; 112 this.cursorRow = ''; 113 this.shellCanvasCtx!.clearRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height); 114 this.shellCanvasCtx!.fillStyle = '#000'; 115 this.shellCanvasCtx!.fillRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height); 116 window.clearInterval(this.intervalId); 117 } 118 119 public hdcShellFocus(): void { 120 HdcDeviceManager.connect(SpRecordTrace.serialNumber).then((connected) => { 121 if (connected) { 122 if (this.sendCallBack && this.startShellDevice === SpRecordTrace.serialNumber) { 123 this.shellCanvas!.focus(); 124 this.refreshShellPage(true); 125 } else { 126 this.clear(); 127 this.sendCallBack = HdcDeviceManager.startShell((result: DataMessage) => { 128 if (result.channelClose) { 129 this.clear(); 130 return; 131 } 132 this.startShellDevice = SpRecordTrace.serialNumber; 133 this.handleHdcRecvData(result); 134 }); 135 this.shellCanvas!.focus(); 136 this.refreshShellPage(true); 137 } 138 } else { 139 this.clear(); 140 } 141 }); 142 } 143 144 arrayBufferCompare(compareA: ArrayBuffer, compareB: number[]): boolean { 145 const arrayA = new Uint8Array(compareA); 146 if (arrayA.length === compareB.length) { 147 for (let i = 0; i < arrayA.length; i++) { 148 const dd = arrayA[i]; 149 if (dd !== compareB[i]) { 150 return false; 151 } 152 } 153 return true; 154 } 155 return false; 156 } 157 158 getSelectedText(): string { 159 let selectedText = ''; 160 let textLines = [...this.finalArr]; 161 let startX = this.points!.startX!; 162 let startY = this.points!.startY!; 163 let endX = this.points!.endX!; 164 let endY = this.points!.endY!; 165 let depth = Math.ceil((endY - startY) / 16); 166 let index = 0; 167 for (let i = 0; i < textLines.length; i++) { 168 let line = textLines[i]; 169 let x = SpWebHdcShell.LEFT_OFFSET; 170 let textFirstRowY = 16 * i + SpWebHdcShell.FIRST_ROW_OFFSET; 171 let textLastRowY = 16 * i + SpWebHdcShell.LAST_ROW_OFFSET; 172 let textEndY = 16 * i + SpWebHdcShell.TOP_OFFSET; 173 let w = this.shellCanvasCtx!.measureText(line).width; 174 if ( 175 (startY < textEndY && endY >= textEndY) || 176 (startY > textFirstRowY && startY < textEndY) || 177 (endY > textLastRowY && endY < textEndY) 178 ) { 179 index++; 180 if (index === 1) { 181 if (depth > 1) { 182 selectedText += 183 line.slice(this.getCurrentLineBackSize(line, startX - x, true)) + (endX < x + w ? '\n' : ''); 184 } else { 185 selectedText += `${line.slice( 186 this.getCurrentLineBackSize(line, startX - x, true), 187 this.getCurrentLineBackSize(line, endX - x, false) 188 )}\n`; 189 } 190 } else if (index === depth) { 191 selectedText += `${line.slice(0, this.getCurrentLineBackSize(line, endX - x, false))}\n`; 192 } else { 193 selectedText += `${line}\n`; 194 } 195 } 196 } 197 return selectedText.trim(); 198 } 199 200 forwardSelected(startX: number, startY: number, endX: number, endY: number): void { 201 //左边界x为SpWebHdcShell.LEFT_OFFSET,右边界为this.shellCanvas!.width 202 let depth = Math.ceil((endY - startY) / 16); 203 let startPointX = 0; 204 let startPointY = 0; 205 let endPointX = 0; 206 let endPointY = 0; 207 if (depth <= 1) { 208 this.shellCanvasCtx!.fillRect(startX, startY, endX - startX, endY - startY); 209 startPointX = startX; 210 startPointY = startY; 211 endPointX = endX; 212 endPointY = endY; 213 } else { 214 //绘制多行 215 for (let index = 1; index <= depth; index++) { 216 //第一行,绘起始点到canvas右边界矩形 217 if (index === 1) { 218 this.shellCanvasCtx!.fillRect(startX, startY, this.shellCanvas!.width - startX, index * 16); 219 startPointX = startX; 220 startPointY = startY; 221 } else if (index === depth) { 222 //最后一行,canvas左边界到结束点矩形 223 this.shellCanvasCtx!.fillRect( 224 SpWebHdcShell.LEFT_OFFSET, 225 startY + (index - 1) * 16, 226 endX - SpWebHdcShell.LEFT_OFFSET, 227 endY - (startY + (index - 1) * 16) 228 ); 229 endPointX = endX; 230 endPointY = endY; 231 } else { 232 //中间行,canvas的左边界到右边界的矩形 233 this.shellCanvasCtx!.fillRect( 234 SpWebHdcShell.LEFT_OFFSET, 235 startY + (index - 1) * 16, 236 this.shellCanvas!.width, 237 16 238 ); 239 } 240 } 241 } 242 this.points = { startX: startPointX, startY: startPointY, endX: endPointX, endY: endPointY }; 243 } 244 245 getCurrentLineBackSize(currentLine: string, maxBackSize: number, isStart: boolean): number { 246 let fillText = ''; 247 let strings = currentLine.split(''); 248 for (let index = 0; index < strings.length; index++) { 249 let text = strings[index]; 250 if ( 251 this.shellCanvasCtx!.measureText(fillText).width < maxBackSize && 252 this.shellCanvasCtx!.measureText(fillText + text).width >= maxBackSize 253 ) { 254 if (!isStart) { 255 fillText += text; 256 } 257 break; 258 } 259 fillText += text; 260 } 261 return fillText.length; 262 } 263 264 reverseSelected(startX: number, startY: number, endX: number, endY: number): void { 265 //左边界x为SpWebHdcShell.LEFT_OFFSET,右边界为this.shellCanvas!.width 266 let depth = Math.ceil((startY - endY) / 16); 267 let startPointX = 0; 268 let startPointY = 0; 269 let endPointX = 0; 270 let endPointY = 0; 271 if (depth <= 1) { 272 this.shellCanvasCtx!.fillRect(endX, endY, startX - endX, startY - endY); 273 startPointX = endX; 274 startPointY = endY; 275 endPointX = startX; 276 endPointY = startY; 277 } else { 278 //绘制多行 279 for (let index = 1; index <= depth; index++) { 280 //第一行,绘起始点到canvas左边界矩形 281 if (index === 1) { 282 this.shellCanvasCtx!.fillRect(SpWebHdcShell.LEFT_OFFSET, startY - 16, startX - SpWebHdcShell.LEFT_OFFSET, 16); 283 endPointX = startX; 284 endPointY = startY; 285 } else if (index === depth) { 286 //最后一行,canvas右边界到结束点矩形 287 this.shellCanvasCtx!.fillRect(endX, endY, this.shellCanvas!.width - endX, startY - (index - 1) * 16 - endY); 288 startPointX = endX; 289 startPointY = endY; 290 } else { 291 this.shellCanvasCtx!.fillRect(SpWebHdcShell.LEFT_OFFSET, startY - index * 16, this.shellCanvas!.width, 16); 292 this.shellCanvasCtx!.textBaseline = 'middle'; 293 } 294 } 295 } 296 this.points = { startX: startPointX, startY: startPointY, endX: endPointX, endY: endPointY }; 297 } 298 299 private singleLineToMultiLine(shellStr: string, foundationWidth: number, maxWidth: number): string[] { 300 let result = []; 301 while (shellStr.length * foundationWidth > maxWidth) { 302 let bfb = maxWidth / (shellStr.length * foundationWidth); 303 let cutIndex = Math.floor(shellStr.length * bfb); 304 let ss = shellStr.substring(0, cutIndex); 305 result.push(ss); 306 shellStr = shellStr.substring(cutIndex); 307 } 308 if (shellStr.length > 0) { 309 result.push(shellStr); 310 } 311 return result; 312 } 313 314 private finalArr: Array<string> = []; 315 316 private drawShellPage(resultStrArr: string[]): void { 317 let maxWidth = this.shellCanvas!.width; 318 let foundationWidth = Math.ceil(this.shellCanvasCtx!.measureText(' ').width); 319 for (let index = 0; index < resultStrArr.length - 1; index++) { 320 let shellStr = resultStrArr[index]; 321 let strWidth = this.shellCanvasCtx!.measureText(shellStr).width; 322 if (strWidth > maxWidth) { 323 let lines = this.singleLineToMultiLine(shellStr, foundationWidth, maxWidth - SpWebHdcShell.LEFT_OFFSET); 324 this.finalArr.push(...lines); 325 } else { 326 this.finalArr.push(shellStr); 327 } 328 } 329 if (this.finalArr.length > SpWebHdcShell.MAX_DISPLAY_ROWS) { 330 this.finalArr.splice(0, this.finalArr.length - SpWebHdcShell.MAX_DISPLAY_ROWS + 1); 331 } 332 this.shellCanvasCtx!.fillStyle = '#fff'; 333 this.shellCanvasCtx!.font = '16px serif'; 334 this.textY = SpWebHdcShell.TOP_OFFSET; 335 this.finalArr.push(this.cursorRow); 336 for (let index: number = 0; index < this.finalArr.length; index++) { 337 let shellStr: string = this.finalArr[index]; 338 this.textY = SpWebHdcShell.TOP_OFFSET + index * 16; 339 this.shellCanvasCtx!.fillText(shellStr, SpWebHdcShell.LEFT_OFFSET, this.textY); 340 } 341 } 342 343 private drawCursorStyle(): void { 344 if (this.intervalId) { 345 window.clearInterval(this.intervalId); 346 } 347 let needClear = false; 348 this.intervalId = window.setInterval(() => { 349 if (needClear) { 350 needClear = false; 351 this.shellCanvasCtx!.fillStyle = '#000'; 352 this.shellCanvasCtx!.fillRect(this.shellStrLength, this.textY, 12, 3); 353 } else { 354 needClear = true; 355 this.shellCanvasCtx!.fillStyle = '#fff'; 356 this.shellCanvasCtx!.fillRect(this.shellStrLength, this.textY, 12, 3); 357 } 358 }, 500); 359 } 360 361 refreshShellPage(scroller: boolean): void { 362 try { 363 if (this.resultStr.length === 0 && this.cursorRow.length === 0) { 364 return; 365 } 366 this.shellCanvasCtx!.clearRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height); 367 this.shellCanvasCtx!.fillStyle = '#000'; 368 this.shellCanvasCtx!.fillRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height); 369 let resultStrArr = this.resultStr.split('\r\n'); 370 if (this.realTimeResult !== '') { 371 resultStrArr = (this.resultStr + this.realTimeResult).split('\r\n'); 372 } 373 this.finalArr = []; 374 if (this.shellCanvas!.width > 0) { 375 this.drawShellPage(resultStrArr); 376 this.shellStrLength = 377 this.shellCanvasCtx!.measureText(this.cursorRow.slice(0, this.cursorIndex)).width + SpWebHdcShell.LEFT_OFFSET; 378 // 记录前一次滚动条的位置 379 this.prevTextY = this.shellDiv!.scrollTop + this.shellDiv!.clientHeight - 3; 380 if (scroller && this.textY > this.shellDiv!.clientHeight && this.textY > this.prevTextY) { 381 this.shellDiv!.scrollTop = this.textY - this.shellDiv!.clientHeight + 3; 382 this.currentScreenRemain = this.shellDiv!.scrollTop; 383 } 384 this.drawCursorStyle(); 385 } 386 } catch (e) {} 387 } 388 389 public initHtml(): string { 390 return ` 391 <style> 392 :host{ 393 display: block; 394 border-radius: 0 16px 16px 0; 395 width: 100%; 396 position: relative; 397 } 398 .shell_cmd_div { 399 width: 90%; 400 margin-left: 5%; 401 margin-top: 3%; 402 overflow-y: scroll; 403 height: 40rem; 404 background: #000; 405 border: 1px solid var(--dark-color1,#4D4D4D); 406 border-radius: 16px; 407 } 408 ::-webkit-scrollbar{ 409 width: 13px; 410 height: 10px; 411 background-color: #FFFFFF; 412 } 413 ::-webkit-scrollbar-track{ 414 border-top-right-radius: 16px; 415 border-bottom-right-radius: 16px; 416 background-color: #000000; 417 } 418 ::-webkit-scrollbar-thumb{ 419 background: #5A5A5A; 420 border-radius: 6px; 421 } 422 canvas { 423 display: inline-block; 424 outline: none; 425 } 426 427 </style> 428 <div class="shell_cmd_div"> 429 <canvas id="shell_cmd" style="width: 100%;height:${16000 + SpWebHdcShell.TOP_OFFSET}px;" tabindex="0"></canvas> 430 </div> 431 `; 432 } 433 434 private refreshCurrentRow(): void { 435 let lastRow: string = this.resultStr; 436 if (this.resultStr.lastIndexOf('\r\n') !== -1) { 437 lastRow = this.resultStr.substring(this.resultStr.lastIndexOf('\r\n') + 2); 438 } 439 let currentRow: string[] = [...lastRow]; 440 let result: string[] = []; 441 this.cursorIndex = 0; 442 for (let index: number = 0; index < currentRow.length; index++) { 443 let currentResult: string = currentRow[index]; 444 if (currentResult === '\b') { 445 this.cursorIndex--; 446 } else { 447 result[this.cursorIndex] = currentResult; 448 this.cursorIndex++; 449 } 450 } 451 this.cursorRow = result.join(''); 452 } 453 454 private handleHdcRecvData(result: DataMessage): void { 455 const resData = result.getData(); 456 if (resData) { 457 if (this.arrayBufferCompare(resData, this.skipFlag)) { 458 return; 459 } else if (this.arrayBufferCompare(resData, this.clearFlag)) { 460 this.resultStr = ''; 461 this.shellDiv!.scrollTop = 0; 462 this.cursorIndex = 3; 463 } else if (this.arrayBufferCompare(resData, this.CRLFFlag)) { 464 if (this.resultStr.lastIndexOf('\r\n') !== -1) { 465 this.resultStr = this.resultStr.substring(0, this.resultStr.lastIndexOf('\r\n') + 2) + this.cursorRow; 466 } else { 467 this.resultStr = this.cursorRow; 468 } 469 this.resultStr += result.getDataToString(); 470 this.cursorIndex = 3; 471 } else { 472 if (this.resultStr.length > SpWebHdcShell.MAX_SAVE_SIZE) { 473 this.resultStr = this.resultStr.substring(this.resultStr.length / 2); 474 } 475 const arrayA = new Uint8Array(resData); 476 if (arrayA[0] === 13 && arrayA[1] !== 10 && arrayA[1] !== 13) { 477 this.hdcRecvEnterAndBracket(arrayA, result); 478 } else if (this.isStartWidthArrayBuffer(arrayA, this.startRealTimeFlag)) { 479 let lastIndex = this.getLastRestorationIndex(arrayA, this.endRealTimeFlag); 480 this.realTimeResult = this.removeTextAndColorSequenceStr( 481 this.textDecoder.decode(arrayA.slice(lastIndex, arrayA.length)) 482 ); 483 this.startRealTime = true; 484 } else if (this.isStartWidthArrayBuffer(arrayA, this.clearRealTimeFlag)) { 485 this.realTimeResult = this.removeTextAndColorSequenceStr( 486 this.textDecoder.decode(arrayA.slice(6, arrayA.length)) 487 ); 488 this.startRealTime = true; 489 } else { 490 if (this.isStartWidthArrayBuffer(arrayA, this.ctrlCFlag)) { 491 this.resultStr += this.realTimeResult; 492 this.startRealTime = false; 493 } 494 this.hdcRecvRealMessage(result); 495 } 496 } 497 this.resultStr = this.removeTextAndColorSequenceStr(this.resultStr); 498 this.refreshCurrentRow(); 499 this.refreshShellPage(true); 500 } 501 } 502 503 private hdcRecvEnterAndBracket(arrayA: Uint8Array, result: DataMessage): void { 504 const index = this.resultStr.lastIndexOf('\n'); 505 const resultStrLength = this.resultStr.length; 506 if (index > -1 && resultStrLength > index) { 507 this.resultStr = this.resultStr.substring(0, index + 1) + this.textDecoder.decode(arrayA.slice(1, arrayA.length)); 508 } else { 509 if (this.resultStr.split('\n').length === 1) { 510 const index = this.cursorRow.lastIndexOf('\n'); 511 this.cursorRow = 512 this.cursorRow.substring(0, index + 1) + this.textDecoder.decode(arrayA.slice(1, arrayA.length)); 513 this.resultStr = this.cursorRow; 514 } else { 515 this.resultStr += result.getDataToString(); 516 } 517 } 518 this.realTimeResult = ''; 519 } 520 521 private hdcRecvRealMessage(result: DataMessage): void { 522 if (this.startRealTime) { 523 if (result.getDataToString().includes(SpWebHdcShell.MULTI_LINE_FLAG)) { 524 this.realTimeResult += result.getDataToString().substring(result.getDataToString().indexOf('\r')); 525 } else { 526 this.realTimeResult += result.getDataToString(); 527 } 528 this.realTimeResult = this.removeTextAndColorSequenceStr(this.realTimeResult!); 529 } else { 530 this.realTimeResult = ''; 531 if (result.getDataToString().includes(SpWebHdcShell.MULTI_LINE_FLAG)) { 532 // 获取所有内容,不包括最后一行数据 533 this.resultStr = this.resultStr.substring( 534 0, 535 this.resultStr.lastIndexOf('\r\n') + SpWebHdcShell.LINE_BREAK_LENGTH 536 ); 537 // 多行情况不能直接拼接返回数据 538 this.resultStr += result.getDataToString().substring(result.getDataToString().indexOf('\r')); 539 } else { 540 this.resultStr += result.getDataToString(); 541 } 542 } 543 } 544 545 private removeTextAndColorSequenceStr(currentStr: string): string { 546 return currentStr.replace(new RegExp(/\x1B\[[0-9;]*[a-zA-Z]/g), ''); 547 } 548 549 private isStartWidthArrayBuffer(sourceArray: Uint8Array, compareArray: number[]): boolean { 550 for (let index = 0; index < compareArray.length; index++) { 551 if (sourceArray[index] !== compareArray[index]) { 552 return false; 553 } 554 } 555 return true; 556 } 557 558 private getLastRestorationIndex(sourceArray: Uint8Array, compareArray: number[]): number { 559 let lastIndex = -1; 560 for (let index = sourceArray.length - 1; index >= 0; index--) { 561 if (sourceArray[index] === compareArray[0]) { 562 let isLast = true; 563 for (let j = 1; j < compareArray.length; j++) { 564 if (sourceArray[index + j] !== compareArray[j]) { 565 isLast = false; 566 break; 567 } 568 } 569 if (isLast) { 570 lastIndex = index; 571 break; 572 } 573 } 574 } 575 return lastIndex + compareArray.length; 576 } 577 578 private shellCanvasAddMouseListener(): void { 579 let startX: number; 580 let startY: number; 581 let endX: number; 582 let endY: number; 583 this.shellCanvas!.addEventListener('mousedown', (event) => { 584 if (this.resultStr.length === 0 && this.cursorRow.length === 0) { 585 return; 586 } 587 this.isDragging = true; 588 startX = event.offsetX; 589 startY = event.offsetY; 590 this.refreshShellPage(false); 591 }); 592 this.shellCanvas!.addEventListener('mousemove', (event) => { 593 if (!this.isDragging) { 594 return; 595 } 596 if (this.resultStr.length === 0 && this.cursorRow.length === 0) { 597 return; 598 } 599 endX = event.offsetX; 600 endY = event.offsetY; 601 this.refreshShellPage(false); 602 this.points = undefined; 603 this.shellCanvasCtx!.fillStyle = 'rgba(128, 128, 128, 0.5)'; 604 if (endY > startY) { 605 this.forwardFlag = true; 606 this.forwardSelected(startX, startY, endX, endY); 607 } else { 608 this.forwardFlag = false; 609 this.reverseSelected(startX, startY, endX, endY); 610 } 611 }); 612 this.shellCanvasAddMouseUpListener(); 613 } 614 615 private shellCanvasAddMouseUpListener(): void { 616 this.shellCanvas!.addEventListener('mouseup', async (event) => { 617 if (!this.isDragging) { 618 return; 619 } 620 if (this.resultStr.length === 0 && this.cursorRow.length === 0) { 621 return; 622 } 623 this.isDragging = false; 624 //右键 625 if (event.button === 2) { 626 let text: string = await navigator.clipboard.readText(); 627 if (text) { 628 if (this.sendCallBack) { 629 this.sendCallBack(text); 630 } 631 return; 632 } 633 } 634 }); 635 } 636} 637 638export class Point { 639 startX: number | undefined; 640 startY: number | undefined; 641 endX: number | undefined; 642 endY: number | undefined; 643} 644