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 = 2.0; 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 IconPanel(parent = null) { 663 this.observeComponentCreation2((elmtId, isInitialRender) => { 664 Flex.create({ wrap: FlexWrap.Wrap }); 665 Flex.onAreaChange((oldValue, newValue) => { 666 let newValueHeight = newValue.height; 667 let newValueWidth = newValue.width; 668 this.horizontalMenuHeight = newValueHeight; 669 this.horizontalMenuWidth = newValueWidth; 670 }); 671 Flex.clip(true); 672 Flex.width(this.fontScale > MAX_FONT_SCALE ? this.customMenuWidth : this.theme.defaultMenuWidth); 673 Flex.padding({ 674 top: this.measureFlexPadding(), 675 bottom: this.measureFlexPadding(), 676 left: this.measureFlexPadding() - 0.1, 677 right: this.measureFlexPadding() - 0.1 678 }); 679 Flex.borderRadius(this.theme.containerBorderRadius); 680 Flex.margin({ bottom: this.theme.menuSpacing }); 681 Flex.backgroundColor(this.theme.backGroundColor); 682 Flex.shadow(this.theme.iconPanelShadowStyle); 683 Flex.border({ 684 width: this.theme.borderWidth, color: this.theme.borderColor, 685 radius: this.theme.containerBorderRadius 686 }); 687 Flex.outline({ 688 width: this.theme.outlineWidth, color: this.theme.outlineColor, 689 radius: this.theme.containerBorderRadius 690 }); 691 }, Flex); 692 this.observeComponentCreation2((elmtId, isInitialRender) => { 693 If.create(); 694 if (this.editorMenuOptions) { 695 this.ifElseBranchUpdateFunction(0, () => { 696 this.observeComponentCreation2((elmtId, isInitialRender) => { 697 ForEach.create(); 698 const forEachItemGenFunction = (_item, index) => { 699 const item = _item; 700 this.observeComponentCreation2((elmtId, isInitialRender) => { 701 Button.createWithChild(); 702 Button.enabled(!(!item.action && !item.builder)); 703 Button.type(ButtonType.Normal); 704 Button.backgroundColor(this.theme.backGroundColor); 705 Button.onClick(() => { 706 if (item.builder) { 707 this.builder = item.builder; 708 this.showCustomerIndex = index; 709 this.showExpandedMenuOptions = false; 710 this.customerChange = !this.customerChange; 711 } else { 712 this.showCustomerIndex = WITHOUT_BUILDER; 713 if (!this.controller) { 714 this.showExpandedMenuOptions = true; 715 } 716 } 717 if (item.action) { 718 item.action(); 719 } 720 }); 721 Button.borderRadius(this.theme.iconBorderRadius); 722 Button.width(this.measureButtonWidth()); 723 Button.height(this.theme.buttonSize); 724 }, Button); 725 this.observeComponentCreation2((elmtId, isInitialRender) => { 726 If.create(); 727 if (item.symbolStyle !== undefined) { 728 this.ifElseBranchUpdateFunction(0, () => { 729 this.observeComponentCreation2((elmtId, isInitialRender) => { 730 SymbolGlyph.create(); 731 SymbolGlyph.fontColor(this.theme.defaultSymbolTheme.fontColor); 732 SymbolGlyph.attributeModifier.bind(this)(item.symbolStyle); 733 SymbolGlyph.focusable(true); 734 SymbolGlyph.draggable(false); 735 SymbolGlyph.effectStrategy(SymbolEffectStrategy.NONE); 736 SymbolGlyph.symbolEffect(new SymbolEffect(), false); 737 SymbolGlyph.fontSize(this.theme.defaultSymbolTheme.fontSize); 738 }, SymbolGlyph); 739 }); 740 } else { 741 this.ifElseBranchUpdateFunction(1, () => { 742 this.observeComponentCreation2((elmtId, isInitialRender) => { 743 If.create(); 744 if (Util.isSymbolResource(item.icon)) { 745 this.ifElseBranchUpdateFunction(0, () => { 746 this.observeComponentCreation2((elmtId, isInitialRender) => { 747 SymbolGlyph.create(item.icon); 748 SymbolGlyph.fontColor(this.theme.defaultSymbolTheme.fontColor); 749 SymbolGlyph.focusable(true); 750 SymbolGlyph.draggable(false); 751 SymbolGlyph.fontSize(this.theme.defaultSymbolTheme.fontSize); 752 }, SymbolGlyph); 753 }); 754 } else { 755 this.ifElseBranchUpdateFunction(1, () => { 756 this.observeComponentCreation2((elmtId, isInitialRender) => { 757 Image.create(item.icon); 758 Image.width(this.theme.imageSize); 759 Image.height(this.theme.imageSize); 760 Image.fillColor(this.theme.imageFillColor); 761 Image.focusable(true); 762 Image.draggable(false); 763 }, Image); 764 }); 765 } 766 }, If); 767 If.pop(); 768 }); 769 } 770 }, If); 771 If.pop(); 772 Button.pop(); 773 }; 774 this.forEachUpdateFunction(elmtId, this.editorMenuOptions, forEachItemGenFunction, undefined, 775 true, false); 776 }, ForEach); 777 ForEach.pop(); 778 }); 779 } else { 780 this.ifElseBranchUpdateFunction(1, () => { 781 }); 782 } 783 }, If); 784 If.pop(); 785 Flex.pop(); 786 } 787 788 SystemMenu(parent = null) { 789 this.observeComponentCreation2((elmtId, isInitialRender) => { 790 Column.create(); 791 Column.width(this.fontScale > MAX_FONT_SCALE ? 'auto' : this.theme.defaultMenuWidth); 792 Column.shadow(this.theme.iconPanelShadowStyle); 793 Column.border({ 794 width: this.theme.borderWidth, color: this.theme.borderColor, 795 radius: this.theme.containerBorderRadius 796 }); 797 Column.constraintSize({ 798 minWidth: this.theme.defaultMenuWidth 799 }); 800 }, Column); 801 this.observeComponentCreation2((elmtId, isInitialRender) => { 802 If.create(); 803 if (this.showCustomerIndex === -1 && 804 (this.controller || (this.expandedMenuOptions && this.expandedMenuOptions.length > 0))) { 805 this.ifElseBranchUpdateFunction(0, () => { 806 this.observeComponentCreation2((elmtId, isInitialRender) => { 807 Menu.create(); 808 Menu.radius(this.theme.containerBorderRadius); 809 Menu.clip(true); 810 Menu.width(this.fontScale > MAX_FONT_SCALE ? 'auto' : this.theme.defaultMenuWidth); 811 Menu.constraintSize({ 812 minWidth: this.theme.defaultMenuWidth 813 }); 814 Menu.onAreaChange((oldValue, newValue) => { 815 let newValueWidth = newValue.width; 816 this.customMenuWidth = 817 this.fontScale > MAX_FONT_SCALE && newValueWidth > this.theme.defaultMenuWidth ? newValueWidth : 818 this.theme.defaultMenuWidth; 819 if (!this.controller) { 820 return; 821 } 822 let richEditorSelection = this.controller.getSelection(); 823 let start = richEditorSelection.selection[0]; 824 let end = richEditorSelection.selection[1]; 825 if (start !== end) { 826 this.cutAndCopyEnable = true; 827 } 828 if (start === 0 && 829 this.controller.getSpans({ start: end + 1, end: end + 1 }).length === 0) { 830 this.visibilityValue = Visibility.None; 831 } else { 832 this.visibilityValue = Visibility.Visible; 833 } 834 }); 835 }, Menu); 836 this.observeComponentCreation2((elmtId, isInitialRender) => { 837 If.create(); 838 if (this.controller) { 839 this.ifElseBranchUpdateFunction(0, () => { 840 this.observeComponentCreation2((elmtId, isInitialRender) => { 841 MenuItemGroup.create(); 842 }, MenuItemGroup); 843 this.observeComponentCreation2((elmtId, isInitialRender) => { 844 MenuItem.create({ 845 startIcon: this.theme.cutIcon, 846 symbolStartIcon: this.theme.defaultSymbolTheme.symbolCutIcon, 847 content: '剪切', 848 labelInfo: 'Ctrl+X' 849 }); 850 MenuItem.enabled(this.cutAndCopyEnable); 851 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 852 this.theme.buttonSize); 853 MenuItem.borderRadius(this.theme.iconBorderRadius); 854 MenuItem.onClick(() => { 855 if (!this.controller) { 856 return; 857 } 858 let richEditorSelection = this.controller.getSelection(); 859 if (this.onCut) { 860 this.onCut({ content: richEditorSelection }); 861 } else { 862 this.pushDataToPasteboard(richEditorSelection); 863 this.controller.deleteSpans({ 864 start: richEditorSelection.selection[0], 865 end: richEditorSelection.selection[1] 866 }); 867 } 868 }); 869 }, MenuItem); 870 MenuItem.pop(); 871 this.observeComponentCreation2((elmtId, isInitialRender) => { 872 MenuItem.create({ 873 startIcon: this.theme.copyIcon, 874 symbolStartIcon: this.theme.defaultSymbolTheme.symbolCopyIcon, 875 content: '复制', 876 labelInfo: 'Ctrl+C' 877 }); 878 MenuItem.enabled(this.cutAndCopyEnable); 879 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 880 this.theme.buttonSize); 881 MenuItem.borderRadius(this.theme.iconBorderRadius); 882 MenuItem.margin({ top: this.theme.menuItemPadding }); 883 MenuItem.onClick(() => { 884 if (!this.controller) { 885 return; 886 } 887 let richEditorSelection = this.controller.getSelection(); 888 if (this.onCopy) { 889 this.onCopy({ content: richEditorSelection }); 890 } else { 891 this.pushDataToPasteboard(richEditorSelection); 892 this.controller.closeSelectionMenu(); 893 } 894 }); 895 }, MenuItem); 896 MenuItem.pop(); 897 this.observeComponentCreation2((elmtId, isInitialRender) => { 898 MenuItem.create({ 899 startIcon: this.theme.pasteIcon, 900 symbolStartIcon: this.theme.defaultSymbolTheme.symbolPasteIcon, 901 content: '粘贴', 902 labelInfo: 'Ctrl+V' 903 }); 904 MenuItem.enabled(this.pasteEnable); 905 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 906 this.theme.buttonSize); 907 MenuItem.borderRadius(this.theme.iconBorderRadius); 908 MenuItem.margin({ top: this.theme.menuItemPadding }); 909 MenuItem.onClick(() => { 910 if (!this.controller) { 911 return; 912 } 913 let richEditorSelection = this.controller.getSelection(); 914 if (this.onPaste) { 915 this.onPaste({ content: richEditorSelection }); 916 } else { 917 this.popDataFromPasteboard(richEditorSelection); 918 this.controller.closeSelectionMenu(); 919 } 920 }); 921 }, MenuItem); 922 MenuItem.pop(); 923 this.observeComponentCreation2((elmtId, isInitialRender) => { 924 MenuItem.create({ 925 startIcon: this.theme.selectAllIcon, 926 symbolStartIcon: this.theme.defaultSymbolTheme.symbolSelectAllIcon, 927 content: '全选', 928 labelInfo: 'Ctrl+A' 929 }); 930 MenuItem.visibility(this.visibilityValue); 931 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 932 this.theme.buttonSize); 933 MenuItem.borderRadius(this.theme.iconBorderRadius); 934 MenuItem.margin({ top: this.theme.menuItemPadding }); 935 MenuItem.onClick(() => { 936 if (!this.controller) { 937 return; 938 } 939 if (this.onSelectAll) { 940 let richEditorSelection = this.controller.getSelection(); 941 this.onSelectAll({ content: richEditorSelection }); 942 } else { 943 this.controller.setSelection(-1, -1); 944 this.visibilityValue = Visibility.None; 945 } 946 this.controller.closeSelectionMenu(); 947 }); 948 }, MenuItem); 949 MenuItem.pop(); 950 MenuItemGroup.pop(); 951 }); 952 } else { 953 this.ifElseBranchUpdateFunction(1, () => { 954 }); 955 } 956 }, If); 957 If.pop(); 958 this.observeComponentCreation2((elmtId, isInitialRender) => { 959 If.create(); 960 if (this.controller && !this.showExpandedMenuOptions && 961 this.expandedMenuOptions && this.expandedMenuOptions.length > 0) { 962 this.ifElseBranchUpdateFunction(0, () => { 963 this.observeComponentCreation2((elmtId, isInitialRender) => { 964 MenuItem.create({ 965 content: '更多', 966 endIcon: this.theme.arrowDownIcon, 967 symbolEndIcon: this.theme.defaultSymbolTheme.symbolArrowDownIcon 968 }); 969 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 970 this.theme.buttonSize); 971 MenuItem.borderRadius(this.theme.iconBorderRadius); 972 MenuItem.margin({ top: this.theme.menuItemPadding }); 973 MenuItem.onClick(() => { 974 this.showExpandedMenuOptions = true; 975 }); 976 }, MenuItem); 977 MenuItem.pop(); 978 }); 979 } else if (this.showExpandedMenuOptions && this.expandedMenuOptions && 980 this.expandedMenuOptions.length > 0) { 981 this.ifElseBranchUpdateFunction(1, () => { 982 this.observeComponentCreation2((elmtId, isInitialRender) => { 983 ForEach.create(); 984 const forEachItemGenFunction = (_item, index) => { 985 const expandedMenuOptionItem = _item; 986 this.observeComponentCreation2((elmtId, isInitialRender) => { 987 MenuItem.create({ 988 startIcon: expandedMenuOptionItem.startIcon, 989 symbolStartIcon: expandedMenuOptionItem.symbolStartIcon, 990 content: expandedMenuOptionItem.content, 991 endIcon: expandedMenuOptionItem.endIcon, 992 symbolEndIcon: expandedMenuOptionItem.symbolEndIcon, 993 labelInfo: expandedMenuOptionItem.labelInfo, 994 builder: expandedMenuOptionItem.builder 995 }); 996 MenuItem.height(this.fontScale > MAX_FONT_STANDARD ? 'auto' : 997 this.theme.buttonSize); 998 MenuItem.borderRadius(this.theme.iconBorderRadius); 999 MenuItem.margin({ top: this.theme.menuItemPadding }); 1000 MenuItem.onClick(() => { 1001 if (expandedMenuOptionItem.action) { 1002 expandedMenuOptionItem.action(); 1003 } 1004 }); 1005 }, MenuItem); 1006 MenuItem.pop(); 1007 }; 1008 this.forEachUpdateFunction(elmtId, this.expandedMenuOptions, forEachItemGenFunction, 1009 undefined, true, false); 1010 }, ForEach); 1011 ForEach.pop(); 1012 }); 1013 } else { 1014 this.ifElseBranchUpdateFunction(2, () => { 1015 }); 1016 } 1017 }, If); 1018 If.pop(); 1019 Menu.pop(); 1020 }); 1021 } else if (this.showCustomerIndex > -1 && this.builder) { 1022 this.ifElseBranchUpdateFunction(1, () => { 1023 this.observeComponentCreation2((elmtId, isInitialRender) => { 1024 Column.create(); 1025 Column.width(this.horizontalMenuWidth); 1026 }, Column); 1027 this.observeComponentCreation2((elmtId, isInitialRender) => { 1028 If.create(); 1029 if (this.customerChange) { 1030 this.ifElseBranchUpdateFunction(0, () => { 1031 this.builder.bind(this)(); 1032 }); 1033 } else { 1034 this.ifElseBranchUpdateFunction(1, () => { 1035 this.builder.bind(this)(); 1036 }); 1037 } 1038 }, If); 1039 If.pop(); 1040 Column.pop(); 1041 }); 1042 } else { 1043 this.ifElseBranchUpdateFunction(2, () => { 1044 }); 1045 } 1046 }, If); 1047 If.pop(); 1048 Column.pop(); 1049 } 1050 1051 rerender() { 1052 this.updateDirtyElements(); 1053 } 1054} 1055 1056export function SelectionMenu(options, parent = null) { 1057 const __options__ = options; 1058 { 1059 (parent ? parent : this).observeComponentCreation2((elmtId, isInitialRender, options = __options__) => { 1060 if (isInitialRender) { 1061 let componentCall = new SelectionMenuComponent(parent ? parent : this, { 1062 editorMenuOptions: options.editorMenuOptions, 1063 expandedMenuOptions: options.expandedMenuOptions, 1064 controller: options.controller, 1065 onPaste: options.onPaste, 1066 onCopy: options.onCopy, 1067 onCut: options.onCut, 1068 onSelectAll: options.onSelectAll 1069 }, undefined, elmtId, () => { 1070 }, { page: 'SelectionMenu/src/main/ets/components/MainPage.ets', line: 633, col: 3 }); 1071 ViewPU.create(componentCall); 1072 let paramsLambda = () => { 1073 return { 1074 editorMenuOptions: options.editorMenuOptions, 1075 expandedMenuOptions: options.expandedMenuOptions, 1076 controller: options.controller, 1077 onPaste: options.onPaste, 1078 onCopy: options.onCopy, 1079 onCut: options.onCut, 1080 onSelectAll: options.onSelectAll 1081 }; 1082 }; 1083 componentCall.paramsGenerator_ = paramsLambda; 1084 } else { 1085 (parent ? parent : this).updateStateVarsOfChildByElmtId(elmtId, {}); 1086 } 1087 }, { name: 'SelectionMenuComponent' }); 1088 } 1089} 1090 1091class Util { 1092 static isSymbolResource(resourceStr) { 1093 if (!Util.isResourceType(resourceStr)) { 1094 return false; 1095 } 1096 let resource = resourceStr; 1097 return resource.type === Util.RESOURCE_TYPE_SYMBOL; 1098 } 1099 1100 static isResourceType(resource) { 1101 if (!resource) { 1102 return false; 1103 } 1104 if (typeof resource === 'string' || typeof resource === 'undefined') { 1105 return false; 1106 } 1107 return true; 1108 } 1109} 1110 1111Util.RESOURCE_TYPE_SYMBOL = 40000; 1112 1113export default { SelectionMenu };