@@ -137,45 +137,41 @@ private val TextFieldSelectionManager.textManager: TextManager get() = object :
137137
138138 val isPassword get() = visualTransformation is PasswordVisualTransformation
139139
140- override val cut: (() -> Unit )? get() =
141- if (! value.selection.collapsed && editable && ! isPassword) {
142- {
140+ override val cut: TextContextMenu .Action get() =
141+ TextContextMenu .Action (
142+ enabled = ! value.selection.collapsed && editable && ! isPassword,
143+ execute = {
143144 cut()
144145 focusRequester?.requestFocus()
145146 }
146- } else {
147- null
148- }
147+ )
149148
150- override val copy: (() -> Unit )? get() =
151- if (! value.selection.collapsed && ! isPassword) {
152- {
149+ override val copy: TextContextMenu .Action get() =
150+ TextContextMenu .Action (
151+ enabled = ! value.selection.collapsed && ! isPassword,
152+ execute = {
153153 copy(false )
154154 focusRequester?.requestFocus()
155155 }
156- } else {
157- null
158- }
156+ )
159157
160- override val paste: (() -> Unit )? get() =
161- if (editable && clipboard?.nativeClipboardHasText() == true ) {
162- {
158+ override val paste: TextContextMenu .Action get() =
159+ TextContextMenu .Action (
160+ enabled = editable && clipboard?.nativeClipboardHasText() == true ,
161+ execute = {
163162 paste()
164163 focusRequester?.requestFocus()
165164 }
166- } else {
167- null
168- }
165+ )
169166
170- override val selectAll: (() -> Unit )? get() =
171- if (value.selection.length != value.text.length) {
172- {
167+ override val selectAll: TextContextMenu .Action get() =
168+ TextContextMenu .Action (
169+ enabled = value.selection.length != value.text.length,
170+ execute = {
173171 selectAll()
174172 focusRequester?.requestFocus()
175173 }
176- } else {
177- null
178- }
174+ )
179175
180176 override fun selectWordAtPositionIfNotAlreadySelected (offset : Offset ) {
181177 this @textManager.selectWordAtPositionIfNotAlreadySelected(offset)
@@ -198,20 +194,32 @@ private fun TextFieldSelectionState.textManager(coroutineScope: CoroutineScope):
198194
199195 private fun pasteImpl () = launchUndispatched { paste() }
200196
201- override val cut: (() -> Unit )?
202- get() = if (canShowCutMenuItem()) ::cutImpl else null
197+ override val cut: TextContextMenu .Action get() =
198+ TextContextMenu .Action (
199+ enabled = canShowCutMenuItem(),
200+ execute = ::cutImpl
201+ )
203202
204- override val copy: (() -> Unit )?
205- get() = if (canShowCopyMenuItem()) ::copyImpl else null
203+ override val copy: TextContextMenu .Action get() =
204+ TextContextMenu .Action (
205+ enabled = canShowCopyMenuItem(),
206+ execute = ::copyImpl
207+ )
206208
207- override val paste: (() -> Unit )?
208- get() {
209- launchUndispatched { updateClipboardEntry() }
210- return if (canShowPasteMenuItem()) ::pasteImpl else null
211- }
209+ override val paste: TextContextMenu .Action get() =
210+ TextContextMenu .Action (
211+ enabled = run {
212+ launchUndispatched { updateClipboardEntry() }
213+ canShowPasteMenuItem()
214+ },
215+ execute = ::pasteImpl
216+ )
212217
213- override val selectAll: (() -> Unit )?
214- get() = if (canShowSelectAllMenuItem()) ::selectAll else null
218+ override val selectAll: TextContextMenu .Action get() =
219+ TextContextMenu .Action (
220+ enabled = canShowSelectAllMenuItem(),
221+ execute = ::selectAll
222+ )
215223
216224 override fun selectWordAtPositionIfNotAlreadySelected (offset : Offset ) {
217225 if (! textLayoutState.isPositionOnText(offset)) return
@@ -236,7 +244,11 @@ private fun TextFieldSelectionState.textManager(coroutineScope: CoroutineScope):
236244private val SelectionManager .textManager: TextManager get() = object : TextManager {
237245 override val selectedText get() = getSelectedText() ? : AnnotatedString (" " )
238246 override val cut = null
239- override val copy = { copy() }
247+ override val copy get() =
248+ TextContextMenu .Action (
249+ enabled = isNonEmptySelection(),
250+ execute = { copy()}
251+ )
240252 override val paste = null
241253 override val selectAll = null
242254 override fun selectWordAtPositionIfNotAlreadySelected (offset : Offset ) {
@@ -283,22 +295,22 @@ interface TextContextMenu {
283295 /* *
284296 * Action for cutting the selected text to the clipboard. Null if there is no text to cut.
285297 */
286- val cut: (() -> Unit ) ?
298+ val cut: Action ?
287299
288300 /* *
289301 * Action for copy the selected text to the clipboard. Null if there is no text to copy.
290302 */
291- val copy: (() -> Unit ) ?
303+ val copy: Action ?
292304
293305 /* *
294306 * Action for pasting text from the clipboard. Null if there is no text in the clipboard.
295307 */
296- val paste: (() -> Unit ) ?
308+ val paste: Action ?
297309
298310 /* *
299311 * Action for selecting the whole text. Null if the text is already selected.
300312 */
301- val selectAll: (() -> Unit ) ?
313+ val selectAll: Action ?
302314
303315 /* *
304316 * Selects the word at the given [offset], unless the current selection already encompasses
@@ -307,40 +319,56 @@ interface TextContextMenu {
307319 fun selectWordAtPositionIfNotAlreadySelected (offset : Offset )
308320 }
309321
322+ @ExperimentalFoundationApi
323+ class Action (val enabled : Boolean , val execute : () -> Unit )
324+
310325 companion object {
311326 /* *
312327 * [TextContextMenu] that is used by default in Compose.
313328 */
314329 @ExperimentalFoundationApi
315- val Default = object : TextContextMenu {
316- @Composable
317- override fun Area (textManager : TextManager , state : ContextMenuState , content : @Composable () -> Unit ) {
318- val localization = LocalLocalization .current
319- val items = {
320- listOfNotNull(
321- textManager.cut?.let {
322- ContextMenuItem (localization.cut, it)
323- },
324- textManager.copy?.let {
325- ContextMenuItem (localization.copy, it)
326- },
327- textManager.paste?.let {
328- ContextMenuItem (localization.paste, it)
329- },
330- textManager.selectAll?.let {
331- ContextMenuItem (localization.selectAll, it)
332- },
333- )
334- }
335-
336- TextContextMenuArea (
337- textManager = textManager,
338- items = items,
339- state = state,
340- content = content
341- )
330+ val Default : TextContextMenu = BasicTextContextMenu (showDisabledItems = true )
331+
332+ /* *
333+ * [TextContextMenu] that doesn't show any disabled items.
334+ */
335+ @ExperimentalFoundationApi
336+ val HideDisabledMenuItems : TextContextMenu = BasicTextContextMenu (showDisabledItems = false )
337+ }
338+ }
339+
340+ /* *
341+ * Basic implementation of [TextContextMenu].
342+ */
343+ private class BasicTextContextMenu (
344+ val showDisabledItems : Boolean
345+ ) : TextContextMenu {
346+ @Composable
347+ override fun Area (
348+ textManager : TextManager ,
349+ state : ContextMenuState ,
350+ content : @Composable () -> Unit
351+ ) {
352+ val localization = LocalLocalization .current
353+ val items = {
354+ listOf (
355+ localization.cut to textManager.cut,
356+ localization.copy to textManager.copy,
357+ localization.paste to textManager.paste,
358+ localization.selectAll to textManager.selectAll,
359+ ).mapNotNull { (localization, action) ->
360+ if (action == null ) return @mapNotNull null
361+ if (! showDisabledItems && ! action.enabled) return @mapNotNull null
362+ ContextMenuItem (localization, action.enabled, action.execute)
342363 }
343364 }
365+
366+ TextContextMenuArea (
367+ textManager = textManager,
368+ items = items,
369+ state = state,
370+ content = content
371+ )
344372 }
345373}
346374
0 commit comments