• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SelectionMenu
2
3
4文本选择菜单,适用于[RichEditor](ts-basic-components-richeditor.md)组件通过[bindSelectionMenu](ts-basic-components-richeditor.md#bindselectionmenu)或[Text](ts-basic-components-text.md)组件通过[bindSelectionMenu](ts-basic-components-text.md#bindselectionmenu11)绑定自定义文本选择菜单,建议绑定鼠标右键或者鼠标选中方式弹出,不支持作为普通组件单独使用。
5
6
7> **说明:**
8>
9> 该组件从API Version 11开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
10>
11> 该组件不支持在Wearable设备上使用。
12
13
14## 导入模块
15
16```
17import { SelectionMenu, EditorMenuOptions, ExpandedMenuOptions, EditorEventInfo, SelectionMenuOptions } from '@kit.ArkUI'
18```
19
20## 子组件
21
22无。
23
24## SelectionMenu
25
26SelectionMenu(options: SelectionMenuOptions): void
27
28入参为空时,文本选择菜单组件SelectionMenu内容区大小及组件大小为零。表现例如,富文本组件[RichEditor](ts-basic-components-richeditor.md)使用[bindSelectionMenu](ts-basic-components-richeditor.md#bindselectionmenu)接口绑定一个SelectionMenu的右键菜单,则右键富文本组件区域时无任何菜单弹出。
29
30**装饰器类型:**\@Builder
31
32**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
33
34**系统能力:** SystemCapability.ArkUI.ArkUI.Full
35
36**参数:**
37
38| 参数名 | 类型 | 必填 | 说明 |
39| -------- | -------- | -------- | -------- |
40| options | [SelectionMenuOptions](#selectionmenuoptions) | 是 | 文本选择菜单可选项。 |
41
42## SelectionMenuOptions
43
44SelectionMenuOptions定义SelectionMenu的可选菜单类型项及其具体配置参数。
45
46**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
47
48**系统能力:** SystemCapability.ArkUI.ArkUI.Full
49
50| 名称 | 类型 | 必填 | 说明 |
51| -------- | -------- | -------- | -------- |
52| editorMenuOptions | Array&lt;[EditorMenuOptions](#editormenuoptions)&gt; | 否 | 编辑菜单。<br/>editorMenuOptions未配置时,不显示编辑菜单。<br/>同时配置EditorMenuOptions中action和builder时,点击图标会同时响应。<br/>点击编辑菜单图标默认不关闭整个菜单,应用可以通过action接口配置RichEditorController的closeSelectionMenu主动关闭菜单。 |
53| expandedMenuOptions | Array&lt;[ExpandedMenuOptions](#expandedmenuoptions)&gt; | 否 | 扩展下拉菜单。<br/>expandedMenuOptions参数为空时无更多按钮,不显示扩展下拉菜单。<br/>expandedMenuOptions参数不为空时显示更多按钮,配置菜单项收起在更多按钮中,点击更多按钮展示。 |
54| controller | [RichEditorController](ts-basic-components-richeditor.md#richeditorcontroller) | 否 | 富文本控制器不为空时显示默认系统菜单(包含剪切复制粘贴等部分)且默认菜单功能内置。<br/>controller为空时不显示更多按钮,expandedMenuOptions参数不为空则显示下拉菜单中。<br/>系统默认只支持复制粘贴富文本文本内容,图文混排需要应用自定义onCopy、onPaste接口。应用自行配置onCopy \| onPaste接口时,系统菜单默认复制粘贴失效,调用应用自定义函数。 <br/>**说明:**<br/> 点击自定义文本选择菜单内置复制功能选项后,自定义菜单消失选中文本高亮保留。<br/> 点击自定义文本选择菜单内置全选功能选项后,自定义菜单消失文本全选高亮。<br/> 点击自定义文本选择菜单内置粘贴功能选项后,空白处粘贴或者选中文本替换粘贴均是保留被复制文本的样式。<br/> 当富文本组件[RichEditor](ts-basic-components-richeditor.md)的copyOptions属性设置为`CopyOptions.None`时,内置的复制剪切功能不会被限制。|
55| onCopy | (event?: [EditorEventInfo](#editoreventinfo))&nbsp;=&gt;&nbsp;void | 否 | 替代内置系统菜单复制项的事件回调。<br/>生效前提是一定要有controller参数,有系统默认菜单才能替换内置复制功能。<br/>**说明:**<br/> event为返回信息。|
56| onPaste | (event?: [EditorEventInfo](#editoreventinfo))&nbsp;=&gt;&nbsp;void | 否 | 替代内置系统菜单粘贴项的事件回调。<br/>生效前提是一定要有controller参数,有系统默认菜单才能替换内置粘贴功能。<br/>**说明:**<br/> event为返回信息。 |
57| onCut | (event?: [EditorEventInfo](#editoreventinfo))&nbsp;=&gt;&nbsp;void | 否 | 替代内置系统菜单剪切项的事件回调。<br/>生效前提是一定要有controller参数,有系统默认菜单才能替换内置剪切功能。<br/>**说明:**<br/>event为返回信息。|
58| onSelectAll | (event?: [EditorEventInfo](#editoreventinfo))&nbsp;=&gt;&nbsp;void | 否 | 替代内置系统菜单全选项的事件回调。<br/>生效前提是一定要有controller参数,有系统默认菜单才能替换内置全选功能。<br/>**说明:**<br/>event为返回信息。|
59
60
61## EditorMenuOptions
62
63编辑菜单选项。
64
65**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
66
67**系统能力:** SystemCapability.ArkUI.ArkUI.Full
68
69| 名称 | 类型 | 必填 | 说明 |
70| -------- | -------- | -------- | -------- |
71| icon | [ResourceStr](ts-types.md#resourcestr) | 是 | 图标资源。 |
72| symbolStyle<sup>18+</sup> | [SymbolGlyphModifier](ts-universal-attributes-attribute-modifier.md) | 否 | Symbol图标资源,优先级大于icon。<br/>**原子化服务API:** 从API version 18开始,该接口支持在原子化服务中使用。 |
73| builder | ()&nbsp;=&gt;&nbsp;void | 否 | 点击时显示用户自定义组件,自定义组件在构造时结合@Builder使用。 |
74| action | ()&nbsp;=&gt;&nbsp;void | 否 | 点击菜单项的事件回调。 |
75
76
77## ExpandedMenuOptions
78
79扩展下拉菜单。
80
81继承于[MenuItemOptions](ts-basic-components-menuitem.md#menuitemoptions对象说明)。
82
83**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
84
85**系统能力:** SystemCapability.ArkUI.ArkUI.Full
86
87| 名称 | 类型 | 必填 | 说明 |
88| -------- | -------- | -------- | -------- |
89| action | ()&nbsp;=&gt;&nbsp;void | 否 | 点击菜单项的事件回调。 |
90
91## EditorEventInfo
92
93选中内容信息。
94
95**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
96
97**系统能力:** SystemCapability.ArkUI.ArkUI.Full
98
99| 名称 | 类型 | 必填 | 说明 |
100| -------- | -------- | -------- | -------- |
101| content | [RichEditorSelection](ts-basic-components-richeditor.md#richeditorselection) | 否 | 选中内容信息。|
102
103## 属性
104
105不支持[通用属性](ts-component-general-attributes.md),宽度默认224vp, 高度自适应内容。
106
107## 事件
108不支持[通用事件](ts-component-general-events.md)。
109
110## 示例
111### 示例1(绑定不同触发方式的自定义文本选择菜单)
112
113该示例展示了文本绑定不同触发方式的自定义文本选择菜单的效果。
114
115```ts
116import {
117  SelectionMenu,
118  EditorMenuOptions,
119  ExpandedMenuOptions,
120  EditorEventInfo,
121  SelectionMenuOptions
122} from '@kit.ArkUI'
123
124@Entry
125@Component
126struct Index {
127  @State select: boolean = true;
128  controller: RichEditorController = new RichEditorController();
129  options: RichEditorOptions = { controller: this.controller };
130  @State message: string = 'Hello world';
131  @State textSize: number = 30;
132  @State fontWeight: FontWeight = FontWeight.Normal;
133  @State start: number = -1;
134  @State end: number = -1;
135  @State visibleValue: Visibility = Visibility.Visible;
136  @State colorTransparent: Color = Color.Transparent;
137  @State textStyle: RichEditorTextStyle = {};
138  private editorMenuOptions: Array<EditorMenuOptions> =
139    [
140      {
141        icon: $r("app.media.ic_notepad_textbold"), action: () => {
142        if (this.controller) {
143          let selection = this.controller.getSelection();
144          let spans = selection.spans;
145          spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
146            if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
147              let span = item as RichEditorTextSpanResult;
148              this.textStyle = span.textStyle;
149              let start = span.offsetInSpan[0];
150              let end = span.offsetInSpan[1];
151              let offset = span.spanPosition.spanRange[0];
152              if (this.textStyle.fontWeight != 11) {
153                this.textStyle.fontWeight = FontWeight.Bolder;
154              } else {
155                this.textStyle.fontWeight = FontWeight.Normal;
156              }
157              this.controller.updateSpanStyle({
158                start: offset + start,
159                end: offset + end,
160                textStyle: this.textStyle
161              })
162            }
163          })
164        }
165      }
166      },
167      {
168        icon: $r("app.media.ic_notepad_texttilt"), action: () => {
169        if (this.controller) {
170          let selection = this.controller.getSelection();
171          let spans = selection.spans;
172          spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
173            if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
174              let span = item as RichEditorTextSpanResult;
175              this.textStyle = span.textStyle;
176              let start = span.offsetInSpan[0];
177              let end = span.offsetInSpan[1];
178              let offset = span.spanPosition.spanRange[0];
179              if (this.textStyle.fontStyle == FontStyle.Italic) {
180                this.textStyle.fontStyle = FontStyle.Normal;
181              } else {
182                this.textStyle.fontStyle = FontStyle.Italic;
183              }
184              this.controller.updateSpanStyle({
185                start: offset + start,
186                end: offset + end,
187                textStyle: this.textStyle
188              })
189            }
190          })
191        }
192      }
193      },
194      {
195        icon: $r("app.media.ic_notepad_underline"),
196        action: () => {
197          if (this.controller) {
198            let selection = this.controller.getSelection();
199            let spans = selection.spans;
200            spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
201              if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
202                let span = item as RichEditorTextSpanResult;
203                this.textStyle = span.textStyle;
204                let start = span.offsetInSpan[0];
205                let end = span.offsetInSpan[1];
206                let offset = span.spanPosition.spanRange[0];
207                if (this.textStyle.decoration) {
208                  if (this.textStyle.decoration.type == TextDecorationType.Underline) {
209                    this.textStyle.decoration.type = TextDecorationType.None;
210                  } else {
211                    this.textStyle.decoration.type = TextDecorationType.Underline;
212                  }
213                } else {
214                  this.textStyle.decoration = { type: TextDecorationType.Underline, color: Color.Black }
215                }
216                this.controller.updateSpanStyle({
217                  start: offset + start,
218                  end: offset + end,
219                  textStyle: this.textStyle
220                })
221              }
222            })
223          }
224        }
225      },
226      {
227        icon: $r("app.media.ic_notepad_fontsize"), action: () => {
228      }, builder: (): void => this.sliderPanel()
229      },
230      {
231        icon: $r("app.media.ic_notepad_textcolor"), action: () => {
232        if (this.controller) {
233          let selection = this.controller.getSelection();
234          let spans = selection.spans;
235          spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
236            if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
237              let span = item as RichEditorTextSpanResult;
238              this.textStyle = span.textStyle;
239              let start = span.offsetInSpan[0];
240              let end = span.offsetInSpan[1];
241              let offset = span.spanPosition.spanRange[0];
242              if (this.textStyle.fontColor == Color.Orange || this.textStyle.fontColor == '#FFFFA500') {
243                this.textStyle.fontColor = Color.Black;
244              } else {
245                this.textStyle.fontColor = Color.Orange;
246              }
247              this.controller.updateSpanStyle({
248                start: offset + start,
249                end: offset + end,
250                textStyle: this.textStyle
251              })
252            }
253          })
254        }
255      }
256      }]
257  private expandedMenuOptions: Array<ExpandedMenuOptions> =
258    [{
259      startIcon: $r("app.media.startIcon"), content: '词典', action: () => {
260      }
261    }, {
262      startIcon: $r("app.media.startIcon"), content: '翻译', action: () => {
263      }
264    }, {
265      startIcon: $r("app.media.startIcon"), content: '搜索', action: () => {
266      }
267    }]
268  private expandedMenuOptions1: Array<ExpandedMenuOptions> = []
269  private editorMenuOptions1: Array<EditorMenuOptions> = []
270  private selectionMenuOptions: SelectionMenuOptions = {
271    editorMenuOptions: this.editorMenuOptions,
272    expandedMenuOptions: this.expandedMenuOptions,
273    controller: this.controller,
274    onCut: (event?: EditorEventInfo) => {
275      if (event && event.content) {
276        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
277          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
278            let span = item as RichEditorTextSpanResult;
279            console.info('test cut' + span.value);
280            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
281          }
282        })
283      }
284    },
285    onPaste: (event?: EditorEventInfo) => {
286      if (event && event.content) {
287        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
288          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
289            let span = item as RichEditorTextSpanResult;
290            console.info('test onPaste' + span.value);
291            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
292          }
293        })
294      }
295    },
296    onCopy: (event?: EditorEventInfo) => {
297      if (event && event.content) {
298        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
299          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
300            let span = item as RichEditorTextSpanResult;
301            console.info('test cut' + span.value);
302            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
303          }
304        })
305      }
306    },
307    onSelectAll: (event?: EditorEventInfo) => {
308      if (event && event.content) {
309        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
310          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
311            let span = item as RichEditorTextSpanResult;
312            console.info('test onPaste' + span.value);
313            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
314          }
315        })
316      }
317    }
318  }
319
320  @Builder
321  sliderPanel() {
322    Column() {
323      Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
324        Text('A').fontSize(15)
325        Slider({ value: this.textSize, step: 10, style: SliderStyle.InSet })
326          .width(210)
327          .onChange((value: number, mode: SliderChangeMode) => {
328            if (this.controller) {
329              let selection = this.controller.getSelection();
330              if (mode == SliderChangeMode.End) {
331                if (this.textSize == undefined) {
332                  this.textSize = 0;
333                }
334                let spans = selection.spans;
335                spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
336                  if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
337                    this.textSize = Math.max(this.textSize, (item as RichEditorTextSpanResult).textStyle.fontSize);
338                  }
339                })
340              }
341              if (mode == SliderChangeMode.Moving || mode == SliderChangeMode.Click) {
342                this.start = selection.selection[0];
343                this.end = selection.selection[1];
344                this.textSize = value;
345                this.controller.updateSpanStyle({
346                  start: this.start,
347                  end: this.end,
348                  textStyle: { fontSize: this.textSize }
349                })
350              }
351            }
352          })
353        Text('A').fontSize(20).fontWeight(FontWeight.Medium)
354      }.borderRadius($r('sys.float.ohos_id_corner_radius_card'))
355    }
356    .shadow(ShadowStyle.OUTER_DEFAULT_MD)
357    .backgroundColor(Color.White)
358    .borderRadius($r('sys.float.ohos_id_corner_radius_card'))
359    .padding(15)
360    .height(48)
361  }
362
363  @Builder
364  MyMenu() {
365    Column() {
366      SelectionMenu(this.selectionMenuOptions)
367    }
368    .width(256)
369    .backgroundColor(Color.Transparent)
370  }
371
372  @Builder
373  MyMenu2() {
374    Column() {
375      SelectionMenu({
376        editorMenuOptions: this.editorMenuOptions,
377        expandedMenuOptions: this.expandedMenuOptions1,
378        controller: this.controller,
379      })
380    }
381    .width(256)
382    .backgroundColor(Color.Transparent)
383  }
384
385  @Builder
386  MyMenu3() {
387    Column() {
388      SelectionMenu({
389        editorMenuOptions: this.editorMenuOptions,
390        expandedMenuOptions: this.expandedMenuOptions,
391        controller: this.controller,
392      })
393    }
394    .width(256)
395    .backgroundColor(Color.Transparent)
396  }
397
398  build() {
399    Column() {
400      Button("SetSelection")
401        .onClick((event: ClickEvent) => {
402          if (this.controller) {
403            this.controller.setSelection(0, 2);
404          }
405        })
406
407      RichEditor(this.options)
408        .onReady(() => {
409          this.controller.addTextSpan(this.message, { style: { fontColor: Color.Orange, fontSize: 30 } });
410          this.controller.addTextSpan(this.message, { style: { fontColor: Color.Black, fontSize: 25 } });
411        })
412        .onSelect((value: RichEditorSelection) => {
413          if (value.selection[0] == -1 && value.selection[1] == -1) {
414            return;
415          }
416          this.start = value.selection[0];
417          this.end = value.selection[1];
418        })
419        .bindSelectionMenu(RichEditorSpanType.TEXT, this.MyMenu3(), RichEditorResponseType.RIGHT_CLICK)
420        .bindSelectionMenu(RichEditorSpanType.TEXT, this.MyMenu2(), RichEditorResponseType.SELECT)
421        .borderWidth(1)
422        .borderColor(Color.Red)
423        .width(200)
424        .height(200)
425        .margin(10)
426    }
427  }
428}
429```
430> **说明:**
431>
432> 系统暂未预置加粗、斜体等图标,示例代码使用本地资源图标,开发者使用时需自行替换editorMenuOptions中icon项的资源。
433
434![selectionmenu](figures/selectionmenu.gif)
435
436### 示例2(设置Symbol类型图标)
437
438该示例通过设置EditorMenuOptions的属性symbolStyle,展示了自定义Symbol类型图标。
439
440```ts
441import {
442  SelectionMenu,
443  EditorMenuOptions,
444  ExpandedMenuOptions,
445  EditorEventInfo,
446  SelectionMenuOptions,
447  SymbolGlyphModifier
448} from '@kit.ArkUI'
449
450@Entry
451@Component
452struct Index {
453  @State select: boolean = true;
454  controller: RichEditorController = new RichEditorController();
455  options: RichEditorOptions = { controller: this.controller };
456  @State message: string = 'Hello world';
457  @State textSize: number = 30;
458  @State fontWeight: FontWeight = FontWeight.Normal;
459  @State start: number = -1;
460  @State end: number = -1;
461  @State visibleValue: Visibility = Visibility.Visible;
462  @State colorTransparent: Color = Color.Transparent;
463  @State textStyle: RichEditorTextStyle = {};
464  private editorMenuOptions: Array<EditorMenuOptions> =
465    [
466      {
467        icon: $r("sys.media.wifi_router_fill"),
468        symbolStyle: new SymbolGlyphModifier($r('sys.symbol.save')),
469        action: () => {
470          if (this.controller) {
471            let selection = this.controller.getSelection();
472            let spans = selection.spans;
473            spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
474              if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
475                let span = item as RichEditorTextSpanResult;
476                this.textStyle = span.textStyle;
477                let start = span.offsetInSpan[0];
478                let end = span.offsetInSpan[1];
479                let offset = span.spanPosition.spanRange[0];
480                if (this.textStyle.fontWeight != 11) {
481                  this.textStyle.fontWeight = FontWeight.Bolder;
482                } else {
483                  this.textStyle.fontWeight = FontWeight.Normal;
484                }
485                this.controller.updateSpanStyle({
486                  start: offset + start,
487                  end: offset + end,
488                  textStyle: this.textStyle
489                })
490              }
491            })
492          }
493        }
494      },
495      {
496        icon: $r("sys.media.save_button_picture"),
497        symbolStyle: new SymbolGlyphModifier($r('sys.symbol.camera')),
498        action: () => {
499          if (this.controller) {
500            let selection = this.controller.getSelection();
501            let spans = selection.spans;
502            spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
503              if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
504                let span = item as RichEditorTextSpanResult;
505                this.textStyle = span.textStyle;
506                let start = span.offsetInSpan[0];
507                let end = span.offsetInSpan[1];
508                let offset = span.spanPosition.spanRange[0];
509                if (this.textStyle.fontStyle == FontStyle.Italic) {
510                  this.textStyle.fontStyle = FontStyle.Normal;
511                } else {
512                  this.textStyle.fontStyle = FontStyle.Italic;
513                }
514                this.controller.updateSpanStyle({
515                  start: offset + start,
516                  end: offset + end,
517                  textStyle: this.textStyle
518                })
519              }
520            })
521          }
522        }
523      },
524      {
525        icon: $r("sys.media.waveform_folder_fill"),
526        symbolStyle: new SymbolGlyphModifier($r('sys.symbol.car')),
527        action: () => {
528          if (this.controller) {
529            let selection = this.controller.getSelection();
530            let spans = selection.spans;
531            spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
532              if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
533                let span = item as RichEditorTextSpanResult;
534                this.textStyle = span.textStyle;
535                let start = span.offsetInSpan[0];
536                let end = span.offsetInSpan[1];
537                let offset = span.spanPosition.spanRange[0];
538                if (this.textStyle.decoration) {
539                  if (this.textStyle.decoration.type == TextDecorationType.Underline) {
540                    this.textStyle.decoration.type = TextDecorationType.None;
541                  } else {
542                    this.textStyle.decoration.type = TextDecorationType.Underline;
543                  }
544                } else {
545                  this.textStyle.decoration = { type: TextDecorationType.Underline, color: Color.Black }
546                }
547                this.controller.updateSpanStyle({
548                  start: offset + start,
549                  end: offset + end,
550                  textStyle: this.textStyle
551                })
552              }
553            })
554          }
555        }
556      },
557      {
558        icon: $r("app.media.app_icon"), action: () => {
559      }, builder: (): void => this.sliderPanel()
560      },
561      {
562        icon: $r("sys.media.thermometer_fill"), action: () => {
563        if (this.controller) {
564          let selection = this.controller.getSelection();
565          let spans = selection.spans;
566          spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
567            if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
568              let span = item as RichEditorTextSpanResult;
569              this.textStyle = span.textStyle;
570              let start = span.offsetInSpan[0];
571              let end = span.offsetInSpan[1];
572              let offset = span.spanPosition.spanRange[0];
573              if (this.textStyle.fontColor == Color.Orange || this.textStyle.fontColor == '#FFFFA500') {
574                this.textStyle.fontColor = Color.Black;
575              } else {
576                this.textStyle.fontColor = Color.Orange;
577              }
578              this.controller.updateSpanStyle({
579                start: offset + start,
580                end: offset + end,
581                textStyle: this.textStyle
582              })
583            }
584          })
585        }
586      }
587      }]
588  private expandedMenuOptions: Array<ExpandedMenuOptions> =
589    [{
590      startIcon: $r("app.media.startIcon"), content: '词典', action: () => {
591      }
592    }, {
593      startIcon: $r("app.media.startIcon"), content: '翻译', action: () => {
594      }
595    }, {
596      startIcon: $r("app.media.startIcon"), content: '搜索', action: () => {
597      }
598    }]
599  private expandedMenuOptions1: Array<ExpandedMenuOptions> = []
600  private editorMenuOptions1: Array<EditorMenuOptions> = []
601  private selectionMenuOptions: SelectionMenuOptions = {
602    editorMenuOptions: this.editorMenuOptions,
603    expandedMenuOptions: this.expandedMenuOptions,
604    controller: this.controller,
605    onCut: (event?: EditorEventInfo) => {
606      if (event && event.content) {
607        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
608          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
609            let span = item as RichEditorTextSpanResult;
610            console.info('test cut' + span.value);
611            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
612          }
613        })
614      }
615    },
616    onPaste: (event?: EditorEventInfo) => {
617      if (event && event.content) {
618        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
619          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
620            let span = item as RichEditorTextSpanResult;
621            console.info('test onPaste' + span.value);
622            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
623          }
624        })
625      }
626    },
627    onCopy: (event?: EditorEventInfo) => {
628      if (event && event.content) {
629        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
630          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
631            let span = item as RichEditorTextSpanResult;
632            console.info('test cut' + span.value);
633            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
634          }
635        })
636      }
637    },
638    onSelectAll: (event?: EditorEventInfo) => {
639      if (event && event.content) {
640        event.content.spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
641          if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
642            let span = item as RichEditorTextSpanResult;
643            console.info('test onPaste' + span.value);
644            console.info('test start ' + span.offsetInSpan[0] + ' end: ' + span.offsetInSpan[1]);
645          }
646        })
647      }
648    }
649  }
650
651  @Builder
652  sliderPanel() {
653    Column() {
654      Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
655        Text('A').fontSize(15)
656        Slider({ value: this.textSize, step: 10, style: SliderStyle.InSet })
657          .width(210)
658          .onChange((value: number, mode: SliderChangeMode) => {
659            if (this.controller) {
660              let selection = this.controller.getSelection();
661              if (mode == SliderChangeMode.End) {
662                if (this.textSize == undefined) {
663                  this.textSize = 0;
664                }
665                let spans = selection.spans;
666                spans.forEach((item: RichEditorTextSpanResult | RichEditorImageSpanResult, index) => {
667                  if (typeof (item as RichEditorTextSpanResult)['textStyle'] != 'undefined') {
668                    this.textSize = Math.max(this.textSize, (item as RichEditorTextSpanResult).textStyle.fontSize);
669                  }
670                })
671              }
672              if (mode == SliderChangeMode.Moving || mode == SliderChangeMode.Click) {
673                this.start = selection.selection[0];
674                this.end = selection.selection[1];
675                this.textSize = value;
676                this.controller.updateSpanStyle({
677                  start: this.start,
678                  end: this.end,
679                  textStyle: { fontSize: this.textSize }
680                })
681              }
682            }
683          })
684        Text('A').fontSize(20).fontWeight(FontWeight.Medium)
685      }.borderRadius($r('sys.float.ohos_id_corner_radius_card'))
686    }
687    .shadow(ShadowStyle.OUTER_DEFAULT_MD)
688    .backgroundColor(Color.White)
689    .borderRadius($r('sys.float.ohos_id_corner_radius_card'))
690    .padding(15)
691    .height(48)
692  }
693
694  @Builder
695  MyMenu() {
696    Column() {
697      SelectionMenu(this.selectionMenuOptions)
698    }
699    .width(256)
700    .backgroundColor(Color.Transparent)
701  }
702
703  @Builder
704  MyMenu2() {
705    Column() {
706      SelectionMenu({
707        editorMenuOptions: this.editorMenuOptions,
708        expandedMenuOptions: this.expandedMenuOptions1,
709        controller: this.controller,
710      })
711    }
712    .width(256)
713    .backgroundColor(Color.Transparent)
714  }
715
716  @Builder
717  MyMenu3() {
718    Column() {
719      SelectionMenu({
720        editorMenuOptions: this.editorMenuOptions1,
721        expandedMenuOptions: this.expandedMenuOptions,
722        controller: this.controller,
723      })
724    }
725    .width(256)
726    .backgroundColor(Color.Transparent)
727  }
728
729  build() {
730    Column() {
731      Button("SetSelection")
732        .onClick((event: ClickEvent) => {
733          if (this.controller) {
734            this.controller.setSelection(0, 2);
735          }
736        })
737
738      RichEditor(this.options)
739        .onReady(() => {
740          this.controller.addTextSpan(this.message, { style: { fontColor: Color.Orange, fontSize: 30 } });
741          this.controller.addTextSpan(this.message, { style: { fontColor: Color.Black, fontSize: 25 } });
742        })
743        .onSelect((value: RichEditorSelection) => {
744          if (value.selection[0] == -1 && value.selection[1] == -1) {
745            return;
746          }
747          this.start = value.selection[0];
748          this.end = value.selection[1];
749        })
750        .bindSelectionMenu(RichEditorSpanType.TEXT, this.MyMenu3(), RichEditorResponseType.RIGHT_CLICK)
751        .bindSelectionMenu(RichEditorSpanType.TEXT, this.MyMenu2(), RichEditorResponseType.SELECT)
752        .borderWidth(1)
753        .borderColor(Color.Red)
754        .width(200)
755        .height(200)
756    }
757  }
758}
759```
760
761![selectionmenu02](figures/selectionmenu02.jpg)
762