1# RichEditor 2 3The **\<RichEditor>** is a component that supports interactive text editing and mixture of text and imagery. 4 5> **NOTE** 6> 7> This component is supported since API version 10. Updates will be marked with a superscript to indicate their earliest API version. 8 9 10## Child Components 11 12This component can contain the [\<Span>](ts-basic-components-span.md) and [\<ImageSpan>](ts-basic-components-imagespan.md) child components. 13 14 15## APIs 16 17RichEditor(value: RichEditorOptions) 18 19**Parameters** 20 21| Name| Type| Mandatory| Description| 22| -------- | -------- | -------- | -------- | 23| value | [RichEditorOptions](#richeditoroptions) | Yes| Options for initializing the component.| 24 25 26## Attributes 27 28The [universal attributes](ts-universal-attributes-size.md) are supported. 29 30> **NOTE** 31> 32> The default value of the **clip** attribute is **true**. 33> 34> The **align** attribute supports only the start, center, and end options. 35 36| Name | Type | Description | 37| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | 38| customKeyboard | [CustomBuilder](ts-types.md#custombuilder8) | Custom keyboard.<br>**NOTE**<br>When a custom keyboard is set, activating the text box opens the specified custom component, instead of the system input method.<br>The custom keyboard's height can be set through the **height** attribute of the custom component's root node, and its width is fixed at the default value.<br>The custom keyboard is displayed on top of the current page, without compressing or raising the page.<br>The custom keyboard cannot obtain the focus, but it blocks gesture events.<br>By default, the custom keyboard is closed when the input component loses the focus.| 39| copyOption | [CopyOptions](ts-appendix-enums.md#copyoptions9) | Whether copy and paste is allowed.<br>Default value: **CopyOptions.LocalDevice**<br>**NOTE**<br>If **copyOptions** is set to **CopyOptions.InApp** or **CopyOptions.LocalDevice**, long pressing content in the component will display a shortcut menu, after which you can adjust the content selection scope and perform the desired operation, such as copy and select all.<br>If **copyOptions** is set to **CopyOptions.None**, copy and paste is not allowed. | 40## Events 41 42In addition to the [universal events](ts-universal-events-click.md), the following events are supported. 43 44| Name | Description | 45| ------------------------------------------------------------ | ------------------------------------------------------------ | 46| onReady(callback: () => void) | Triggered when initialization of the component is completed.| 47| onSelect(callback: (value: [RichEditorSelection](#richeditorselection)) => void) | Triggered when selection (by clicking the left mouse button, highlighting the text to select, and releasing the left mouse button) is performed.<br>- **value**: information about all selected spans.| 48| aboutToIMEInput(callback: (value: [RichEditorInsertValue](#richeditorinsertvalue)) => boolean) | Triggered when content is about to be entered in the input method.<br>- **value**: content to be entered in the input method.| 49| onIMEInputComplete(callback: (value: [RichEditorTextSpanResult](#richeditortextspanresult)) => void) | Triggered when text input is completed.<br>- **value**: text span information after text input is completed.| 50| aboutToDelete(callback: (value: [RichEditorDeleteValue](#richeditordeletevalue)) => boolean) | Triggered when content is about to be deleted in the input method.<br>- **value**: information about the text span where the content to be deleted is located.| 51| onDeleteComplete(callback: () => void) | Triggered when deletion in the input method is completed.| 52 53## RichEditorInsertValue 54 55Describes the text to be inserted. 56 57| Name| Type| Mandatory| Description| 58| -------- | -------- | -------- | -------- | 59| insertOffset | number | Yes| Offset of the text to be inserted.| 60| insertValue | string | Yes| Content of the text to be inserted.| 61 62 63## RichEditorDeleteValue 64 65| Name| Type| Mandatory| Description| 66| -------- | -------- | -------- | -------- | 67| offset | number | Yes| Offset of the text to be deleted.| 68| direction | [RichEditorDeleteDirection](#richeditordeletedirection) | Yes| Direction of the delete operation.| 69| length | number | Yes| Length of the content to be deleted.| 70| richEditorDeleteSpans | Array<[RichEditorTextSpanResult](#richeditortextspanresult) \| [RichEditorImageSpanResult](#richeditorimagespanresult)> | Yes| Information about the text or image spans to be deleted.| 71 72 73## RichEditorDeleteDirection 74 75Enumerates the directions of the delete operation. 76 77| Name | Description | 78| -------- | ------------------------------ | 79| BACKWARD | Backward. | 80| FORWARD | Forward. | 81 82 83## RichEditorTextSpanResult 84 85Provides the text span information. 86 87| Name| Type| Mandatory| Description| 88| -------- | -------- | -------- | -------- | 89| spanPosition | [RichEditorSpanPosition](#richeditorspanposition) | Yes| Span position.| 90| value | string | Yes| Text span content.| 91| textStyle | [RichEditorTextStyleResult](#richeditortextstyleresult) | Yes| Text span style.| 92| offsetInSpan | [number, number] | Yes| Start and end positions of the valid content in the text span.| 93 94 95## RichEditorSpanPosition 96 97Provides the span position information. 98 99| Name| Type| Mandatory| Description| 100| -------- | -------- | -------- | -------- | 101| spanIndex | number | Yes| Span index.| 102| spanRange | [number, number] | Yes| Start and end positions of the span content in the **\<RichEditor>** component.| 103 104 105## RichEditorTextStyleResult 106 107Provides the text span style information returned by the backend. 108 109| Name| Type| Mandatory| Description | 110| ------ | -------- | ---- | -------------------------------------- | 111| fontColor | [ResourceColor](ts-types.md#resourcecolor) | Yes| Font color.| 112| fontSize | number | Yes| Font size.| 113| fontStyle | [FontStyle](ts-appendix-enums.md#fontstyle) | Yes| Font style.| 114| fontWeight | number | Yes| Font weight.| 115| fontFamily | string | Yes| Font family.| 116| decoration | {<br>type: [TextDecorationType](ts-appendix-enums.md#textdecorationtype),<br>color?: [ResourceColor](ts-types.md#resourcecolor)<br>} | Yes| Style and color of the text decorative line.| 117 118 119## RichEditorImageSpanResult 120 121Provides the image span style information returned by the backend. 122 123| Name| Type| Mandatory| Description | 124| ------ | -------- | ---- | -------------------------------------- | 125| size | [number, number] | Yes| Width and height of the image.| 126| verticalAlign | [ImageSpanAlignment](ts-basic-components-imagespan.md#imagespanalignment) | Yes | Vertical alignment mode of the image.| 127| objectFit | [ImageFit](ts-appendix-enums.md#imagefit) | Yes| Scale mode of the image.| 128 129 130## RichEditorOptions 131 132Defines the options for initializing the **\<RichEditor>** component. 133 134| Name| Type| Mandatory| Description| 135| -------- | -------- | -------- | -------- | 136| controller | [RichEditorController](#richeditorcontroller) | Yes| Controller for the **\<RichEditor>** component.| 137 138 139## RichEditorController 140 141Implements the controller for the **\<RichEditor>** component. 142 143### Objects to Import 144 145``` 146controller: RichEditorController = new RichEditorController() 147``` 148 149### getCaretOffset 150 151getCaretOffset(): number 152 153Obtains the current cursor position. 154 155**Return value** 156 157| Type | Description | 158| ----------------------- | ---------------- | 159| number | Cursor position.| 160 161### setCaretOffset 162 163setCaretOffset(offset: number): boolean 164 165Sets the cursor position. 166 167**Parameters** 168 169| Name| Type| Mandatory| Description | 170| ------ | -------- | ---- | -------------------------------------- | 171| offset | number | Yes| Offset of the cursor. If the value is out of the text range, the setting fails.| 172 173**Return value** 174 175| Type | Description | 176| ----------------------- | ---------------- | 177| boolean | Whether the cursor position is set successfully.| 178 179### addTextSpan 180 181addTextSpan(value: string, options?: RichEditorTextSpanOptions): number 182 183Adds a text span. 184 185**Parameters** 186 187| Name| Type| Mandatory| Description | 188| ------ | -------- | ---- | -------------------------------------- | 189| value | string | Yes | Text content.| 190| options | [RichEditorTextSpanOptions](#richeditortextspanoptions) | No | Text options.| 191 192**Return value** 193 194| Type | Description | 195| ----------------------- | ---------------- | 196| number | Position of the added text span.| 197 198### addImageSpan 199 200addImageSpan(value: PixelMap | ResourceStr, options?: RichEditorImageSpanOptions): number 201 202Adds an image span. 203 204**Parameters** 205 206| Name| Type| Mandatory| Description | 207| ------ | -------- | ---- | -------------------------------------- | 208| value | [PixelMap](../apis/js-apis-image.md#pixelmap7)\|[ResourceStr](ts-types.md#ResourceStr) | Yes | Image content.| 209| options | [RichEditorImageSpanOptions](#richeditorimagespanoptions) | No | Image options.| 210 211**Return value** 212 213| Type | Description | 214| ----------------------- | ---------------- | 215| number | Position of the added image span.| 216 217 218### updateSpanStyle 219 220updateSpanStyle(value: RichEditorUpdateTextSpanStyleOptions | RichEditorUpdateImageSpanStyleOptions): void 221 222Updates the text or image span style. <br>If only part of a span is updated, the span is split into multiple spans based on the updated part and the unupdated part. 223 224**Parameters** 225 226| Name| Type| Mandatory| Description | 227| ------ | -------- | ---- | -------------------------------------- | 228| value | [RichEditorUpdateTextSpanStyleOptions](#richeditorupdatetextspanstyleoptions) \| [RichEditorUpdateImageSpanStyleOptions](#richeditorupdatetextspanstyleoptions) | Yes| Text or image span style options.| 229 230 231### getSpans 232 233getSpans(value?: RichEditorRange): Array<RichEditorTextSpanResult| RichEditorImageSpanResult> 234 235Obtains span information. 236 237**Parameters** 238 239| Name| Type | Mandatory| Description | 240| ------ | ----------------------------------- | ---- | ---------------- | 241| value | [RichEditorRange](#richeditorrange) | No | Range of the target span.| 242 243**Return value** 244 245| Type | Description | 246| ----------------------- | ---------------- | 247| Array<[RichEditorTextSpanResult](#richeditortextspanresult) \| [RichEditorImageSpanResult](#richeditorimagespanresult)> | Text and image span information.| 248 249### deleteSpans 250 251deleteSpans(value?: RichEditorRange): void 252 253Deletes the text and image spans in a specified range. 254 255**Parameters** 256 257| Name| Type| Mandatory| Description | 258| ------ | -------- | ---- | -------------------------------------- | 259| value | [RichEditorRange](#richeditorrange) | No| Range of the target spans. If this parameter is omitted, all text and image spans will be deleted.| 260 261 262## RichEditorSelection 263 264Provides information about the selected content. 265 266| Name | Type | Mandatory| Description | 267| --------- | ------------------------------------------------------------ | ---- | ---------- | 268| selection | [number, number] | Yes | Range of the selected.| 269| spans | Array<[RichEditorTextSpanResult](#richeditortextspanresult)\| [RichEditorImageSpanResult](#richeditorimagespanresult)> | Yes | Span information. | 270 271 272## RichEditorUpdateTextSpanStyleOptions 273 274Defines the text span style options. 275 276| Name| Type| Mandatory| Description | 277| ------ | -------- | ---- | -------------------------------------- | 278| start | number | No| Start position of the text span whose style needs to be updated. If this parameter is omitted or set to a negative value, the value **0** will be used.| 279| end | number | No| End position of the text span whose style needs to be updated. If this parameter is omitted or set to a value beyond the text range, it indicates the end of the text span.| 280| textStyle | [RichEditorTextStyle](#richeditortextstyle) | Yes| Text style.| 281 282 283## RichEditorUpdateImageSpanStyleOptions 284 285Defines the image span style options. 286 287| Name| Type| Mandatory| Description | 288| ------ | -------- | ---- | -------------------------------------- | 289| start | number | No| Start position of the image span whose style needs to be updated. If this parameter is omitted or set to a negative value, the value **0** will be used.| 290| end | number | No| End position of the image span whose style needs to be updated. If this parameter is omitted or set to a value beyond the text range, it indicates the end of the image span.| 291| imageStyle | [RichEditorImageSpanStyle](#richeditorimagespanstyle) | Yes| Image style.| 292 293 294## RichEditorTextSpanOptions 295 296Describes the options for adding a text span. 297 298| Name| Type| Mandatory| Description | 299| ------ | -------- | ---- | -------------------------------------- | 300| offset | number | No | Position of the text span to be added. If this parameter is omitted, the text span will be added to the end of all text strings.| 301| style | [RichEditorTextStyle](#richeditortextstyle) | No | Style of the text span to be added. If this parameter is omitted, the default text style will be used.| 302 303## RichEditorTextStyle 304 305Provides the text style information. 306 307| Name| Type| Mandatory| Description | 308| ------ | -------- | ---- | -------------------------------------- | 309| fontColor | [ResourceColor](ts-types.md#resourcecolor) | No| Font color.<br> Default value: **Color.Black**| 310| fontSize | [Length](ts-types.md#length) \| number | No| Font size.<br>Default value: **16fp**| 311| fontStyle | [FontStyle](ts-appendix-enums.md#fontstyle) | No| Font style.<br>Default value: **FontStyle.Normal**| 312| fontWeight | [FontWeight](ts-appendix-enums.md#fontweight) \| number \| string | No| Font weight.<br>For the number type, the value ranges from 100 to 900, at an interval of 100. A larger value indicates a heavier font weight. The default value is **400**.<br>For the string type, only strings of the number type are supported, for example, **"400"**, **"bold"**, **"bolder"**, **"lighter"**, **"regular"**, and **"medium"**, which correspond to the enumerated values in **FontWeight**.<br>Default value: **FontWeight.Normal**| 313| fontFamily | [ResourceStr](ts-types.md#resourcestr) \| number \| string | No| Font family. The HarmonyOS Sans font and [register custom fonts](../apis/js-apis-font.md) are supported.<br>Default font: **'HarmonyOS Sans'**| 314| decoration | {<br>type: [TextDecorationType](ts-appendix-enums.md#textdecorationtype),<br>color?: [ResourceColor](ts-types.md#resourcecolor)<br>} | No| Style and color of the text decorative line.<br>Default value: {<br>type: TextDecorationType.None,<br>color: Color.Black<br>}| 315 316 317## RichEditorImageSpanOptions 318 319Defines the options for adding an image span. 320 321| Name| Type| Mandatory| Description | 322| ------ | -------- | ---- | -------------------------------------- | 323| offset | number | No | Position of the image span to be added. If this parameter is omitted, the image span will be added to the end of all text strings.| 324| imageStyle | [RichEditorImageSpanStyle](#richeditorimagespanstyle) | No | Image style. If this parameter is omitted, the default image style will be used.| 325 326## RichEditorImageSpanStyle 327 328Provides the image span style information. 329 330| Name| Type| Mandatory| Description | 331| ------ | -------- | ---- | -------------------------------------- | 332| size | [Dimension, Dimension] | No| Width and height of the image.| 333| verticalAlign | [ImageSpanAlignment](ts-basic-components-imagespan.md#imagespanalignment) | No | Vertical alignment mode of the image.<br>Default value: **ImageSpanAlignment.BASELINE**| 334| objectFit | [ImageFit](ts-appendix-enums.md#imagefit) | No| Scale mode of the image.<br> Default value: **ImageFit.Cover**| 335 336## RichEditorRange 337 338Provides the span range information. 339 340| Name| Type| Mandatory| Description | 341| ------ | -------- | ---- | -------------------------------------- | 342| start | number | No| Start position. If this parameter is omitted or set to a negative value, the value **0** will be used.| 343| end | number | No| End position of the image span whose style needs to be updated. If this parameter is omitted or set to a value beyond the text range, it indicates the very end.| 344 345 346## Example 347 348### Example 1 349 350```ts 351// xxx.ets 352@Entry 353@Component 354struct Index { 355 controller: RichEditorController = new RichEditorController(); 356 options: RichEditorOptions = { controller: this.controller }; 357 private start: number = -1; 358 private end: number = -1; 359 @State message: string = "[-1, -1]" 360 @State content: string = "" 361 362 build() { 363 Column() { 364 Column() { 365 Text("selection range:").width("100%") 366 Text() { 367 Span(this.message) 368 }.width("100%") 369 Text("selection content:").width("100%") 370 Text() { 371 Span(this.content) 372 }.width("100%") 373 } 374 .borderWidth(1) 375 .borderColor(Color.Red) 376 .width("100%") 377 .height("20%") 378 379 Row() { 380 Button ("Update Style: Bold").onClick(() => { 381 this.controller.updateSpanStyle({ 382 start: this.start, 383 end: this.end, 384 textStyle: 385 { 386 fontWeight: FontWeight.Bolder 387 } 388 }) 389 }) 390 Button("Obtain Selection").onClick(() => { 391 this.content = ""; 392 this.controller.getSpans({ 393 start: this.start, 394 end: this.end 395 }).forEach(item => { 396 if(typeof(item as RichEditorImageSpanResult)['imageStyle'] != 'undefined'){ 397 this.content += (item as RichEditorImageSpanResult).valueResourceStr; 398 this.content += "\n" 399 } else { 400 this.content += (item as RichEditorTextSpanResult).value; 401 this.content += "\n" 402 } 403 }) 404 }) 405 Button("Delete Selection").onClick(() => { 406 this.controller.deleteSpans({ 407 start: this.start, 408 end: this.end 409 }) 410 this.start = -1; 411 this.end = -1; 412 this.message = "[" + this.start + ", " + this.end + "]" 413 }) 414 } 415 .borderWidth(1) 416 .borderColor(Color.Red) 417 .width("100%") 418 .height("10%") 419 420 Column() { 421 RichEditor(this.options) 422 .onReady(() => { 423 this.controller.addTextSpan("0123456789", 424 { 425 style: 426 { 427 fontColor: Color.Orange, 428 fontSize: 30 429 } 430 }) 431 this.controller.addImageSpan($r("app.media.icon"), 432 { 433 imageStyle: 434 { 435 size: ["57px", "57px"] 436 } 437 }) 438 this.controller.addTextSpan("0123456789", 439 { 440 style: 441 { 442 fontColor: Color.Black, 443 fontSize: 30 444 } 445 }) 446 }) 447 .onSelect((value: RichEditorSelection) => { 448 this.start = value.selection[0]; 449 this.end = value.selection[1]; 450 this.message = "[" + this.start + ", " + this.end + "]" 451 }) 452 .aboutToIMEInput((value: RichEditorInsertValue) => { 453 console.log("---------------------- aboutToIMEInput ----------------------") 454 console.log("insertOffset:" + value.insertOffset) 455 console.log("insertValue:" + value.insertValue) 456 return true; 457 }) 458 .onIMEInputComplete((value: RichEditorTextSpanResult) => { 459 console.log("---------------------- onIMEInputComplete ---------------------") 460 console.log("spanIndex:" + value.spanPosition.spanIndex) 461 console.log("spanRange:[" + value.spanPosition.spanRange[0] + "," + value.spanPosition.spanRange[1] + "]") 462 console.log("offsetInSpan:[" + value.offsetInSpan[0] + "," + value.offsetInSpan[1] + "]") 463 console.log("value:" + value.value) 464 }) 465 .aboutToDelete((value: RichEditorDeleteValue) => { 466 console.log("---------------------- aboutToDelete --------------------------") 467 console.log("offset:" + value.offset) 468 console.log("direction:" + value.direction) 469 console.log("length:" + value.length) 470 value.richEditorDeleteSpans.forEach(item => { 471 console.log("---------------------- item --------------------------") 472 console.log("spanIndex:" + item.spanPosition.spanIndex) 473 console.log("spanRange:[" + item.spanPosition.spanRange[0] + "," + item.spanPosition.spanRange[1] + "]") 474 console.log("offsetInSpan:[" + item.offsetInSpan[0] + "," + item.offsetInSpan[1] + "]") 475 if (typeof(item as RichEditorImageSpanResult)['imageStyle'] != 'undefined') { 476 console.log("image:" + (item as RichEditorImageSpanResult).valueResourceStr) 477 } else { 478 console.log("text:" + (item as RichEditorTextSpanResult).value) 479 } 480 }) 481 return true; 482 }) 483 .onDeleteComplete(() => { 484 console.log("---------------------- onDeleteComplete ------------------------") 485 }) 486 .borderWidth(1) 487 .borderColor(Color.Green) 488 .width("100%") 489 .height("30%") 490 } 491 .borderWidth(1) 492 .borderColor(Color.Red) 493 .width("100%") 494 .height("70%") 495 } 496 } 497} 498``` 499 500 501### Example 2 502 503```ts 504// xxx.ets 505@Entry 506@Component 507struct RichEditorExample { 508 controller: RichEditorController = new RichEditorController() 509 510 // Create a custom keyboard component. 511 @Builder CustomKeyboardBuilder() { 512 Column() { 513 Grid() { 514 ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, '*', 0, '#'], (item: number | string) => { 515 GridItem() { 516 Button(item + "") 517 .width(110).onClick(() => { 518 this.controller.addTextSpan(item + '', { 519 offset: this.controller.getCaretOffset(), 520 style: 521 { 522 fontColor: Color.Orange, 523 fontSize: 30 524 } 525 }) 526 this.controller.setCaretOffset(this.controller.getCaretOffset() + item.toString().length) 527 }) 528 } 529 }) 530 }.maxCount(3).columnsGap(10).rowsGap(10).padding(5) 531 }.backgroundColor(Color.Gray) 532 } 533 534 build() { 535 Column() { 536 RichEditor({ controller: this.controller }) 537 // Bind the custom keyboard. 538 .customKeyboard(this.CustomKeyboardBuilder()).margin(10).border({ width: 1 }) 539 .height(200) 540 .borderWidth(1) 541 .borderColor(Color.Red) 542 .width("100%") 543 } 544 } 545} 546``` 547 548 549