1 /*
2  * Copyright 2024 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.contextmenu
18 
19 import androidx.compose.foundation.contextmenu.ContextMenuState.Status
20 import androidx.compose.foundation.internal.checkPrecondition
21 import androidx.compose.runtime.getValue
22 import androidx.compose.runtime.mutableStateOf
23 import androidx.compose.runtime.setValue
24 import androidx.compose.ui.geometry.Offset
25 import androidx.compose.ui.geometry.isSpecified
26 
27 private const val UNSPECIFIED_OFFSET_ERROR_MESSAGE =
28     "ContextMenuState.Status should never be open with an unspecified offset. " +
29         "Use ContextMenuState.Status.Closed instead."
30 
31 /** Holds state related to the context menu. */
32 internal class ContextMenuState internal constructor(initialStatus: Status = Status.Closed) {
33     var status by mutableStateOf(initialStatus)
34 
toStringnull35     override fun toString(): String = "ContextMenuState(status=$status)"
36 
37     override fun hashCode(): Int = status.hashCode()
38 
39     override fun equals(other: Any?): Boolean {
40         if (other === this) return true
41         if (other !is ContextMenuState) return false
42         return other.status == this.status
43     }
44 
45     /** The status of the context menu. Can be [Open] or [Closed]. */
46     sealed class Status {
47         /** An open context menu [Status]. */
48         class Open(
49             /** The offset to open the menu at. It must be specified. */
50             val offset: Offset
51         ) : Status() {
52             init {
<lambda>null53                 checkPrecondition(offset.isSpecified) { UNSPECIFIED_OFFSET_ERROR_MESSAGE }
54             }
55 
toStringnull56             override fun toString(): String = "Open(offset=$offset)"
57 
58             override fun hashCode(): Int = offset.hashCode()
59 
60             override fun equals(other: Any?): Boolean {
61                 if (other === this) return true
62                 if (other !is Open) return false
63                 return this.offset == other.offset
64             }
65         }
66 
67         /** A closed context menu [Status]. */
68         object Closed : Status() {
toStringnull69             override fun toString(): String = "Closed"
70         }
71     }
72 }
73 
74 /** Convenience method to set the state's status to [Status.Closed]. */
75 internal fun ContextMenuState.close() {
76     status = Status.Closed
77 }
78