Skip to content

Commit 4317803

Browse files
authored
Merge pull request #36 from FourthDing/main
Angle snapping in control pad editor
2 parents 2b171c4 + da7f13b commit 4317803

10 files changed

Lines changed: 293 additions & 157 deletions

File tree

app/src/androidTest/java/com/github/umer0586/droidpad/ExampleInstrumentedTest.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package com.github.umer0586.droidpad
22

3-
import androidx.test.platform.app.InstrumentationRegistry
43
import androidx.test.ext.junit.runners.AndroidJUnit4
5-
4+
import androidx.test.platform.app.InstrumentationRegistry
5+
import org.junit.Assert.assertEquals
66
import org.junit.Test
77
import org.junit.runner.RunWith
88

9-
import org.junit.Assert.*
10-
119
/**
1210
* Instrumented test, which will execute on an Android device.
1311
*

app/src/main/java/com/github/umer0586/droidpad/ui/screens/controlpadbuilderscreen/ControlPadBuilderScreen.kt

Lines changed: 221 additions & 132 deletions
Large diffs are not rendered by default.

app/src/main/java/com/github/umer0586/droidpad/ui/screens/controlpadbuilderscreen/ControlPadBuilderScreenViewModel.kt

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import kotlinx.coroutines.flow.asStateFlow
4242
import kotlinx.coroutines.flow.update
4343
import kotlinx.coroutines.launch
4444
import javax.inject.Inject
45+
import kotlin.math.roundToInt
4546

4647

4748
data class ControlPadBuilderScreenState(
@@ -50,8 +51,10 @@ data class ControlPadBuilderScreenState(
5051
val showItemEditor: Boolean = false,
5152
val itemToBeEdited: ControlPadItem? = null,
5253
val transformableStatesMap: SnapshotStateMap<Long, TransformableState> = SnapshotStateMap(),
53-
val isModified: Boolean = false
54-
)
54+
val isModified: Boolean = false,
55+
val useAngleSnap: Boolean = false,
56+
val angleSnapDivision:Int = 36,
57+
)
5558

5659
sealed interface ControlPadBuilderScreenEvent {
5760
data object OnAddItemClick : ControlPadBuilderScreenEvent
@@ -65,6 +68,8 @@ sealed interface ControlPadBuilderScreenEvent {
6568
data object OnBackPress: ControlPadBuilderScreenEvent
6669
data class OnResolutionReported(val controlPad: ControlPad, val builderScreenResolution: Resolution, val tempOpen : Boolean = false) : ControlPadBuilderScreenEvent
6770
data object OnTempOpenCompleted : ControlPadBuilderScreenEvent
71+
data object OnUseAngleSnapChange: ControlPadBuilderScreenEvent
72+
data class OnAngleSnapChange(val newValue:Float) : ControlPadBuilderScreenEvent
6873

6974
}
7075

@@ -91,6 +96,13 @@ class ControlPadBuilderScreenViewModel @Inject constructor(
9196
Log.d(tag,"onCleared() ${hashCode()}")
9297
}
9398

99+
private fun snappedRotation(input: Float): Float {
100+
return input.div(360)
101+
.times(_uiState.value.angleSnapDivision)// Scale the input into [-angleSnapDivision,angleSnapDivision]
102+
.roundToInt()// Snap!
103+
.times(360).toFloat()
104+
.div(_uiState.value.angleSnapDivision)// Scale back to normal and convert
105+
}
94106
fun loadControlPadItemsFor(controlPad: ControlPad){
95107
_uiState.update {
96108
it.copy(isModified = false)
@@ -101,6 +113,8 @@ class ControlPadBuilderScreenViewModel @Inject constructor(
101113
Log.d(tag, items.toString())
102114
_uiState.value.controlPadItems.clear()
103115
items.forEach { item ->
116+
var temporalRotation: Float =item.rotation// I have little understanding on kotlin, thanks closure isolation
117+
104118
uiState.value.controlPadItems.add(item)
105119
uiState.value.transformableStatesMap[item.id] =
106120
TransformableState { zoomChange, offsetChange, rotationChange ->
@@ -110,15 +124,29 @@ class ControlPadBuilderScreenViewModel @Inject constructor(
110124
val controlPadItem = uiState.value.controlPadItems[index]
111125

112126
val newScale = controlPadItem.scale * zoomChange
113-
val newRotation = controlPadItem.rotation + rotationChange
114-
val newOffset = controlPadItem.offset + offsetChange.rotateBy(newRotation) * newScale
127+
128+
val newRotation = if(controlPadItem.itemType == ItemType.JOYSTICK || controlPadItem.itemType == ItemType.STEERING_WHEEL) 0f
129+
else if (_uiState.value.useAngleSnap){ temporalRotation + rotationChange }
130+
else{ controlPadItem.rotation + rotationChange }
131+
132+
temporalRotation = newRotation
133+
134+
val snappedNewRotation = snappedRotation(newRotation)
135+
136+
val newOffset = controlPadItem.offset + offsetChange.rotateBy(
137+
if (rotationChange == 0f) controlPadItem.rotation
138+
else if (_uiState.value.useAngleSnap) snappedNewRotation
139+
else newRotation
140+
) * newScale
115141

116142
uiState.value.controlPadItems[index] =
117143
controlPadItem.copy(
118144
offsetX = newOffset.x,
119145
offsetY = newOffset.y,
120146
// Joystick and steering wheel should not be rotatable
121-
rotation = if(controlPadItem.itemType == ItemType.JOYSTICK || controlPadItem.itemType == ItemType.STEERING_WHEEL) 0f else newRotation,
147+
rotation = if (rotationChange == 0f) controlPadItem.rotation
148+
else if (_uiState.value.useAngleSnap) snappedNewRotation
149+
else newRotation,
122150
scale = newScale.coerceIn(minScale,maxScale)
123151
)
124152

@@ -198,29 +226,41 @@ class ControlPadBuilderScreenViewModel @Inject constructor(
198226
properties = event.properties
199227
)
200228
viewModelScope.launch {
229+
var temporalRotation: Float =newItem.rotation
201230
controlPadItemRepository.save(newItem).also { newId ->
202231
controlPadItemRepository.getById(newId).also { newItem ->
203232

204233
uiState.value.controlPadItems.add(newItem!!)
205234

206-
207235
uiState.value.transformableStatesMap[newItem.id] =
208236
TransformableState { zoomChange, offsetChange, rotationChange ->
209-
210237
val index = uiState.value.controlPadItems.indexOfFirst { it.id == newItem.id }
211-
212238
val controlPadItem = uiState.value.controlPadItems[index]
213239

214240
val newScale = controlPadItem.scale * zoomChange
215-
val newRotation = controlPadItem.rotation + rotationChange
216-
val newOffset = controlPadItem.offset + offsetChange.rotateBy(newRotation) * newScale
241+
242+
val newRotation = if(controlPadItem.itemType == ItemType.JOYSTICK || controlPadItem.itemType == ItemType.STEERING_WHEEL) 0f
243+
else if (_uiState.value.useAngleSnap){ temporalRotation + rotationChange }
244+
else{ controlPadItem.rotation + rotationChange }
245+
246+
temporalRotation = newRotation
247+
248+
val snappedNewRotation = snappedRotation(newRotation)
249+
250+
val newOffset = controlPadItem.offset + offsetChange.rotateBy(
251+
if (rotationChange == 0f) controlPadItem.rotation
252+
else if (_uiState.value.useAngleSnap) snappedNewRotation
253+
else newRotation
254+
) * newScale
217255

218256
uiState.value.controlPadItems[index] =
219257
controlPadItem.copy(
220258
offsetX = newOffset.x,
221259
offsetY = newOffset.y,
222260
// Joystick and steering wheel should not be rotatable
223-
rotation = if(controlPadItem.itemType == ItemType.JOYSTICK || controlPadItem.itemType == ItemType.STEERING_WHEEL) 0f else newRotation,
261+
rotation = if (rotationChange == 0f) controlPadItem.rotation
262+
else if (_uiState.value.useAngleSnap) snappedNewRotation
263+
else newRotation,
224264
scale = newScale.coerceIn(minScale,maxScale)
225265
)
226266

@@ -260,6 +300,18 @@ class ControlPadBuilderScreenViewModel @Inject constructor(
260300

261301
}
262302

303+
is ControlPadBuilderScreenEvent.OnUseAngleSnapChange ->{
304+
_uiState.update {
305+
it.copy(useAngleSnap = !it.useAngleSnap)
306+
}
307+
}
308+
is ControlPadBuilderScreenEvent.OnAngleSnapChange -> {
309+
_uiState.update {
310+
it.copy(angleSnapDivision = event.newValue.toInt())
311+
}
312+
}
313+
314+
263315
ControlPadBuilderScreenEvent.OnBackPress -> {}
264316
ControlPadBuilderScreenEvent.OnTempOpenCompleted -> {}
265317
}

app/src/test/java/com/github/umer0586/droidpad/MigrationTest.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import org.junit.Rule
1414
import org.junit.Test
1515
import org.junit.runner.RunWith
1616
import org.robolectric.RobolectricTestRunner
17-
import kotlin.jvm.Throws
1817

1918
@RunWith(RobolectricTestRunner::class)
2019
class MigrationTest {

app/src/test/java/com/github/umer0586/droidpad/connectiontests/WebsocketConnectionTest.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package com.github.umer0586.droidpad.connectiontests
33
import app.cash.turbine.test
44
import com.github.umer0586.droidpad.MainDispatcherRule
55
import com.github.umer0586.droidpad.data.connection.ConnectionState
6-
import com.github.umer0586.droidpad.data.connectionconfig.WebsocketConfig
76
import com.github.umer0586.droidpad.data.connection.WebsocketConnection
7+
import com.github.umer0586.droidpad.data.connectionconfig.WebsocketConfig
88
import kotlinx.coroutines.CompletableDeferred
99
import kotlinx.coroutines.launch
1010
import kotlinx.coroutines.test.runTest
@@ -17,7 +17,6 @@ import org.junit.Rule
1717
import org.junit.Test
1818
import org.junit.runner.RunWith
1919
import org.junit.runners.JUnit4
20-
import java.lang.Exception
2120
import java.net.InetSocketAddress
2221

2322
@Ignore("This Tests times out with 'gradlew test' but works fine when run manually")

app/src/test/java/com/github/umer0586/droidpad/daotests/ConnectionConfigDaoTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package com.github.umer0586.droidpad.daotests
22

33
import androidx.room.Room
44
import androidx.test.core.app.ApplicationProvider
5-
import com.github.umer0586.droidpad.data.database.AppDatabase
65
import com.github.umer0586.droidpad.MainDispatcherRule
6+
import com.github.umer0586.droidpad.data.database.AppDatabase
77
import com.github.umer0586.droidpad.data.database.dao.ConnectionConfigurationDao
88
import com.github.umer0586.droidpad.data.database.dao.ControlPadDao
99
import com.github.umer0586.droidpad.data.database.entities.ConnectionConfig

app/src/test/java/com/github/umer0586/droidpad/daotests/ControlPadDaoTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package com.github.umer0586.droidpad.daotests
22

33
import androidx.room.Room
44
import androidx.test.core.app.ApplicationProvider
5-
import com.github.umer0586.droidpad.data.database.AppDatabase
65
import com.github.umer0586.droidpad.MainDispatcherRule
6+
import com.github.umer0586.droidpad.data.database.AppDatabase
77
import com.github.umer0586.droidpad.data.database.dao.ControlPadDao
88
import com.github.umer0586.droidpad.data.database.dao.ControlPadItemDao
99
import com.github.umer0586.droidpad.data.database.entities.ControlPad

app/src/test/java/com/github/umer0586/droidpad/daotests/ControlPadItemDaoTest.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@ package com.github.umer0586.droidpad.daotests
22

33
import androidx.room.Room
44
import androidx.test.core.app.ApplicationProvider
5-
import com.github.umer0586.droidpad.data.database.AppDatabase
65
import com.github.umer0586.droidpad.MainDispatcherRule
6+
import com.github.umer0586.droidpad.data.database.AppDatabase
77
import com.github.umer0586.droidpad.data.database.dao.ControlPadDao
88
import com.github.umer0586.droidpad.data.database.dao.ControlPadItemDao
99
import com.github.umer0586.droidpad.data.database.entities.ControlPad
1010
import com.github.umer0586.droidpad.data.database.entities.ControlPadItem
1111
import com.github.umer0586.droidpad.data.database.entities.ItemType
1212
import com.github.umer0586.droidpad.data.database.entities.Orientation
13-
1413
import kotlinx.coroutines.test.runTest
1514
import org.junit.After
1615
import org.junit.Assert.assertEquals

app/src/test/java/com/github/umer0586/droidpad/repositorytests/ControlPadRepositoryImpTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package com.github.umer0586.droidpad.repositorytests
22

33
import androidx.room.Room
44
import androidx.test.core.app.ApplicationProvider
5-
import com.github.umer0586.droidpad.data.database.AppDatabase
65
import com.github.umer0586.droidpad.MainDispatcherRule
6+
import com.github.umer0586.droidpad.data.database.AppDatabase
77
import com.github.umer0586.droidpad.data.database.entities.ControlPad
88
import com.github.umer0586.droidpad.data.database.entities.ControlPadItem
99
import com.github.umer0586.droidpad.data.database.entities.ItemType

app/src/test/java/com/github/umer0586/droidpad/uitests/ItemEditorTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import androidx.compose.ui.test.junit4.createComposeRule
77
import androidx.compose.ui.test.onNodeWithTag
88
import androidx.compose.ui.test.performClick
99
import androidx.compose.ui.test.performTextReplacement
10-
import com.github.umer0586.droidpad.data.database.entities.ControlPadItem
11-
import com.github.umer0586.droidpad.data.database.entities.ItemType
1210
import com.github.umer0586.droidpad.data.ButtonProperties
1311
import com.github.umer0586.droidpad.data.LabelProperties
1412
import com.github.umer0586.droidpad.data.SliderProperties
13+
import com.github.umer0586.droidpad.data.database.entities.ControlPadItem
14+
import com.github.umer0586.droidpad.data.database.entities.ItemType
1515
import com.github.umer0586.droidpad.ui.components.propertieseditor.ItemPropertiesEditorSheet
1616
import org.junit.Assert.assertEquals
1717
import org.junit.Assert.fail

0 commit comments

Comments
 (0)