1 /*
<lambda>null2  * 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
18 
19 import androidx.compose.ui.input.key.Key
20 import androidx.compose.ui.input.key.KeyEvent
21 import androidx.compose.ui.input.key.isAltPressed
22 import androidx.compose.ui.input.key.isCtrlPressed
23 import androidx.compose.ui.input.key.isShiftPressed
24 import androidx.compose.ui.input.key.key
25 
26 internal interface KeyMapping {
27     fun map(event: KeyEvent): KeyCommand?
28 }
29 
30 // each platform can define its own key mapping, on Android its just defaultKeyMapping, but on
31 // desktop, the value depends on the current OS
32 internal expect val platformDefaultKeyMapping: KeyMapping
33 
34 /** Copied from [Key] as the constants there are experimental */
35 internal expect object MappedKeys {
36     val A: Key
37     val C: Key
38     val H: Key
39     val V: Key
40     val X: Key
41     val Y: Key
42     val Z: Key
43     val Backslash: Key
44     val DirectionLeft: Key
45     val DirectionRight: Key
46     val DirectionUp: Key
47     val DirectionDown: Key
48     val PageUp: Key
49     val PageDown: Key
50     val MoveHome: Key
51     val MoveEnd: Key
52     val Insert: Key
53     val Enter: Key
54     val NumPadEnter: Key
55     val Backspace: Key
56     val Delete: Key
57     val Paste: Key
58     val Cut: Key
59     val Copy: Key
60     val Tab: Key
61 }
62 
63 // It's common for all platforms key mapping
commonKeyMappingnull64 internal fun commonKeyMapping(shortcutModifier: (KeyEvent) -> Boolean): KeyMapping {
65     return object : KeyMapping {
66         override fun map(event: KeyEvent): KeyCommand? {
67             return when {
68                 shortcutModifier(event) && event.isShiftPressed ->
69                     when (event.key) {
70                         MappedKeys.Z -> KeyCommand.REDO
71                         else -> null
72                     }
73                 shortcutModifier(event) ->
74                     when (event.key) {
75                         MappedKeys.C,
76                         MappedKeys.Insert -> KeyCommand.COPY
77                         MappedKeys.V -> KeyCommand.PASTE
78                         MappedKeys.X -> KeyCommand.CUT
79                         MappedKeys.A -> KeyCommand.SELECT_ALL
80                         MappedKeys.Y -> KeyCommand.REDO
81                         MappedKeys.Z -> KeyCommand.UNDO
82                         else -> null
83                     }
84                 event.isCtrlPressed -> null
85                 event.isShiftPressed ->
86                     when (event.key) {
87                         MappedKeys.DirectionLeft -> KeyCommand.SELECT_LEFT_CHAR
88                         MappedKeys.DirectionRight -> KeyCommand.SELECT_RIGHT_CHAR
89                         MappedKeys.DirectionUp -> KeyCommand.SELECT_UP
90                         MappedKeys.DirectionDown -> KeyCommand.SELECT_DOWN
91                         MappedKeys.PageUp -> KeyCommand.SELECT_PAGE_UP
92                         MappedKeys.PageDown -> KeyCommand.SELECT_PAGE_DOWN
93                         MappedKeys.MoveHome -> KeyCommand.SELECT_LINE_START
94                         MappedKeys.MoveEnd -> KeyCommand.SELECT_LINE_END
95                         MappedKeys.Insert -> KeyCommand.PASTE
96                         else -> null
97                     }
98                 else ->
99                     when (event.key) {
100                         MappedKeys.DirectionLeft -> KeyCommand.LEFT_CHAR
101                         MappedKeys.DirectionRight -> KeyCommand.RIGHT_CHAR
102                         MappedKeys.DirectionUp -> KeyCommand.UP
103                         MappedKeys.DirectionDown -> KeyCommand.DOWN
104                         MappedKeys.PageUp -> KeyCommand.PAGE_UP
105                         MappedKeys.PageDown -> KeyCommand.PAGE_DOWN
106                         MappedKeys.MoveHome -> KeyCommand.LINE_START
107                         MappedKeys.MoveEnd -> KeyCommand.LINE_END
108                         MappedKeys.Enter,
109                         MappedKeys.NumPadEnter -> KeyCommand.NEW_LINE
110                         MappedKeys.Backspace -> KeyCommand.DELETE_PREV_CHAR
111                         MappedKeys.Delete -> KeyCommand.DELETE_NEXT_CHAR
112                         MappedKeys.Paste -> KeyCommand.PASTE
113                         MappedKeys.Cut -> KeyCommand.CUT
114                         MappedKeys.Copy -> KeyCommand.COPY
115                         MappedKeys.Tab -> KeyCommand.TAB
116                         else -> null
117                     }
118             }
119         }
120     }
121 }
122 
123 // It's "default" or actually "non macOS" key mapping
124 internal val defaultKeyMapping: KeyMapping =
commonnull125     commonKeyMapping(KeyEvent::isCtrlPressed).let { common ->
126         object : KeyMapping {
127             override fun map(event: KeyEvent): KeyCommand? {
128                 return when {
129                     event.isShiftPressed && event.isCtrlPressed ->
130                         when (event.key) {
131                             MappedKeys.DirectionLeft -> KeyCommand.SELECT_LEFT_WORD
132                             MappedKeys.DirectionRight -> KeyCommand.SELECT_RIGHT_WORD
133                             MappedKeys.DirectionUp -> KeyCommand.SELECT_PREV_PARAGRAPH
134                             MappedKeys.DirectionDown -> KeyCommand.SELECT_NEXT_PARAGRAPH
135                             else -> null
136                         }
137                     event.isCtrlPressed ->
138                         when (event.key) {
139                             MappedKeys.DirectionLeft -> KeyCommand.LEFT_WORD
140                             MappedKeys.DirectionRight -> KeyCommand.RIGHT_WORD
141                             MappedKeys.DirectionUp -> KeyCommand.PREV_PARAGRAPH
142                             MappedKeys.DirectionDown -> KeyCommand.NEXT_PARAGRAPH
143                             MappedKeys.H -> KeyCommand.DELETE_PREV_CHAR
144                             MappedKeys.Delete -> KeyCommand.DELETE_NEXT_WORD
145                             MappedKeys.Backspace -> KeyCommand.DELETE_PREV_WORD
146                             MappedKeys.Backslash -> KeyCommand.DESELECT
147                             else -> null
148                         }
149                     event.isShiftPressed ->
150                         when (event.key) {
151                             MappedKeys.MoveHome -> KeyCommand.SELECT_LINE_START
152                             MappedKeys.MoveEnd -> KeyCommand.SELECT_LINE_END
153                             else -> null
154                         }
155                     event.isAltPressed ->
156                         when (event.key) {
157                             MappedKeys.Backspace -> KeyCommand.DELETE_FROM_LINE_START
158                             MappedKeys.Delete -> KeyCommand.DELETE_TO_LINE_END
159                             else -> null
160                         }
161                     else -> null
162                 } ?: common.map(event)
163             }
164         }
165     }
166