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