Skip to content

Commit 05e63e2

Browse files
eymarOleksandr Karpovich
andcommitted
Fix selection with end home (#279)
* fix selection using End and Home buttons JetBrains/compose-multiplatform#1479 * make different behaviour for selection with Home and End for different platforms Co-authored-by: Oleksandr Karpovich <oleksandr.karpovich@jetbrains.com>
1 parent ef1c900 commit 05e63e2

File tree

3 files changed

+164
-4
lines changed

3 files changed

+164
-4
lines changed

compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/KeyMapping.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,12 @@ internal val defaultKeyMapping: KeyMapping =
144144
}
145145
event.isShiftPressed ->
146146
when (event.key) {
147-
MappedKeys.MoveHome -> KeyCommand.SELECT_HOME
148-
MappedKeys.MoveEnd -> KeyCommand.SELECT_END
147+
MappedKeys.MoveHome -> KeyCommand.SELECT_LINE_START
148+
MappedKeys.MoveEnd -> KeyCommand.SELECT_LINE_END
149149
else -> null
150150
}
151151
else -> null
152152
} ?: common.map(event)
153153
}
154154
}
155-
}
155+
}

compose/foundation/foundation/src/desktopMain/kotlin/androidx/compose/foundation/text/KeyMapping.desktop.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ import androidx.compose.ui.input.key.key
2727
import java.awt.event.KeyEvent as AwtKeyEvent
2828

2929
internal actual val platformDefaultKeyMapping: KeyMapping =
30-
when (DesktopPlatform.Current) {
30+
createPlatformDefaultKeyMapping(DesktopPlatform.Current)
31+
32+
internal fun createPlatformDefaultKeyMapping(platform: DesktopPlatform): KeyMapping {
33+
return when (platform) {
3134
DesktopPlatform.MacOS -> {
3235
val common = commonKeyMapping(KeyEvent::isMetaPressed)
3336
object : KeyMapping {
@@ -132,6 +135,7 @@ internal actual val platformDefaultKeyMapping: KeyMapping =
132135

133136
else -> defaultKeyMapping
134137
}
138+
}
135139

136140
internal actual object MappedKeys {
137141
actual val A: Key = Key(AwtKeyEvent.VK_A)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright 2022 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.foundation.DesktopPlatform
20+
import androidx.compose.foundation.text.BasicTextField
21+
import androidx.compose.foundation.text.KeyMapping
22+
import androidx.compose.foundation.text.createPlatformDefaultKeyMapping
23+
import androidx.compose.foundation.text.platformDefaultKeyMapping
24+
import androidx.compose.runtime.mutableStateOf
25+
import androidx.compose.ui.ExperimentalComposeUiApi
26+
import androidx.compose.ui.Modifier
27+
import androidx.compose.ui.geometry.Offset
28+
import androidx.compose.ui.input.key.Key
29+
import androidx.compose.ui.platform.testTag
30+
import androidx.compose.ui.test.ExperimentalTestApi
31+
import androidx.compose.ui.test.assertIsFocused
32+
import androidx.compose.ui.test.click
33+
import androidx.compose.ui.test.junit4.createComposeRule
34+
import androidx.compose.ui.test.onNodeWithTag
35+
import androidx.compose.ui.test.performKeyInput
36+
import androidx.compose.ui.test.performMouseInput
37+
import androidx.compose.ui.test.pressKey
38+
import androidx.compose.ui.text.TextRange
39+
import androidx.compose.ui.text.input.TextFieldValue
40+
import com.google.common.truth.Truth
41+
import kotlinx.coroutines.runBlocking
42+
import org.junit.After
43+
import org.junit.Rule
44+
import org.junit.Test
45+
46+
class SelectionTests {
47+
48+
@get:Rule
49+
val rule = createComposeRule()
50+
51+
private val originalPlatformKeyMapping = platformDefaultKeyMapping
52+
53+
@After
54+
fun restoreRealDesktopPlatform() {
55+
setPlatformDefaultKeyMapping(originalPlatformKeyMapping)
56+
}
57+
58+
private fun setPlatformDefaultKeyMapping(value: KeyMapping) {
59+
val field = Class.forName("androidx.compose.foundation.text.KeyMapping_desktopKt")
60+
.getDeclaredField("platformDefaultKeyMapping")
61+
field.isAccessible = true
62+
val modifiersField = java.lang.reflect.Field::class.java.getDeclaredField("modifiers")
63+
modifiersField.isAccessible = true
64+
modifiersField.setInt(field, field.modifiers and java.lang.reflect.Modifier.FINAL.inv())
65+
field.set(null, value)
66+
}
67+
68+
@OptIn(ExperimentalTestApi::class, ExperimentalComposeUiApi::class)
69+
@Test
70+
fun `select using Shift_End and Shift_Home combinations with DesktopPlatform-Windows`() = runBlocking {
71+
setPlatformDefaultKeyMapping(createPlatformDefaultKeyMapping(DesktopPlatform.Windows))
72+
val state = mutableStateOf(TextFieldValue("line 1\nline 2\nline 3\nline 4\nline 5"))
73+
74+
75+
rule.setContent {
76+
BasicTextField(
77+
value = state.value,
78+
onValueChange = { state.value = it },
79+
modifier = Modifier.testTag("textField")
80+
)
81+
}
82+
rule.awaitIdle()
83+
rule.onNodeWithTag("textField").performMouseInput {
84+
click(Offset(0f, 0f))
85+
}
86+
rule.awaitIdle()
87+
rule.onNodeWithTag("textField").assertIsFocused()
88+
Truth.assertThat(state.value.selection).isEqualTo(TextRange(0, 0))
89+
90+
rule.onNodeWithTag("textField").performKeyInput {
91+
pressKey(Key.DirectionRight)
92+
}
93+
rule.awaitIdle()
94+
Truth.assertThat(state.value.selection).isEqualTo(TextRange(1, 1))
95+
96+
rule.onNodeWithTag("textField").performKeyInput {
97+
keyDown(Key.ShiftLeft)
98+
pressKey(Key.MoveEnd)
99+
keyUp(Key.ShiftLeft)
100+
}
101+
rule.awaitIdle()
102+
Truth.assertThat(state.value.selection).isEqualTo(TextRange(1, 6))
103+
104+
rule.onNodeWithTag("textField").performKeyInput {
105+
keyDown(Key.ShiftLeft)
106+
pressKey(Key.MoveHome)
107+
keyUp(Key.ShiftLeft)
108+
}
109+
rule.awaitIdle()
110+
Truth.assertThat(state.value.selection).isEqualTo(TextRange(1, 0))
111+
}
112+
113+
@OptIn(ExperimentalTestApi::class, ExperimentalComposeUiApi::class)
114+
@Test
115+
fun `select using Shift_End and Shift_Home combinations with DesktopPlatform-MacOs`() = runBlocking {
116+
setPlatformDefaultKeyMapping(createPlatformDefaultKeyMapping(DesktopPlatform.MacOS))
117+
val state = mutableStateOf(TextFieldValue("line 1\nline 2\nline 3\nline 4\nline 5"))
118+
119+
rule.setContent {
120+
BasicTextField(
121+
value = state.value,
122+
onValueChange = { state.value = it },
123+
modifier = Modifier.testTag("textField")
124+
)
125+
}
126+
rule.awaitIdle()
127+
rule.onNodeWithTag("textField").performMouseInput {
128+
click(Offset(0f, 0f))
129+
}
130+
rule.awaitIdle()
131+
rule.onNodeWithTag("textField").assertIsFocused()
132+
Truth.assertThat(state.value.selection).isEqualTo(TextRange(0, 0))
133+
134+
rule.onNodeWithTag("textField").performKeyInput {
135+
pressKey(Key.DirectionRight)
136+
}
137+
rule.awaitIdle()
138+
Truth.assertThat(state.value.selection).isEqualTo(TextRange(1, 1))
139+
140+
rule.onNodeWithTag("textField").performKeyInput {
141+
keyDown(Key.ShiftLeft)
142+
pressKey(Key.MoveEnd)
143+
keyUp(Key.ShiftLeft)
144+
}
145+
rule.awaitIdle()
146+
Truth.assertThat(state.value.selection).isEqualTo(TextRange(1, 34))
147+
148+
rule.onNodeWithTag("textField").performKeyInput {
149+
keyDown(Key.ShiftLeft)
150+
pressKey(Key.MoveHome)
151+
keyUp(Key.ShiftLeft)
152+
}
153+
rule.awaitIdle()
154+
Truth.assertThat(state.value.selection).isEqualTo(TextRange(1, 0))
155+
}
156+
}

0 commit comments

Comments
 (0)