• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SelectionMenu
2
3
4The **SelectionMenu** component is a context menu designed for use with the [RichEditor](ts-basic-components-richeditor.md) component, allowing you to bind a custom context menu on selection through the [bindSelectionMenu](./ts-basic-components-richeditor.md#bindselectionmenu) API. This component is not intended for standalone use, and you are advised to display it by right-clicking or by selecting text with a mouse device.
5
6
7> **NOTE**
8>
9> This component is supported since API version 11. Updates will be marked with a superscript to indicate their earliest API version.
10
11
12## Modules to Import
13
14```
15import { SelectionMenu, EditorMenuOptions, ExpandedMenuOptions, EditorEventInfo, SelectionMenuOptions } from '@kit.ArkUI'
16```
17
18## Child Components
19
20Not supported
21
22## SelectionMenu
23
24SelectionMenu(options: SelectionMenuOptions): void
25
26Defines a custom context menu on selection. When the input parameter is empty, the sizes of the menu and its content area are 0, making the menu invisible. In this case, for example, if a right-click context menu is bound to the **RichEditor** component, it will not be displayed when the component is right-clicked.
27
28**Decorator**: @Builder
29
30**Atomic service API**: This API can be used in atomic services since API version 12.
31
32**System capability**: SystemCapability.ArkUI.ArkUI.Full
33
34**Parameters**
35
36| Name| Type| Mandatory| Description|
37| -------- | -------- | -------- | -------- |
38| options | [SelectionMenuOptions](#selectionmenuoptions) | Yes| Options of the context menu on selection.|
39
40## SelectionMenuOptions
41
42Defines the options of the context menu on selection.
43
44**Atomic service API**: This API can be used in atomic services since API version 12.
45
46**System capability**: SystemCapability.ArkUI.ArkUI.Full
47
48| Name| Type| Mandatory| Description|
49| -------- | -------- | -------- | -------- |
50| editorMenuOptions | Array&lt;[EditorMenuOptions](#editormenuoptions)&gt; | No| Edit menu.<br>If **editorMenuOptions** is not set, the edit menu is not displayed.<br>When both **action** and **builder** in **EditorMenuOptions** are configured, clicking the edit icon will trigger both.<br>By default, the context menu is not closed when the edit menu icon is clicked. You can configure **closeSelectionMenu** of **RichEditorController** in **action** to enable the menu to be closed.|
51| expandedMenuOptions | Array&lt;[ExpandedMenuOptions](#expandedmenuoptions)&gt; | No| Expanded drop-down menu options.<br>If this parameter is left empty, the expanded drop-down menu is not displayed.<br>The options configured for **ExpandedMenuOptions** are displayed in the **More** menu option, and clicking **More** shows the expanded drop-down menu.|
52| controller | [RichEditorController](ts-basic-components-richeditor.md#richeditorcontroller) | No| Rich text editor controller. If **controller** is set, the default system menu (including the cut, copy, and paste options) is displayed, and the preset menu features are provided.<br>If **controller** is left empty, the **More** menu option is not displayed. If **expandedMenuOptions** is not empty, the expanded drop-down menu is displayed.<br>By default, the copy and paste feature is only available for rich text. To use the feature for content that includes both text and images, define custom **onCopy** and **onPaste** APIs. If a custom **onCopy** \| **onPaste** API is defined, the default copy and paste feature is ineffective, and the custom API is called instead.<br>**NOTE**<br> When the preset copy option is selected, the custom context menu on selection is hidden, while the selected text is still highlighted.<br> When the preset select-all option is selected, the custom context menu on selection is hidden, while all text is highlighted.<br> When the preset paste option is selected, the style of the copied text is retained, whether the text is pasted to a blank area or not.<br> When the **copyOptions** attribute of the [RichEditor](ts-basic-components-richeditor.md) component is set to **CopyOptions.None**, the preset copy and cut features are not restricted.|
53| onCopy | (event?: [EditorEventInfo](#editoreventinfo)) =&gt; void | No| Event callback to take the place of the preset copy menu option.<br>It is effective only when the **controller** parameter is set and the preset menu is available.<br>**NOTE**<br> **event** indicates the returned information.|
54| onPaste | (event?: [EditorEventInfo](#editoreventinfo)) =&gt; void | No| Event callback to take the place of the preset paste menu option.<br>It is effective only when the **controller** parameter is set and the preset menu is available.<br>**NOTE**<br> **event** indicates the returned information.|
55| onCut | (event?: [EditorEventInfo](#editoreventinfo)) =&gt; void | No| Event callback to take the place of the preset cut menu option.<br>It is effective only when the **controller** parameter is set and the preset menu is available.<br>**NOTE**<br>**event** indicates the returned information.|
56| onSelectAll | (event?: [EditorEventInfo](#editoreventinfo)) =&gt; void | No| Event callback to take the place of the preset select-all menu option.<br>It is effective only when the **controller** parameter is set and the preset menu is available.<br>**NOTE**<br>**event** indicates the returned information.|
57
58
59## EditorMenuOptions
60
61Describes the edit menu options.
62
63**Atomic service API**: This API can be used in atomic services since API version 12.
64
65**System capability**: SystemCapability.ArkUI.ArkUI.Full
66
67| Name| Type| Mandatory| Description|
68| -------- | -------- | -------- | -------- |
69| icon | [ResourceStr](ts-types.md#resourcestr) | Yes| Icon.|
70| symbolStyle<sup>18+</sup> | [SymbolGlyphModifier](ts-universal-attributes-attribute-modifier.md) | No| Symbol icon resource, which has higher priority than **icon**.<br>**Atomic service API**: This API can be used in atomic services since API version 18.|
71| builder | () =&gt; void | No| Builder of the custom component displayed upon click. It must be used with @Builder for building the custom component.|
72| action | () =&gt; void | No| Action triggered when the menu option is clicked.|
73
74
75## ExpandedMenuOptions
76
77Describes the expanded drop-down menu options.
78
79Inherits from [MenuItemOptions](ts-basic-components-menuitem.md#menuitemoptions).
80
81**Atomic service API**: This API can be used in atomic services since API version 12.
82
83**System capability**: SystemCapability.ArkUI.ArkUI.Full
84
85| Name| Type| Mandatory| Description|
86| -------- | -------- | -------- | -------- |
87| action | () =&gt; void | No| Action triggered when the menu option is clicked.|
88
89## EditorEventInfo
90
91Provides the information about the selected content.
92
93**Atomic service API**: This API can be used in atomic services since API version 12.
94
95**System capability**: SystemCapability.ArkUI.ArkUI.Full
96
97| Name| Type| Mandatory| Description|
98| -------- | -------- | -------- | -------- |
99| content | [RichEditorSelection](ts-basic-components-richeditor.md#richeditorselection) | No| Information about the selected content.|
100
101## Attributes
102
103The [universal attributes](ts-component-general-attributes.md) are not supported. The default width is 224 vp, and the height is adaptive.
104
105## Events
106The [universal events](ts-component-general-events.md) are not supported.
107
108## Example
109### Example 1: Binding Context Menus on Selection with Different Trigger Methods
110
111This example demonstrates the effects of a custom context menu on selection bound to text with different triggering modes.
112
113```ts
114import {
115  SelectionMenu,
116  EditorMenuOptions,
117  ExpandedMenuOptions,
118  EditorEventInfo,
119  SelectionMenuOptions
120} from '@kit.ArkUI'
121
122@Entry
123@Component
124struct Index {
125  @State select: boolean = true;
126  controller: RichEditorController = new RichEditorController();
127  options: RichEditorOptions = { controller: this.controller };
128  @State message: string = 'Hello world';
129  @State textSize: number = 30;
130  @State fontWeight: FontWeight = FontWeight.Normal;
131  @State start: number = -1;
132  @State end: number = -1;
133  @State visibleValue: Visibility = Visibility.Visible;
134  @State colorTransparent: Color = Color.Transparent;
135  @State textStyle: RichEditorTextStyle = {};
136  private editorMenuOptions: Array<EditorMenuOptions> =
137    [
138      {
139        icon: $r("app.media.ic_notepad_textbold"), action: () => {
140        if (this.controller) {
141          let selection = this.controller.getSelection();
142          let spans = selection.spans;
143          spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
144            if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
145              let span = item as RichEditorTextSpanResult;
146              this.textStyle = span.textStyle;
147              let start = span.offsetInSpan[0];
148              let end = span.offsetInSpan[1];
149              let offset = span.spanPosition.spanRange[0];
150              if (this.textStyle.fontWeight != 11) {
151                this.textStyle.fontWeight = FontWeight.Bolder;
152              } else {
153                this.textStyle.fontWeight = FontWeight.Normal;
154              }
155              this.controller.updateSpanStyle({
156                start: offset + start,
157                end: offset + end,
158                textStyle: this.textStyle
159              })
160            }
161          })
162        }
163      }
164      },
165      {
166        icon: $r("app.media.ic_notepad_texttilt"), action: () => {
167        if (this.controller) {
168          let selection = this.controller.getSelection();
169          let spans = selection.spans;
170          spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
171            if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
172              let span = item as RichEditorTextSpanResult;
173              this.textStyle = span.textStyle;
174              let start = span.offsetInSpan[0];
175              let end = span.offsetInSpan[1];
176              let offset = span.spanPosition.spanRange[0];
177              if (this.textStyle.fontStyle == FontStyle.Italic) {
178                this.textStyle.fontStyle = FontStyle.Normal;
179              } else {
180                this.textStyle.fontStyle = FontStyle.Italic;
181              }
182              this.controller.updateSpanStyle({
183                start: offset + start,
184                end: offset + end,
185                textStyle: this.textStyle
186              })
187            }
188          })
189        }
190      }
191      },
192      {
193        icon: $r("app.media.ic_notepad_underline"),
194        action: () => {
195          if (this.controller) {
196            let selection = this.controller.getSelection();
197            let spans = selection.spans;
198            spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
199              if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
200                let span = item as RichEditorTextSpanResult;
201                this.textStyle = span.textStyle;
202                let start = span.offsetInSpan[0];
203                let end = span.offsetInSpan[1];
204                let offset = span.spanPosition.spanRange[0];
205                if (this.textStyle.decoration) {
206                  if (this.textStyle.decoration.type == TextDecorationType.Underline) {
207                    this.textStyle.decoration.type = TextDecorationType.None;
208                  } else {
209                    this.textStyle.decoration.type = TextDecorationType.Underline;
210                  }
211                } else {
212                  this.textStyle.decoration = { type: TextDecorationType.Underline, color: Color.Black }
213                }
214                this.controller.updateSpanStyle({
215                  start: offset + start,
216                  end: offset + end,
217                  textStyle: this.textStyle
218                })
219              }
220            })
221          }
222        }
223      },
224      {
225        icon: $r("app.media.ic_notepad_fontsize"), action: () => {
226      }, builder: (): void => this.sliderPanel()
227      },
228      {
229        icon: $r("app.media.ic_notepad_textcolor"), action: () => {
230        if (this.controller) {
231          let selection = this.controller.getSelection();
232          let spans = selection.spans;
233          spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
234            if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
235              let span = item as RichEditorTextSpanResult;
236              this.textStyle = span.textStyle;
237              let start = span.offsetInSpan[0];
238              let end = span.offsetInSpan[1];
239              let offset = span.spanPosition.spanRange[0];
240              if (this.textStyle.fontColor == Color.Orange || this.textStyle.fontColor == '#FFFFA500') {
241                this.textStyle.fontColor = Color.Black;
242              } else {
243                this.textStyle.fontColor = Color.Orange;
244              }
245              this.controller.updateSpanStyle({
246                start: offset + start,
247                end: offset + end,
248                textStyle: this.textStyle
249              })
250            }
251          })
252        }
253      }
254      }]
255  private expandedMenuOptions: Array<ExpandedMenuOptions> =
256    [{
257      startIcon: $r("app.media.startIcon"), content: 'Dictionary', action: () => {
258      }
259    }, {
260      startIcon: $r("app.media.startIcon"), content: 'Translate', action: () => {
261      }
262    }, {
263      startIcon: $r("app.media.startIcon"), content: 'Search', action: () => {
264      }
265    }]
266  private expandedMenuOptions1: Array<ExpandedMenuOptions> = []
267  private editorMenuOptions1: Array<EditorMenuOptions> = []
268  private selectionMenuOptions: SelectionMenuOptions = {
269    editorMenuOptions: this.editorMenuOptions,
270    expandedMenuOptions: this.expandedMenuOptions,
271    controller: this.controller,
272    onCut: (event?: EditorEventInfo) => {
273      if (event && event.content) {
274        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
275          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
276            let span = item as RichEditorTextSpanResult;
277            console.info('test cut' + span.value);
278            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
279          }
280        })
281      }
282    },
283    onPaste: (event?: EditorEventInfo) => {
284      if (event && event.content) {
285        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
286          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
287            let span = item as RichEditorTextSpanResult;
288            console.info('test onPaste' + span.value);
289            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
290          }
291        })
292      }
293    },
294    onCopy: (event?: EditorEventInfo) => {
295      if (event && event.content) {
296        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
297          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
298            let span = item as RichEditorTextSpanResult;
299            console.info('test cut' + span.value);
300            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
301          }
302        })
303      }
304    },
305    onSelectAll: (event?: EditorEventInfo) => {
306      if (event && event.content) {
307        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
308          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
309            let span = item as RichEditorTextSpanResult;
310            console.info('test onPaste' + span.value);
311            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
312          }
313        })
314      }
315    }
316  }
317
318  @Builder
319  sliderPanel() {
320    Column() {
321      Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
322        Text('A').fontSize(15)
323        Slider({ value: this.textSize, step: 10, style: SliderStyle.InSet })
324          .width(210)
325          .onChange((value: number, mode: SliderChangeMode) => {
326            if (this.controller) {
327              let selection = this.controller.getSelection();
328              if (mode == SliderChangeMode.End) {
329                if (this.textSize == undefined) {
330                  this.textSize = 0;
331                }
332                let spans = selection.spans;
333                spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
334                  if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
335                    this.textSize = Math.max(this.textSize, (item as RichEditorTextSpanResult).textStyle.fontSize);
336                  }
337                })
338              }
339              if (mode == SliderChangeMode.Moving || mode == SliderChangeMode.Click) {
340                this.start = selection.selection[0];
341                this.end = selection.selection[1];
342                this.textSize = value;
343                this.controller.updateSpanStyle({
344                  start: this.start,
345                  end: this.end,
346                  textStyle: { fontSize: this.textSize }
347                })
348              }
349            }
350          })
351        Text('A').fontSize(20).fontWeight(FontWeight.Medium)
352      }.borderRadius($r('sys.float.ohos_id_corner_radius_card'))
353    }
354    .shadow(ShadowStyle.OUTER_DEFAULT_MD)
355    .backgroundColor(Color.White)
356    .borderRadius($r('sys.float.ohos_id_corner_radius_card'))
357    .padding(15)
358    .height(48)
359  }
360
361  @Builder
362  MyMenu() {
363    Column() {
364      SelectionMenu(this.selectionMenuOptions)
365    }
366    .width(256)
367    .backgroundColor(Color.Transparent)
368  }
369
370  @Builder
371  MyMenu2() {
372    Column() {
373      SelectionMenu({
374        editorMenuOptions: this.editorMenuOptions,
375        expandedMenuOptions: this.expandedMenuOptions1,
376        controller: this.controller,
377      })
378    }
379    .width(256)
380    .backgroundColor(Color.Transparent)
381  }
382
383  @Builder
384  MyMenu3() {
385    Column() {
386      SelectionMenu({
387        editorMenuOptions: this.editorMenuOptions,
388        expandedMenuOptions: this.expandedMenuOptions,
389        controller: this.controller,
390      })
391    }
392    .width(256)
393    .backgroundColor(Color.Transparent)
394  }
395
396  build() {
397    Column() {
398      Button("SetSelection")
399        .onClick((event: ClickEvent) => {
400          if (this.controller) {
401            this.controller.setSelection(0, 2);
402          }
403        })
404
405      RichEditor(this.options)
406        .onReady(() => {
407          this.controller.addTextSpan(this.message, { style: { fontColor: Color.Orange, fontSize: 30 } });
408          this.controller.addTextSpan(this.message, { style: { fontColor: Color.Black, fontSize: 25 } });
409        })
410        .onSelect((value: RichEditorSelection) => {
411          if (value.selection[0] == -1 && value.selection[1] == -1) {
412            return;
413          }
414          this.start = value.selection[0];
415          this.end = value.selection[1];
416        })
417        .bindSelectionMenu(RichEditorSpanType.TEXT, this.MyMenu3(), RichEditorResponseType.RIGHT_CLICK)
418        .bindSelectionMenu(RichEditorSpanType.TEXT, this.MyMenu2(), RichEditorResponseType.SELECT)
419        .borderWidth(1)
420        .borderColor(Color.Red)
421        .width(200)
422        .height(200)
423        .margin(10)
424    }
425  }
426}
427```
428> **NOTE**
429>
430> Icons in bold and italics are not preset in the system. The sample code uses the default icons. You need to replace the icons in **editorMenuOptions** with the desired icons.
431
432
433
434### Example 2: Setting the Symbol Icon
435
436This example demonstrates how to use **symbolStyle** in **EditorMenuOptions** to set custom symbol icons.
437
438```ts
439import {
440  SelectionMenu,
441  EditorMenuOptions,
442  ExpandedMenuOptions,
443  EditorEventInfo,
444  SelectionMenuOptions,
445  SymbolGlyphModifier
446} from '@kit.ArkUI'
447
448@Entry
449@Component
450struct Index {
451  @State select: boolean = true;
452  controller: RichEditorController = new RichEditorController();
453  options: RichEditorOptions = { controller: this.controller };
454  @State message: string = 'Hello world';
455  @State textSize: number = 30;
456  @State fontWeight: FontWeight = FontWeight.Normal;
457  @State start: number = -1;
458  @State end: number = -1;
459  @State visibleValue: Visibility = Visibility.Visible;
460  @State colorTransparent: Color = Color.Transparent;
461  @State textStyle: RichEditorTextStyle = {};
462  private editorMenuOptions: Array<EditorMenuOptions> =
463    [
464      {
465        icon: $r("sys.media.wifi_router_fill"),
466        symbolStyle: new SymbolGlyphModifier($r('sys.symbol.save')),
467        action: () => {
468          if (this.controller) {
469            let selection = this.controller.getSelection();
470            let spans = selection.spans;
471            spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
472              if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
473                let span = item as RichEditorTextSpanResult;
474                this.textStyle = span.textStyle;
475                let start = span.offsetInSpan[0];
476                let end = span.offsetInSpan[1];
477                let offset = span.spanPosition.spanRange[0];
478                if (this.textStyle.fontWeight != 11) {
479                  this.textStyle.fontWeight = FontWeight.Bolder;
480                } else {
481                  this.textStyle.fontWeight = FontWeight.Normal;
482                }
483                this.controller.updateSpanStyle({
484                  start: offset + start,
485                  end: offset + end,
486                  textStyle: this.textStyle
487                })
488              }
489            })
490          }
491        }
492      },
493      {
494        icon: $r("sys.media.save_button_picture"),
495        symbolStyle: new SymbolGlyphModifier($r('sys.symbol.camera')),
496        action: () => {
497          if (this.controller) {
498            let selection = this.controller.getSelection();
499            let spans = selection.spans;
500            spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
501              if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
502                let span = item as RichEditorTextSpanResult;
503                this.textStyle = span.textStyle;
504                let start = span.offsetInSpan[0];
505                let end = span.offsetInSpan[1];
506                let offset = span.spanPosition.spanRange[0];
507                if (this.textStyle.fontStyle == FontStyle.Italic) {
508                  this.textStyle.fontStyle = FontStyle.Normal;
509                } else {
510                  this.textStyle.fontStyle = FontStyle.Italic;
511                }
512                this.controller.updateSpanStyle({
513                  start: offset + start,
514                  end: offset + end,
515                  textStyle: this.textStyle
516                })
517              }
518            })
519          }
520        }
521      },
522      {
523        icon: $r("sys.media.waveform_folder_fill"),
524        symbolStyle: new SymbolGlyphModifier($r('sys.symbol.car')),
525        action: () => {
526          if (this.controller) {
527            let selection = this.controller.getSelection();
528            let spans = selection.spans;
529            spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
530              if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
531                let span = item as RichEditorTextSpanResult;
532                this.textStyle = span.textStyle;
533                let start = span.offsetInSpan[0];
534                let end = span.offsetInSpan[1];
535                let offset = span.spanPosition.spanRange[0];
536                if (this.textStyle.decoration) {
537                  if (this.textStyle.decoration.type == TextDecorationType.Underline) {
538                    this.textStyle.decoration.type = TextDecorationType.None;
539                  } else {
540                    this.textStyle.decoration.type = TextDecorationType.Underline;
541                  }
542                } else {
543                  this.textStyle.decoration = { type: TextDecorationType.Underline, color: Color.Black }
544                }
545                this.controller.updateSpanStyle({
546                  start: offset + start,
547                  end: offset + end,
548                  textStyle: this.textStyle
549                })
550              }
551            })
552          }
553        }
554      },
555      {
556        icon: $r("app.media.app_icon"), action: () => {
557      }, builder: (): void => this.sliderPanel()
558      },
559      {
560        icon: $r("sys.media.thermometer_fill"), action: () => {
561        if (this.controller) {
562          let selection = this.controller.getSelection();
563          let spans = selection.spans;
564          spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
565            if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
566              let span = item as RichEditorTextSpanResult;
567              this.textStyle = span.textStyle;
568              let start = span.offsetInSpan[0];
569              let end = span.offsetInSpan[1];
570              let offset = span.spanPosition.spanRange[0];
571              if (this.textStyle.fontColor == Color.Orange || this.textStyle.fontColor == '#FFFFA500') {
572                this.textStyle.fontColor = Color.Black;
573              } else {
574                this.textStyle.fontColor = Color.Orange;
575              }
576              this.controller.updateSpanStyle({
577                start: offset + start,
578                end: offset + end,
579                textStyle: this.textStyle
580              })
581            }
582          })
583        }
584      }
585      }]
586  private expandedMenuOptions: Array<ExpandedMenuOptions> =
587    [{
588      startIcon: $r("app.media.startIcon"), content: 'Dictionary', action: () => {
589      }
590    }, {
591      startIcon: $r("app.media.startIcon"), content: 'Translate', action: () => {
592      }
593    }, {
594      startIcon: $r("app.media.startIcon"), content: 'Search', action: () => {
595      }
596    }]
597  private expandedMenuOptions1: Array<ExpandedMenuOptions> = []
598  private editorMenuOptions1: Array<EditorMenuOptions> = []
599  private selectionMenuOptions: SelectionMenuOptions = {
600    editorMenuOptions: this.editorMenuOptions,
601    expandedMenuOptions: this.expandedMenuOptions,
602    controller: this.controller,
603    onCut: (event?: EditorEventInfo) => {
604      if (event && event.content) {
605        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
606          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
607            let span = item as RichEditorTextSpanResult;
608            console.info('test cut' + span.value);
609            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
610          }
611        })
612      }
613    },
614    onPaste: (event?: EditorEventInfo) => {
615      if (event && event.content) {
616        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
617          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
618            let span = item as RichEditorTextSpanResult;
619            console.info('test onPaste' + span.value);
620            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
621          }
622        })
623      }
624    },
625    onCopy: (event?: EditorEventInfo) => {
626      if (event && event.content) {
627        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
628          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
629            let span = item as RichEditorTextSpanResult;
630            console.info('test cut' + span.value);
631            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
632          }
633        })
634      }
635    },
636    onSelectAll: (event?: EditorEventInfo) => {
637      if (event && event.content) {
638        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
639          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
640            let span = item as RichEditorTextSpanResult;
641            console.info('test onPaste' + span.value);
642            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
643          }
644        })
645      }
646    }
647  }
648
649  @Builder
650  sliderPanel() {
651    Column() {
652      Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
653        Text('A').fontSize(15)
654        Slider({ value: this.textSize, step: 10, style: SliderStyle.InSet })
655          .width(210)
656          .onChange((value: number, mode: SliderChangeMode) => {
657            if (this.controller) {
658              let selection = this.controller.getSelection();
659              if (mode == SliderChangeMode.End) {
660                if (this.textSize == undefined) {
661                  this.textSize = 0;
662                }
663                let spans = selection.spans;
664                spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
665                  if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
666                    this.textSize = Math.max(this.textSize, (item as RichEditorTextSpanResult).textStyle.fontSize);
667                  }
668                })
669              }
670              if (mode == SliderChangeMode.Moving || mode == SliderChangeMode.Click) {
671                this.start = selection.selection[0];
672                this.end = selection.selection[1];
673                this.textSize = value;
674                this.controller.updateSpanStyle({
675                  start: this.start,
676                  end: this.end,
677                  textStyle: { fontSize: this.textSize }
678                })
679              }
680            }
681          })
682        Text('A').fontSize(20).fontWeight(FontWeight.Medium)
683      }.borderRadius($r('sys.float.ohos_id_corner_radius_card'))
684    }
685    .shadow(ShadowStyle.OUTER_DEFAULT_MD)
686    .backgroundColor(Color.White)
687    .borderRadius($r('sys.float.ohos_id_corner_radius_card'))
688    .padding(15)
689    .height(48)
690  }
691
692  @Builder
693  MyMenu() {
694    Column() {
695      SelectionMenu(this.selectionMenuOptions)
696    }
697    .width(256)
698    .backgroundColor(Color.Transparent)
699  }
700
701  @Builder
702  MyMenu2() {
703    Column() {
704      SelectionMenu({
705        editorMenuOptions: this.editorMenuOptions,
706        expandedMenuOptions: this.expandedMenuOptions1,
707        controller: this.controller,
708      })
709    }
710    .width(256)
711    .backgroundColor(Color.Transparent)
712  }
713
714  @Builder
715  MyMenu3() {
716    Column() {
717      SelectionMenu({
718        editorMenuOptions: this.editorMenuOptions1,
719        expandedMenuOptions: this.expandedMenuOptions,
720        controller: this.controller,
721      })
722    }
723    .width(256)
724    .backgroundColor(Color.Transparent)
725  }
726
727  build() {
728    Column() {
729      Button("SetSelection")
730        .onClick((event: ClickEvent) => {
731          if (this.controller) {
732            this.controller.setSelection(0, 2);
733          }
734        })
735
736      RichEditor(this.options)
737        .onReady(() => {
738          this.controller.addTextSpan(this.message, { style: { fontColor: Color.Orange, fontSize: 30 } });
739          this.controller.addTextSpan(this.message, { style: { fontColor: Color.Black, fontSize: 25 } });
740        })
741        .onSelect((value: RichEditorSelection) => {
742          if (value.selection[0] == -1 && value.selection[1] == -1) {
743            return;
744          }
745          this.start = value.selection[0];
746          this.end = value.selection[1];
747        })
748        .bindSelectionMenu(RichEditorSpanType.TEXT, this.MyMenu3(), RichEditorResponseType.RIGHT_CLICK)
749        .bindSelectionMenu(RichEditorSpanType.TEXT, this.MyMenu2(), RichEditorResponseType.SELECT)
750        .borderWidth(1)
751        .borderColor(Color.Red)
752        .width(200)
753        .height(200)
754    }
755  }
756}
757```
758
759![selectionmenu02](figures/selectionmenu02.jpg)
760