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