1 /* 2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.compose.foundation.text.selection 18 19 import androidx.compose.runtime.Immutable 20 import androidx.compose.ui.text.TextRange 21 import androidx.compose.ui.text.style.ResolvedTextDirection 22 23 /** Information about the current Selection. */ 24 @Immutable 25 internal data class Selection( 26 /** Information about the start of the selection. */ 27 val start: AnchorInfo, 28 29 /** Information about the end of the selection. */ 30 val end: AnchorInfo, 31 /** 32 * The flag to show that the selection handles are dragged across each other. After selection is 33 * initialized, if user drags one handle to cross the other handle, this is true, otherwise it's 34 * false. 35 */ 36 // If selection happens in single widget, checking [TextRange.start] > [TextRange.end] is 37 // enough. 38 // But when selection happens across multiple widgets, this value needs more complicated 39 // calculation. To avoid repeated calculation, making it as a flag is cheaper. 40 val handlesCrossed: Boolean = false 41 ) { 42 /** Contains information about an anchor (start/end) of selection. */ 43 @Immutable 44 internal data class AnchorInfo( 45 /** Text direction of the character in selection edge. */ 46 val direction: ResolvedTextDirection, 47 48 /** 49 * Character offset for the selection edge. This offset is within individual child text 50 * composable. 51 */ 52 val offset: Int, 53 54 /** The id of the [Selectable] which contains this [Selection] Anchor. */ 55 val selectableId: Long 56 ) 57 mergenull58 fun merge(other: Selection?): Selection { 59 if (other == null) return this 60 61 val selection = this 62 63 return if (handlesCrossed || other.handlesCrossed) { 64 Selection( 65 start = if (other.handlesCrossed) other.start else other.end, 66 end = if (handlesCrossed) end else start, 67 handlesCrossed = true 68 ) 69 } else { 70 selection.copy(end = other.end) 71 } 72 } 73 74 /** Returns the selection offset information as a [TextRange] */ toTextRangenull75 fun toTextRange(): TextRange { 76 return TextRange(start.offset, end.offset) 77 } 78 } 79