1/* 2 * Copyright (c) 2023-2023 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 */ 15const pasteboard = requireNapi('pasteboard'); 16const hilog = requireNapi('hilog'); 17const SymbolGlyphModifier = requireNapi('arkui.modifier').SymbolGlyphModifier; 18 19if (!('finalizeConstruction' in ViewPU.prototype)) { 20 Reflect.set(ViewPU.prototype, 'finalizeConstruction', () => { 21 }); 22} 23 24const WITHOUT_BUILDER = -2; 25const MAX_FONT_STANDARD = 1.0; 26const MAX_FONT_SCALE = 1.75; 27const SYMBOL_SIZE = 24; 28const defaultTheme = { 29 imageSize: 24, 30 buttonSize: 40, 31 menuSpacing: 8, 32 expandedOptionPadding: 4, 33 defaultMenuWidth: 224, 34 menuItemPadding: { 35 'id': -1, 36 'type': 10002, 37 params: ['sys.float.padding_level1'], 38 'bundleName': '__harDefaultBundleName__', 39 'moduleName': '__harDefaultModuleName__' 40 }, 41 imageFillColor: { 42 'id': -1, 43 'type': 10001, 44 params: ['sys.color.ohos_id_color_primary'], 45 'bundleName': '__harDefaultBundleName__', 46 'moduleName': '__harDefaultModuleName__' 47 }, 48 backGroundColor: { 49 'id': -1, 50 'type': 10001, 51 params: ['sys.color.ohos_id_color_dialog_bg'], 52 'bundleName': '__harDefaultBundleName__', 53 'moduleName': '__harDefaultModuleName__' 54 }, 55 iconBorderRadius: { 56 'id': -1, 57 'type': 10002, 58 params: ['sys.float.corner_radius_level2'], 59 'bundleName': '__harDefaultBundleName__', 60 'moduleName': '__harDefaultModuleName__' 61 }, 62 containerBorderRadius: { 63 'id': -1, 64 'type': 10002, 65 params: ['sys.float.corner_radius_level4'], 66 'bundleName': '__harDefaultBundleName__', 67 'moduleName': '__harDefaultModuleName__' 68 }, 69 borderWidth: { 70 'id': -1, 71 'type': 10002, 72 params: ['sys.float.ohos_id_menu_inner_border_width'], 73 'bundleName': '__harDefaultBundleName__', 74 'moduleName': '__harDefaultModuleName__' 75 }, 76 borderColor: { 77 'id': -1, 78 'type': 10001, 79 params: ['sys.color.ohos_id_menu_inner_border_color'], 80 'bundleName': '__harDefaultBundleName__', 81 'moduleName': '__harDefaultModuleName__' 82 }, 83 outlineWidth: { 84 'id': -1, 85 'type': 10002, 86 params: ['sys.float.ohos_id_menu_outer_border_width'], 87 'bundleName': '__harDefaultBundleName__', 88 'moduleName': '__harDefaultModuleName__' 89 }, 90 outlineColor: { 91 'id': -1, 92 'type': 10001, 93 params: ['sys.color.ohos_id_menu_outer_border_color'], 94 'bundleName': '__harDefaultBundleName__', 95 'moduleName': '__harDefaultModuleName__' 96 }, 97 cutIcon: { 98 'id': -1, 99 'type': 20000, 100 params: ['sys.media.ohos_ic_public_cut'], 101 'bundleName': '__harDefaultBundleName__', 102 'moduleName': '__harDefaultModuleName__' 103 }, 104 copyIcon: { 105 'id': -1, 106 'type': 20000, 107 params: ['sys.media.ohos_ic_public_copy'], 108 'bundleName': '__harDefaultBundleName__', 109 'moduleName': '__harDefaultModuleName__' 110 }, 111 pasteIcon: { 112 'id': -1, 113 'type': 20000, 114 params: ['sys.media.ohos_ic_public_paste'], 115 'bundleName': '__harDefaultBundleName__', 116 'moduleName': '__harDefaultModuleName__' 117 }, 118 selectAllIcon: { 119 'id': -1, 120 'type': 20000, 121 params: ['sys.media.ohos_ic_public_select_all'], 122 'bundleName': '__harDefaultBundleName__', 123 'moduleName': '__harDefaultModuleName__' 124 }, 125 shareIcon: { 126 'id': -1, 127 'type': 20000, 128 params: ['sys.media.ohos_ic_public_share'], 129 'bundleName': '__harDefaultBundleName__', 130 'moduleName': '__harDefaultModuleName__' 131 }, 132 translateIcon: { 133 'id': -1, 134 'type': 20000, 135 params: ['sys.media.ohos_ic_public_translate_c2e'], 136 'bundleName': '__harDefaultBundleName__', 137 'moduleName': '__harDefaultModuleName__' 138 }, 139 searchIcon: { 140 'id': -1, 141 'type': 20000, 142 params: ['sys.media.ohos_ic_public_search_filled'], 143 'bundleName': '__harDefaultBundleName__', 144 'moduleName': '__harDefaultModuleName__' 145 }, 146 arrowDownIcon: { 147 'id': -1, 148 'type': 20000, 149 params: ['sys.media.ohos_ic_public_arrow_down'], 150 'bundleName': '__harDefaultBundleName__', 151 'moduleName': '__harDefaultModuleName__' 152 }, 153 iconPanelShadowStyle: ShadowStyle.OUTER_DEFAULT_SM, 154 defaultSymbolTheme: { 155 fontSize: `${SYMBOL_SIZE}vp`, 156 fontColor: [{ 157 'id': -1, 158 'type': 10001, 159 params: ['sys.color.ohos_id_color_primary'], 160 'bundleName': '__harDefaultBundleName__', 161 'moduleName': '__harDefaultModuleName__' 162 }], 163 symbolCutIcon: new SymbolGlyphModifier({ 164 'id': -1, 165 'type': 40000, 166 params: ['sys.symbol.cut'], 167 'bundleName': '__harDefaultBundleName__', 168 'moduleName': '__harDefaultModuleName__' 169 }), 170 symbolCopyIcon: new SymbolGlyphModifier({ 171 'id': -1, 172 'type': 40000, 173 params: ['sys.symbol.plus_square_on_square'], 174 'bundleName': '__harDefaultBundleName__', 175 'moduleName': '__harDefaultModuleName__' 176 }), 177 symbolPasteIcon: new SymbolGlyphModifier({ 178 'id': -1, 179 'type': 40000, 180 params: ['sys.symbol.plus_square_dashed_on_square'], 181 'bundleName': '__harDefaultBundleName__', 182 'moduleName': '__harDefaultModuleName__' 183 }), 184 symbolSelectAllIcon: new SymbolGlyphModifier({ 185 'id': -1, 186 'type': 40000, 187 params: ['sys.symbol.checkmark_square_on_square'], 188 'bundleName': '__harDefaultBundleName__', 189 'moduleName': '__harDefaultModuleName__' 190 }), 191 symbolShareIcon: new SymbolGlyphModifier({ 192 'id': -1, 193 'type': 40000, 194 params: ['sys.symbol.share'], 195 'bundleName': '__harDefaultBundleName__', 196 'moduleName': '__harDefaultModuleName__' 197 }), 198 symbolTranslateIcon: new SymbolGlyphModifier({ 199 'id': -1, 200 'type': 40000, 201 params: ['sys.symbol.translate_c2e'], 202 'bundleName': '__harDefaultBundleName__', 203 'moduleName': '__harDefaultModuleName__' 204 }), 205 symbolSearchIcon: new SymbolGlyphModifier({ 206 'id': -1, 207 'type': 40000, 208 params: ['sys.symbol.magnifyingglass'], 209 'bundleName': '__harDefaultBundleName__', 210 'moduleName': '__harDefaultModuleName__' 211 }), 212 symbolArrowDownIcon: new SymbolGlyphModifier({ 213 'id': -1, 214 'type': 40000, 215 params: ['sys.symbol.chevron_down'], 216 'bundleName': '__harDefaultBundleName__', 217 'moduleName': '__harDefaultModuleName__' 218 }), 219 }, 220}; 221 222class SelectionMenuComponent extends ViewPU { 223 constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { 224 super(parent, __localStorage, elmtId, extraInfo); 225 if (typeof paramsLambda === 'function') { 226 this.paramsGenerator_ = paramsLambda; 227 } 228 this.editorMenuOptions = undefined; 229 this.expandedMenuOptions = undefined; 230 this.controller = undefined; 231 this.onPaste = undefined; 232 this.onCopy = undefined; 233 this.onCut = undefined; 234 this.onSelectAll = undefined; 235 this.theme = defaultTheme; 236 this.builder = this.CloserFun; 237 this.__showExpandedMenuOptions = new ObservedPropertySimplePU(false, this, 'showExpandedMenuOptions'); 238 this.__showCustomerIndex = new ObservedPropertySimplePU(-1, this, 'showCustomerIndex'); 239 this.__customerChange = new ObservedPropertySimplePU(false, this, 'customerChange'); 240 this.__cutAndCopyEnable = new ObservedPropertySimplePU(false, this, 'cutAndCopyEnable'); 241 this.__pasteEnable = new ObservedPropertySimplePU(false, this, 'pasteEnable'); 242 this.__visibilityValue = new ObservedPropertySimplePU(Visibility.Visible, this, 'visibilityValue'); 243 this.__fontScale = new ObservedPropertySimplePU(1, this, 'fontScale'); 244 this.__customMenuWidth = new ObservedPropertySimplePU(this.theme.defaultMenuWidth, this, 'customMenuWidth'); 245 this.__horizontalMenuHeight = new ObservedPropertySimplePU(0, this, 'horizontalMenuHeight'); 246 this.__horizontalMenuWidth = 247 new ObservedPropertySimplePU(this.theme.defaultMenuWidth, this, 'horizontalMenuWidth'); 248 this.fontWeightTable = 249 ['100', '200', '300', '400', '500', '600', '700', '800', '900', 'bold', 'normal', 'bolder', 'lighter', 250 'medium', 251 'regular']; 252 this.isFollowingSystemFontScale = false; 253 this.appMaxFontScale = 3.2; 254 this.setInitiallyProvidedValue(params); 255 this.finalizeConstruction(); 256 } 257 258 setInitiallyProvidedValue(params) { 259 if (params.editorMenuOptions !== undefined) { 260 this.editorMenuOptions = params.editorMenuOptions; 261 } 262 if (params.expandedMenuOptions !== undefined) { 263 this.expandedMenuOptions = params.expandedMenuOptions; 264 } 265 if (params.controller !== undefined) { 266 this.controller = params.controller; 267 } 268 if (params.onPaste !== undefined) { 269 this.onPaste = params.onPaste; 270 } 271 if (params.onCopy !== undefined) { 272 this.onCopy = params.onCopy; 273 } 274 if (params.onCut !== undefined) { 275 this.onCut = params.onCut; 276 } 277 if (params.onSelectAll !== undefined) { 278 this.onSelectAll = params.onSelectAll; 279 } 280 if (params.theme !== undefined) { 281 this.theme = params.theme; 282 } 283 if (params.builder !== undefined) { 284 this.builder = params.builder; 285 } 286 if (params.showExpandedMenuOptions !== undefined) { 287 this.showExpandedMenuOptions = params.showExpandedMenuOptions; 288 } 289 if (params.showCustomerIndex !== undefined) { 290 this.showCustomerIndex = params.showCustomerIndex; 291 } 292 if (params.customerChange !== undefined) { 293 this.customerChange = params.customerChange; 294 } 295 if (params.cutAndCopyEnable !== undefined) { 296 this.cutAndCopyEnable = params.cutAndCopyEnable; 297 } 298 if (params.pasteEnable !== undefined) { 299 this.pasteEnable = params.pasteEnable; 300 } 301 if (params.visibilityValue !== undefined) { 302 this.visibilityValue = params.visibilityValue; 303 } 304 if (params.fontScale !== undefined) { 305 this.fontScale = params.fontScale; 306 } 307 if (params.customMenuWidth !== undefined) { 308 this.customMenuWidth = params.customMenuWidth; 309 } 310 if (params.horizontalMenuHeight !== undefined) { 311 this.horizontalMenuHeight = params.horizontalMenuHeight; 312 } 313 if (params.horizontalMenuWidth !== undefined) { 314 this.horizontalMenuWidth = params.horizontalMenuWidth; 315 } 316 if (params.fontWeightTable !== undefined) { 317 this.fontWeightTable = params.fontWeightTable; 318 } 319 if (params.isFollowingSystemFontScale !== undefined) { 320 this.isFollowingSystemFontScale = params.isFollowingSystemFontScale; 321 } 322 if (params.appMaxFontScale !== undefined) { 323 this.appMaxFontScale = params.appMaxFontScale; 324 } 325 } 326 327 updateStateVars(params) { 328 } 329 330 purgeVariableDependenciesOnElmtId(rmElmtId) { 331 this.__showExpandedMenuOptions.purgeDependencyOnElmtId(rmElmtId); 332 this.__showCustomerIndex.purgeDependencyOnElmtId(rmElmtId); 333 this.__customerChange.purgeDependencyOnElmtId(rmElmtId); 334 this.__cutAndCopyEnable.purgeDependencyOnElmtId(rmElmtId); 335 this.__pasteEnable.purgeDependencyOnElmtId(rmElmtId); 336 this.__visibilityValue.purgeDependencyOnElmtId(rmElmtId); 337 this.__fontScale.purgeDependencyOnElmtId(rmElmtId); 338 this.__customMenuWidth.purgeDependencyOnElmtId(rmElmtId); 339 this.__horizontalMenuHeight.purgeDependencyOnElmtId(rmElmtId); 340 this.__horizontalMenuWidth.purgeDependencyOnElmtId(rmElmtId); 341 } 342 343 aboutToBeDeleted() { 344 this.__showExpandedMenuOptions.aboutToBeDeleted(); 345 this.__showCustomerIndex.aboutToBeDeleted(); 346 this.__customerChange.aboutToBeDeleted(); 347 this.__cutAndCopyEnable.aboutToBeDeleted(); 348 this.__pasteEnable.aboutToBeDeleted(); 349 this.__visibilityValue.aboutToBeDeleted(); 350 this.__fontScale.aboutToBeDeleted(); 351 this.__customMenuWidth.aboutToBeDeleted(); 352 this.__horizontalMenuHeight.aboutToBeDeleted(); 353 this.__horizontalMenuWidth.aboutToBeDeleted(); 354 SubscriberManager.Get().delete(this.id__()); 355 this.aboutToBeDeletedInternal(); 356 } 357 358 CloserFun(parent = null) { 359 } 360 361 get showExpandedMenuOptions() { 362 return this.__showExpandedMenuOptions.get(); 363 } 364 365 set showExpandedMenuOptions(newValue) { 366 this.__showExpandedMenuOptions.set(newValue); 367 } 368 369 get showCustomerIndex() { 370 return this.__showCustomerIndex.get(); 371 } 372 373 set showCustomerIndex(newValue) { 374 this.__showCustomerIndex.set(newValue); 375 } 376 377 get customerChange() { 378 return this.__customerChange.get(); 379 } 380 381 set customerChange(newValue) { 382 this.__customerChange.set(newValue); 383 } 384 385 get cutAndCopyEnable() { 386 return this.__cutAndCopyEnable.get(); 387 } 388 389 set cutAndCopyEnable(newValue) { 390 this.__cutAndCopyEnable.set(newValue); 391 } 392 393 get pasteEnable() { 394 return this.__pasteEnable.get(); 395 } 396 397 set pasteEnable(newValue) { 398 this.__pasteEnable.set(newValue); 399 } 400 401 get visibilityValue() { 402 return this.__visibilityValue.get(); 403 } 404 405 set visibilityValue(newValue) { 406 this.__visibilityValue.set(newValue); 407 } 408 409 get fontScale() { 410 return this.__fontScale.get(); 411 } 412 413 set fontScale(newValue) { 414 this.__fontScale.set(newValue); 415 } 416 417 get customMenuWidth() { 418 return this.__customMenuWidth.get(); 419 } 420 421 set customMenuWidth(newValue) { 422 this.__customMenuWidth.set(newValue); 423 } 424 425 get horizontalMenuHeight() { 426 return this.__horizontalMenuHeight.get(); 427 } 428 429 set horizontalMenuHeight(newValue) { 430 this.__horizontalMenuHeight.set(newValue); 431 } 432 433 get horizontalMenuWidth() { 434 return this.__horizontalMenuWidth.get(); 435 } 436 437 set horizontalMenuWidth(newValue) { 438 this.__horizontalMenuWidth.set(newValue); 439 } 440 441 aboutToAppear() { 442 if (this.controller) { 443 let richEditorSelection = this.controller.getSelection(); 444 let start = richEditorSelection.selection[0]; 445 let end = richEditorSelection.selection[1]; 446 if (start !== end) { 447 this.cutAndCopyEnable = true; 448 } 449 if (start === 0 && this.controller.getSpans({ start: end + 1, end: end + 1 }).length === 0) { 450 this.visibilityValue = Visibility.None; 451 } else { 452 this.visibilityValue = Visibility.Visible; 453 } 454 } else if (this.expandedMenuOptions && this.expandedMenuOptions.length > 0) { 455 this.showExpandedMenuOptions = true; 456 } 457 let sysBoard = pasteboard.getSystemPasteboard(); 458 if (sysBoard && sysBoard.hasDataSync()) { 459 this.pasteEnable = true; 460 } 461 let uiContext = this.getUIContext(); 462 if (uiContext) { 463 this.isFollowingSystemFontScale = uiContext.isFollowingSystemFontScale(); 464 this.appMaxFontScale = uiContext.getMaxFontScale(); 465 } 466 this.fontScale = this.getFontScale(); 467 } 468 469 hasSystemMenu() { 470 let showMenuOption = this.showCustomerIndex === -1 && 471 (this.controller || (this.expandedMenuOptions && this.expandedMenuOptions.length > 0)); 472 let showBuilder = this.showCustomerIndex > -1 && this.builder; 473 return Boolean(showMenuOption || showBuilder); 474 } 475 476 initialRender() { 477 this.observeComponentCreation2((elmtId, isInitialRender) => { 478 Column.create(); 479 Column.useShadowBatching(true); 480 Column.constraintSize({ 481 maxHeight: '100%', 482 minWidth: this.theme.defaultMenuWidth 483 }); 484 }, Column); 485 this.observeComponentCreation2((elmtId, isInitialRender) => { 486 If.create(); 487 if (this.editorMenuOptions && this.editorMenuOptions.length > 0) { 488 this.ifElseBranchUpdateFunction(0, () => { 489 this.IconPanel.bind(this)(); 490 }); 491 } else { 492 this.ifElseBranchUpdateFunction(1, () => { 493 }); 494 } 495 }, If); 496 If.pop(); 497 this.observeComponentCreation2((elmtId, isInitialRender) => { 498 Scroll.create(); 499 Scroll.backgroundColor(this.theme.backGroundColor); 500 Scroll.shadow(this.theme.iconPanelShadowStyle); 501 Scroll.borderRadius(this.theme.containerBorderRadius); 502 Scroll.outline(this.hasSystemMenu() ? { 503 width: this.theme.outlineWidth, color: this.theme.outlineColor, 504 radius: this.theme.containerBorderRadius 505 } : undefined); 506 Scroll.constraintSize({ 507 maxHeight: `calc(100% - ${this.horizontalMenuHeight > 0 ? 508 this.horizontalMenuHeight + this.theme.menuSpacing : 0}vp)`, 509 minWidth: this.theme.defaultMenuWidth 510 }); 511 }, Scroll); 512 this.SystemMenu.bind(this)(); 513 Scroll.pop(); 514 Column.pop(); 515 } 516 517 pushDataToPasteboard(richEditorSelection) { 518 let sysBoard = pasteboard.getSystemPasteboard(); 519 let pasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, ''); 520 if (richEditorSelection.spans && richEditorSelection.spans.length > 0) { 521 let count = richEditorSelection.spans.length; 522 for (let i = count - 1; i >= 0; i--) { 523 let item = richEditorSelection.spans[i]; 524 if (item?.textStyle) { 525 let span = item; 526 let style = span.textStyle; 527 let data = pasteboard.createRecord(pasteboard.MIMETYPE_TEXT_PLAIN, 528 span.value.substring(span.offsetInSpan[0], span.offsetInSpan[1])); 529 let prop = pasteData.getProperty(); 530 let temp = { 531 'color': style.fontColor, 532 'size': style.fontSize, 533 'style': style.fontStyle, 534 'weight': this.fontWeightTable[style.fontWeight], 535 'fontFamily': style.fontFamily, 536 'decorationType': style.decoration.type, 537 'decorationColor': style.decoration.color 538 }; 539 prop.additions[i] = temp; 540 pasteData.addRecord(data); 541 pasteData.setProperty(prop); 542 } 543 } 544 } 545 sysBoard.clearData(); 546 sysBoard.setData(pasteData).then(() => { 547 hilog.info(0x3900, 'Ace', 'SelectionMenu copy option, Succeeded in setting PasteData.'); 548 }).catch((err) => { 549 hilog.info(0x3900, 'Ace', 'SelectionMenu copy option, Failed to set PasteData. Cause:' + err.message); 550 }); 551 } 552 553 popDataFromPasteboard(richEditorSelection) { 554 let start = richEditorSelection.selection[0]; 555 let end = richEditorSelection.selection[1]; 556 if (start === end && this.controller) { 557 start = this.controller.getCaretOffset(); 558 end = this.controller.getCaretOffset(); 559 } 560 let moveOffset = 0; 561 let sysBoard = pasteboard.getSystemPasteboard(); 562 sysBoard.getData((err, data) => { 563 if (err) { 564 return; 565 } 566 let count = data.getRecordCount(); 567 for (let i = 0; i < count; i++) { 568 const element = data.getRecord(i); 569 let tex = { 570 fontSize: 16, 571 fontColor: Color.Black, 572 fontWeight: FontWeight.Normal, 573 fontFamily: 'HarmonyOS Sans', 574 fontStyle: FontStyle.Normal, 575 decoration: { type: TextDecorationType.None, color: '#FF000000' } 576 }; 577 if (data.getProperty() && data.getProperty().additions[i]) { 578 const tmp = data.getProperty().additions[i]; 579 if (tmp.color) { 580 tex.fontColor = tmp.color; 581 } 582 if (tmp.size) { 583 tex.fontSize = tmp.size; 584 } 585 if (tmp.style) { 586 tex.fontStyle = tmp.style; 587 } 588 if (tmp.weight) { 589 tex.fontWeight = tmp.weight; 590 } 591 if (tmp.fontFamily) { 592 tex.fontFamily = tmp.fontFamily; 593 } 594 if (tmp.decorationType && tex.decoration) { 595 tex.decoration.type = tmp.decorationType; 596 } 597 if (tmp.decorationColor && tex.decoration) { 598 tex.decoration.color = tmp.decorationColor; 599 } 600 if (tex.decoration) { 601 tex.decoration = { type: tex.decoration.type, color: tex.decoration.color }; 602 } 603 } 604 if (element && element.plainText && element.mimeType === pasteboard.MIMETYPE_TEXT_PLAIN && 605 this.controller) { 606 this.controller.addTextSpan(element.plainText, { 607 style: tex, 608 offset: start + moveOffset 609 }); 610 moveOffset += element.plainText.length; 611 } 612 } 613 if (this.controller) { 614 this.controller.setCaretOffset(start + moveOffset); 615 } 616 if (start !== end && this.controller) { 617 this.controller.deleteSpans({ start: start + moveOffset, end: end + moveOffset }); 618 } 619 }); 620 } 621 622 measureButtonWidth() { 623 let numOfBtnPerRow = 5; 624 let width = this.fontScale > MAX_FONT_SCALE ? this.customMenuWidth : this.theme.defaultMenuWidth; 625 if (this.editorMenuOptions && this.editorMenuOptions.length <= numOfBtnPerRow) { 626 return (width - this.theme.expandedOptionPadding * 2) / this.editorMenuOptions.length; 627 } 628 return (width - this.theme.expandedOptionPadding * 2) / numOfBtnPerRow; 629 } 630 631 measureFlexPadding() { 632 return Math.floor((this.theme.expandedOptionPadding - px2vp(2.0)) * 10) / 10; 633 } 634 635 getFontScale() { 636 try { 637 let uiContext = this.getUIContext(); 638 let systemFontScale = uiContext.getHostContext()?.config?.fontSizeScale ?? 1; 639 if (!this.isFollowingSystemFontScale) { 640 return 1; 641 } 642 return Math.min(systemFontScale, this.appMaxFontScale); 643 } catch (exception) { 644 let code = exception.code; 645 let message = exception.message; 646 hilog.error(0x3900, 'Ace', `Faild to init fontsizescale info,cause, code: ${code}, message: ${message}`); 647 return 1; 648 } 649 } 650 651 onMeasureSize(selfLayoutInfo, children, constraint) { 652 this.fontScale = this.getFontScale(); 653 let sizeResult = { height: 0, width: 0 }; 654 children.forEach((child) => { 655 let childMeasureResult = child.measure(constraint); 656 sizeResult.width = childMeasureResult.width; 657 sizeResult.height = childMeasureResult.height; 658 }); 659 return sizeResult; 660 } 661 662 updateMenuItemVisibility() { 663 if (!this.controller) { 664 return; 665 } 666 let richEditorSelection = this.controller.getSelection(); 667 let start = richEditorSelection.selection[0]; 668 let end = richEditorSelection.selection[1]; 669 if (start !== end) { 670 this.cutAndCopyEnable = true; 671 } 672 if (start === 0 && this.controller.getSpans({ start: end + 1, end: end + 1 }).length === 0) { 673 this.visibilityValue = Visibility.None; 674 } 675 else { 676 this.visibilityValue = Visibility.Visible; 677 } 678 } 679 680 IconPanel(parent = null) { 681 this.observeComponentCreation2((elmtId, isInitialRender) => { 682 Flex.create({ wrap: FlexWrap.Wrap }); 683 Flex.onAreaChange((oldValue, newValue) => { 684 let newValueHeight = newValue.height; 685 let newValueWidth = newValue.width; 686 this.horizontalMenuHeight = newValueHeight; 687 this.horizontalMenuWidth = newValueWidth; 688 }); 689 Flex.clip(true); 690 Flex.width(this.fontScale > MAX_FONT_SCALE ? this.customMenuWidth : this.theme.defaultMenuWidth); 691 Flex.padding({ 692 top: this.measureFlexPadding(), 693 bottom: this.measureFlexPadding(), 694 left: this.measureFlexPadding() - 0.1, 695 right: this.measureFlexPadding() - 0.1 696 }); 697 Flex.borderRadius(this.theme.containerBorderRadius); 698 Flex.margin({ bottom: this.theme.menuSpacing }); 699 Flex.backgroundColor(this.theme.backGroundColor); 700 Flex.shadow(this.theme.iconPanelShadowStyle); 701 Flex.border({ 702 width: this.theme.borderWidth, color: this.theme.borderColor, 703 radius: this.theme.containerBorderRadius 704 }); 705 Flex.outline({ 706 width: this.theme.outlineWidth, color: this.theme.outlineColor, 707 radius: this.theme.containerBorderRadius 708 }); 709 }, Flex); 710 this.observeComponentCreation2((elmtId, isInitialRender) => { 711 If.create(); 712 if (this.editorMenuOptions) { 713 this.ifElseBranchUpdateFunction(0, () => { 714 this.observeComponentCreation2((elmtId, isInitialRender) => { 715 ForEach.create(); 716 const forEachItemGenFunction = (_item, index) => { 717 const item = _item; 718 this.observeComponentCreation2((elmtId, isInitialRender) => { 719 Button.createWithChild(); 720 Button.enabled(!(!item.action && !item.builder)); 721 Button.type(ButtonType.Normal); 722 Button.backgroundColor(this.theme.backGroundColor); 723 Button.onClick(() => { 724 if (item.builder) { 725 this.builder = item.builder; 726 this.showCustomerIndex = index; 727 this.showExpandedMenuOptions = false; 728 this.customerChange = !this.customerChange; 729 } else { 730 this.showCustomerIndex = WITHOUT_BUILDER; 731 if (!this.controller) { 732 this.showExpandedMenuOptions = true; 733 } 734 } 735 if (item.action) { 736 item.action(); 737 } 738 }); 739 Button.borderRadius(this.theme.iconBorderRadius); 740 Button.width(this.measureButtonWidth()); 741 Button.height(this.theme.buttonSize); 742 }, Button); 743 this.observeComponentCreation2((elmtId, isInitialRender) => { 744 If.create(); 745 if (item.symbolStyle !== undefined) { 746 this.ifElseBranchUpdateFunction(0, () => { 747 this.observeComponentCreation2((elmtId, isInitialRender) => { 748 SymbolGlyph.create(); 749 SymbolGlyph.fontColor(this.theme.defaultSymbolTheme.fontColor); 750 SymbolGlyph.attributeModifier.bind(this)(item.symbolStyle); 751 SymbolGlyph.focusable(true); 752 SymbolGlyph.draggable(false); 753 SymbolGlyph.effectStrategy(SymbolEffectStrategy.NONE); 754 SymbolGlyph.symbolEffect(new SymbolEffect(), false); 755 SymbolGlyph.fontSize(this.theme.defaultSymbolTheme.fontSize); 756 }, SymbolGlyph); 757 }); 758 } else { 759 this.ifElseBranchUpdateFunction(1, () => { 760 this.observeComponentCreation2((elmtId, isInitialRender) => { 761 If.create(); 762 if (Util.isSymbolResource(item.icon)) { 763 this.ifElseBranchUpdateFunction(0, () => { 764 this.observeComponentCreation2((elmtId, isInitialRender) => { 765 SymbolGlyph.create(item.icon); 766 SymbolGlyph.fontColor(this.theme.defaultSymbolTheme.fontColor); 767 SymbolGlyph.focusable(true); 768 SymbolGlyph.draggable(false); 769 SymbolGlyph.fontSize(this.theme.defaultSymbolTheme.fontSize); 770 }, SymbolGlyph); 771 }); 772 } else { 773 this.ifElseBranchUpdateFunction(1, () => { 774 this.observeComponentCreation2((elmtId, isInitialRender) => { 775 Image.create(item.icon); 776 Image.width(this.theme.imageSize); 777 Image.height(this.theme.imageSize); 778 Image.fillColor(this.theme.imageFillColor); 779 Image.focusable(true); 780 Image.draggable(false); 781 }, Image); 782 }); 783 } 784 }, If); 785 If.pop(); 786 }); 787 } 788 }, If); 789 If.pop(); 790 Button.pop(); 791 }; 792 this.forEachUpdateFunction(elmtId, this.editorMenuOptions, forEachItemGenFunction, undefined, 793 true, false); 794 }, ForEach); 795 ForEach.pop(); 796 }); 797 } else { 798 this.ifElseBranchUpdateFunction(1, () => { 799 }); 800 } 801 }, If); 802 If.pop(); 803 Flex.pop(); 804 } 805 806 SystemMenu(parent = null) { 807 this.observeComponentCreation2((elmtId, isInitialRender) => { 808 Column.create(); 809 Column.width(this.fontScale > MAX_FONT_SCALE ? 'auto' : this.theme.defaultMenuWidth); 810 Column.shadow(this.theme.iconPanelShadowStyle); 811 Column.border({ 812 width: this.theme.borderWidth, color: this.theme.borderColor, 813 radius: this.theme.containerBorderRadius 814 }); 815 Column.constraintSize({ 816 minWidth: this.theme.defaultMenuWidth 817 }); 818 }, Column); 819 this.observeComponentCreation2((elmtId, isInitialRender) => { 820 If.create(); 821 if (this.showCustomerIndex === -1 && 822 (this.controller || (this.expandedMenuOptions && this.expandedMenuOptions.length > 0))) { 823 this.ifElseBranchUpdateFunction(0, () => { 824 this.observeComponentCreation2((elmtId, isInitialRender) => { 825 Menu.create(); 826 Menu.radius(this.theme.containerBorderRadius); 827 Menu.clip(true); 828 Menu.width(this.fontScale > MAX_FONT_SCALE ? 'auto' : this.theme.defaultMenuWidth); 829 Menu.constraintSize({ 830 minWidth: this.theme.defaultMenuWidth 831 }); 832 Menu.onVisibleAreaChange([0.0, 1.0], () => { 833 this.updateMenuItemVisibility(); 834 }); 835 Menu.onAreaChange((oldValue, newValue) => { 836 let newValueWidth = newValue.width; 837 this.customMenuWidth = 838 this.fontScale > MAX_FONT_SCALE && newValueWidth > this.theme.defaultMenuWidth ? newValueWidth : 839 this.theme.defaultMenuWidth; 840 this.updateMenuItemVisibility(); 841 }); 842 }, Menu); 843 this.observeComponentCreation2((elmtId, isInitialRender) => { 844 If.create(); 845 if (this.controller) { 846 this.ifElseBranchUpdateFunction(0, () => { 847 this.observeComponentCreation2((elmtId, isInitialRender) => { 848 MenuItemGroup.create(); 849 }, MenuItemGroup); 850 this.observeComponentCreation2((elmtId, isInitialRender) => { 851 MenuItem.create({ 852 startIcon: this.theme.cutIcon, 853 symbolStartIcon: this.theme.defaultSymbolTheme.symbolCutIcon, 854 content: '剪切', 855 labelInfo: 'Ctrl+X' 856 }); 857 MenuItem.enabled(this.cutAndCopyEnable); 858 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 859 this.theme.buttonSize); 860 MenuItem.borderRadius(this.theme.iconBorderRadius); 861 MenuItem.onClick(() => { 862 if (!this.controller) { 863 return; 864 } 865 let richEditorSelection = this.controller.getSelection(); 866 if (this.onCut) { 867 this.onCut({ content: richEditorSelection }); 868 } else { 869 this.pushDataToPasteboard(richEditorSelection); 870 this.controller.deleteSpans({ 871 start: richEditorSelection.selection[0], 872 end: richEditorSelection.selection[1] 873 }); 874 } 875 }); 876 }, MenuItem); 877 MenuItem.pop(); 878 this.observeComponentCreation2((elmtId, isInitialRender) => { 879 MenuItem.create({ 880 startIcon: this.theme.copyIcon, 881 symbolStartIcon: this.theme.defaultSymbolTheme.symbolCopyIcon, 882 content: '复制', 883 labelInfo: 'Ctrl+C' 884 }); 885 MenuItem.enabled(this.cutAndCopyEnable); 886 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 887 this.theme.buttonSize); 888 MenuItem.borderRadius(this.theme.iconBorderRadius); 889 MenuItem.margin({ top: this.theme.menuItemPadding }); 890 MenuItem.onClick(() => { 891 if (!this.controller) { 892 return; 893 } 894 let richEditorSelection = this.controller.getSelection(); 895 if (this.onCopy) { 896 this.onCopy({ content: richEditorSelection }); 897 } else { 898 this.pushDataToPasteboard(richEditorSelection); 899 this.controller.closeSelectionMenu(); 900 } 901 }); 902 }, MenuItem); 903 MenuItem.pop(); 904 this.observeComponentCreation2((elmtId, isInitialRender) => { 905 MenuItem.create({ 906 startIcon: this.theme.pasteIcon, 907 symbolStartIcon: this.theme.defaultSymbolTheme.symbolPasteIcon, 908 content: '粘贴', 909 labelInfo: 'Ctrl+V' 910 }); 911 MenuItem.enabled(this.pasteEnable); 912 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 913 this.theme.buttonSize); 914 MenuItem.borderRadius(this.theme.iconBorderRadius); 915 MenuItem.margin({ top: this.theme.menuItemPadding }); 916 MenuItem.onClick(() => { 917 if (!this.controller) { 918 return; 919 } 920 let richEditorSelection = this.controller.getSelection(); 921 if (this.onPaste) { 922 this.onPaste({ content: richEditorSelection }); 923 } else { 924 this.popDataFromPasteboard(richEditorSelection); 925 this.controller.closeSelectionMenu(); 926 } 927 }); 928 }, MenuItem); 929 MenuItem.pop(); 930 this.observeComponentCreation2((elmtId, isInitialRender) => { 931 MenuItem.create({ 932 startIcon: this.theme.selectAllIcon, 933 symbolStartIcon: this.theme.defaultSymbolTheme.symbolSelectAllIcon, 934 content: '全选', 935 labelInfo: 'Ctrl+A' 936 }); 937 MenuItem.visibility(this.visibilityValue); 938 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 939 this.theme.buttonSize); 940 MenuItem.borderRadius(this.theme.iconBorderRadius); 941 MenuItem.margin({ top: this.theme.menuItemPadding }); 942 MenuItem.onClick(() => { 943 if (!this.controller) { 944 return; 945 } 946 if (this.onSelectAll) { 947 let richEditorSelection = this.controller.getSelection(); 948 this.onSelectAll({ content: richEditorSelection }); 949 } else { 950 this.controller.setSelection(-1, -1); 951 this.visibilityValue = Visibility.None; 952 } 953 this.controller.closeSelectionMenu(); 954 }); 955 }, MenuItem); 956 MenuItem.pop(); 957 MenuItemGroup.pop(); 958 }); 959 } else { 960 this.ifElseBranchUpdateFunction(1, () => { 961 }); 962 } 963 }, If); 964 If.pop(); 965 this.observeComponentCreation2((elmtId, isInitialRender) => { 966 If.create(); 967 if (this.controller && !this.showExpandedMenuOptions && 968 this.expandedMenuOptions && this.expandedMenuOptions.length > 0) { 969 this.ifElseBranchUpdateFunction(0, () => { 970 this.observeComponentCreation2((elmtId, isInitialRender) => { 971 MenuItem.create({ 972 content: '更多', 973 endIcon: this.theme.arrowDownIcon, 974 symbolEndIcon: this.theme.defaultSymbolTheme.symbolArrowDownIcon 975 }); 976 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 977 this.theme.buttonSize); 978 MenuItem.borderRadius(this.theme.iconBorderRadius); 979 MenuItem.margin({ top: this.theme.menuItemPadding }); 980 MenuItem.onClick(() => { 981 this.showExpandedMenuOptions = true; 982 }); 983 }, MenuItem); 984 MenuItem.pop(); 985 }); 986 } else if (this.showExpandedMenuOptions && this.expandedMenuOptions && 987 this.expandedMenuOptions.length > 0) { 988 this.ifElseBranchUpdateFunction(1, () => { 989 this.observeComponentCreation2((elmtId, isInitialRender) => { 990 ForEach.create(); 991 const forEachItemGenFunction = (_item, index) => { 992 const expandedMenuOptionItem = _item; 993 this.observeComponentCreation2((elmtId, isInitialRender) => { 994 MenuItem.create({ 995 startIcon: expandedMenuOptionItem.startIcon, 996 symbolStartIcon: expandedMenuOptionItem.symbolStartIcon, 997 content: expandedMenuOptionItem.content, 998 endIcon: expandedMenuOptionItem.endIcon, 999 symbolEndIcon: expandedMenuOptionItem.symbolEndIcon, 1000 labelInfo: expandedMenuOptionItem.labelInfo, 1001 builder: expandedMenuOptionItem.builder 1002 }); 1003 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 1004 this.theme.buttonSize); 1005 MenuItem.borderRadius(this.theme.iconBorderRadius); 1006 MenuItem.margin({ top: this.theme.menuItemPadding }); 1007 MenuItem.onClick(() => { 1008 if (expandedMenuOptionItem.action) { 1009 expandedMenuOptionItem.action(); 1010 } 1011 }); 1012 }, MenuItem); 1013 MenuItem.pop(); 1014 }; 1015 this.forEachUpdateFunction(elmtId, this.expandedMenuOptions, forEachItemGenFunction, 1016 undefined, true, false); 1017 }, ForEach); 1018 ForEach.pop(); 1019 }); 1020 } else { 1021 this.ifElseBranchUpdateFunction(2, () => { 1022 }); 1023 } 1024 }, If); 1025 If.pop(); 1026 Menu.pop(); 1027 }); 1028 } else if (this.showCustomerIndex > -1 && this.builder) { 1029 this.ifElseBranchUpdateFunction(1, () => { 1030 this.observeComponentCreation2((elmtId, isInitialRender) => { 1031 Column.create(); 1032 Column.width(this.horizontalMenuWidth); 1033 }, Column); 1034 this.observeComponentCreation2((elmtId, isInitialRender) => { 1035 If.create(); 1036 if (this.customerChange) { 1037 this.ifElseBranchUpdateFunction(0, () => { 1038 this.builder.bind(this)(); 1039 }); 1040 } else { 1041 this.ifElseBranchUpdateFunction(1, () => { 1042 this.builder.bind(this)(); 1043 }); 1044 } 1045 }, If); 1046 If.pop(); 1047 Column.pop(); 1048 }); 1049 } else { 1050 this.ifElseBranchUpdateFunction(2, () => { 1051 }); 1052 } 1053 }, If); 1054 If.pop(); 1055 Column.pop(); 1056 } 1057 1058 rerender() { 1059 this.updateDirtyElements(); 1060 } 1061} 1062 1063export function SelectionMenu(options, parent = null) { 1064 const __options__ = options; 1065 { 1066 (parent ? parent : this).observeComponentCreation2((elmtId, isInitialRender, options = __options__) => { 1067 if (isInitialRender) { 1068 let componentCall = new SelectionMenuComponent(parent ? parent : this, { 1069 editorMenuOptions: options.editorMenuOptions, 1070 expandedMenuOptions: options.expandedMenuOptions, 1071 controller: options.controller, 1072 onPaste: options.onPaste, 1073 onCopy: options.onCopy, 1074 onCut: options.onCut, 1075 onSelectAll: options.onSelectAll 1076 }, undefined, elmtId, () => { 1077 }, { page: 'SelectionMenu/src/main/ets/components/MainPage.ets', line: 633, col: 3 }); 1078 ViewPU.create(componentCall); 1079 let paramsLambda = () => { 1080 return { 1081 editorMenuOptions: options.editorMenuOptions, 1082 expandedMenuOptions: options.expandedMenuOptions, 1083 controller: options.controller, 1084 onPaste: options.onPaste, 1085 onCopy: options.onCopy, 1086 onCut: options.onCut, 1087 onSelectAll: options.onSelectAll 1088 }; 1089 }; 1090 componentCall.paramsGenerator_ = paramsLambda; 1091 } else { 1092 (parent ? parent : this).updateStateVarsOfChildByElmtId(elmtId, {}); 1093 } 1094 }, { name: 'SelectionMenuComponent' }); 1095 } 1096} 1097 1098class Util { 1099 static isSymbolResource(resourceStr) { 1100 if (!Util.isResourceType(resourceStr)) { 1101 return false; 1102 } 1103 let resource = resourceStr; 1104 return resource.type === Util.RESOURCE_TYPE_SYMBOL; 1105 } 1106 1107 static isResourceType(resource) { 1108 if (!resource) { 1109 return false; 1110 } 1111 if (typeof resource === 'string' || typeof resource === 'undefined') { 1112 return false; 1113 } 1114 return true; 1115 } 1116} 1117 1118Util.RESOURCE_TYPE_SYMBOL = 40000; 1119 1120export default { SelectionMenu };