mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-26 11:55:30 +03:00
Implement OSC snap to grid functionality
This commit is contained in:
parent
88084016a1
commit
88e6fc9888
@ -179,8 +179,40 @@ abstract class OnScreenButton(
|
||||
* Moves this button to the given coordinates
|
||||
*/
|
||||
open fun move(x : Float, y : Float) {
|
||||
relativeX = x / width
|
||||
relativeY = (y - heightDiff) / adjustedHeight
|
||||
var adjustedX = x
|
||||
var adjustedY = y
|
||||
|
||||
if (editInfo.snapToGrid) {
|
||||
val centerX = width / 2f
|
||||
val centerY = height / 2f
|
||||
val gridSize = editInfo.gridSize
|
||||
// The coordinates of the first grid line for each axis, because the grid is centered and might not start at [0,0]
|
||||
val startX = centerX % gridSize
|
||||
val startY = centerY % gridSize
|
||||
|
||||
/**
|
||||
* The offset to apply to a coordinate to snap it to the grid is the remainder of
|
||||
* the coordinate divided by the grid size.
|
||||
* Since the grid is centered on the screen and might not start at [0,0] we need to
|
||||
* subtract the grid start offset, otherwise we'd be calculating the offset for a grid that starts at [0,0].
|
||||
*
|
||||
* Example: Touch event X: 158 | Grid size: 50 | Grid start X: 40 -> Grid lines at 40, 90, 140, 190, ...
|
||||
* Snap offset: 158 - 40 = 118 -> 118 % 50 = 18
|
||||
* Apply offset to X: 158 - 18 = 140 which is a grid line
|
||||
*
|
||||
* If we didn't subtract the grid start offset:
|
||||
* Snap offset: 158 % 50 = 8
|
||||
* Apply offset to X: 158 - 8 = 150 which is not a grid line
|
||||
*/
|
||||
val snapOffsetX = (x - startX) % gridSize
|
||||
val snapOffsetY = (y - startY) % gridSize
|
||||
|
||||
adjustedX = x - snapOffsetX
|
||||
adjustedY = y - snapOffsetY
|
||||
}
|
||||
|
||||
relativeX = adjustedX / width
|
||||
relativeY = (adjustedY - heightDiff) / adjustedHeight
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,6 +69,7 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
|
||||
lateinit var vibrator : Vibrator
|
||||
private val effectClick = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
|
||||
|
||||
// Ensure controls init happens after editInfo is initialized so that the buttons have a valid reference to it
|
||||
private val controls = Controls(this)
|
||||
|
||||
override fun onDraw(canvas : Canvas) {
|
||||
@ -263,8 +264,7 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
|
||||
|
||||
fun setEditMode(editMode : EditMode) {
|
||||
editInfo.editMode = editMode
|
||||
setOnTouchListener(if (editMode == EditMode.None) playingTouchHandler else editingTouchHandler)
|
||||
invalidate()
|
||||
setOnTouchListener(if (isEditing) editingTouchHandler else playingTouchHandler )
|
||||
}
|
||||
|
||||
fun resetControls() {
|
||||
@ -286,6 +286,10 @@ class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun setSnapToGrid(snap : Boolean) {
|
||||
editInfo.snapToGrid = snap
|
||||
}
|
||||
|
||||
fun increaseOpacity() {
|
||||
controls.alpha = (controls.alpha + ALPHA_STEP).coerceIn(ALPHA_RANGE)
|
||||
invalidate()
|
||||
|
@ -12,6 +12,7 @@ import android.os.VibratorManager
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isGone
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@ -108,12 +109,26 @@ class OnScreenEditActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private val enableGridAction = {
|
||||
appSettings.onScreenControlSnapToGrid = true
|
||||
binding.onScreenControllerView.setSnapToGrid(true)
|
||||
binding.alignmentGrid.isGone = false
|
||||
}
|
||||
|
||||
private val disableGridAction = {
|
||||
appSettings.onScreenControlSnapToGrid = false
|
||||
binding.onScreenControllerView.setSnapToGrid(false)
|
||||
binding.alignmentGrid.isGone = true
|
||||
}
|
||||
|
||||
private val actions : List<Pair<Int, () -> Unit>> = listOf(
|
||||
Pair(R.drawable.ic_palette, paletteAction),
|
||||
Pair(R.drawable.ic_restore) { binding.onScreenControllerView.resetControls() },
|
||||
Pair(R.drawable.ic_toggle, toggleAction),
|
||||
Pair(R.drawable.ic_move, moveAction),
|
||||
Pair(R.drawable.ic_resize, resizeAction),
|
||||
Pair(R.drawable.ic_grid_on, enableGridAction),
|
||||
Pair(R.drawable.ic_grid_off, disableGridAction),
|
||||
Pair(R.drawable.ic_zoom_out) { binding.onScreenControllerView.decreaseScale() },
|
||||
Pair(R.drawable.ic_zoom_in) { binding.onScreenControllerView.increaseScale() },
|
||||
Pair(R.drawable.ic_opacity_minus) { binding.onScreenControllerView.decreaseOpacity() },
|
||||
@ -147,6 +162,12 @@ class OnScreenEditActivity : AppCompatActivity() {
|
||||
|
||||
binding.onScreenControllerView.recenterSticks = appSettings.onScreenControlRecenterSticks
|
||||
|
||||
val snapToGrid = appSettings.onScreenControlSnapToGrid
|
||||
binding.onScreenControllerView.setSnapToGrid(snapToGrid)
|
||||
|
||||
binding.alignmentGrid.isGone = !snapToGrid
|
||||
binding.alignmentGrid.gridSize = OnScreenEditInfo.GridSize
|
||||
|
||||
actions.forEach { pair ->
|
||||
binding.fabParent.addView(LayoutInflater.from(this).inflate(R.layout.on_screen_edit_mini_fab, binding.fabParent, false).apply {
|
||||
(this as FloatingActionButton).setImageDrawable(ContextCompat.getDrawable(context, pair.first))
|
||||
|
@ -5,6 +5,9 @@
|
||||
|
||||
package emu.skyline.input.onscreen
|
||||
|
||||
import android.util.TypedValue
|
||||
import emu.skyline.SkylineApplication
|
||||
|
||||
enum class EditMode {
|
||||
None,
|
||||
Move,
|
||||
@ -26,5 +29,20 @@ class OnScreenEditInfo {
|
||||
*/
|
||||
var editButton : OnScreenButton? = null
|
||||
|
||||
/**
|
||||
* Whether the buttons should snap to a grid when in edit mode
|
||||
*/
|
||||
var snapToGrid : Boolean = false
|
||||
|
||||
var gridSize : Int = GridSize
|
||||
|
||||
val isEditing get() = editMode != EditMode.None
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* The size of the grid, calculated from the value of 8dp
|
||||
*/
|
||||
val GridSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8f, SkylineApplication.context.resources.displayMetrics).toInt()
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ class AppSettings @Inject constructor(@ApplicationContext private val context :
|
||||
var onScreenControl by sharedPreferences(context, true)
|
||||
var onScreenControlFeedback by sharedPreferences(context, true)
|
||||
var onScreenControlRecenterSticks by sharedPreferences(context, true)
|
||||
var onScreenControlSnapToGrid by sharedPreferences(context, false)
|
||||
|
||||
// Other
|
||||
var romFormatFilter by sharedPreferences(context, 0)
|
||||
|
65
app/src/main/java/emu/skyline/views/AlignmentGridView.kt
Normal file
65
app/src/main/java/emu/skyline/views/AlignmentGridView.kt
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline.views
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
|
||||
/**
|
||||
* A view that draws a grid, used for aligning on-screen controller buttons
|
||||
*/
|
||||
class AlignmentGridView @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = 0) : View(context, attrs, defStyleAttr) {
|
||||
var gridSize = 0
|
||||
set(value) {
|
||||
field = value
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private val gridPaint = Paint().apply {
|
||||
color = Color.WHITE
|
||||
alpha = 135
|
||||
}
|
||||
private val gridCenterPaint = Paint().apply {
|
||||
color = Color.WHITE
|
||||
alpha = 55
|
||||
strokeWidth = 10f
|
||||
}
|
||||
|
||||
override fun onDraw(canvas : Canvas) {
|
||||
super.onDraw(canvas)
|
||||
drawGrid(canvas)
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a centered grid on the given canvas
|
||||
*/
|
||||
private fun drawGrid(canvas : Canvas) {
|
||||
val centerX = width / 2f
|
||||
val centerY = height / 2f
|
||||
val gridSize = gridSize
|
||||
// Compute the coordinates of the first grid line for each axis, because we want a centered grid, which might not start at [0,0]
|
||||
val startX = centerX % gridSize
|
||||
val startY = centerY % gridSize
|
||||
|
||||
// Draw the center lines with a thicker stroke
|
||||
canvas.drawLine(centerX, 0f, centerX, height.toFloat(), gridCenterPaint)
|
||||
canvas.drawLine(0f, centerY, width.toFloat(), centerY, gridCenterPaint)
|
||||
|
||||
// Draw the rest of the grid starting from the start coordinates
|
||||
// Draw vertical lines
|
||||
for (i in 0..width step gridSize) {
|
||||
canvas.drawLine(startX + i, 0f, startX + i, height.toFloat(), gridPaint)
|
||||
}
|
||||
// Draw horizontal lines
|
||||
for (i in 0..height step gridSize) {
|
||||
canvas.drawLine(0f, startY + i, width.toFloat(), startY + i, gridPaint)
|
||||
}
|
||||
}
|
||||
}
|
10
app/src/main/res/drawable/ic_grid_off.xml
Normal file
10
app/src/main/res/drawable/ic_grid_off.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M8,4v1.45l2,2L10,4h4v4h-3.45l2,2L14,10v1.45l2,2L16,10h4v4h-3.45l2,2L20,16v1.45l2,2L22,4c0,-1.1 -0.9,-2 -2,-2L4.55,2l2,2L8,4zM16,4h4v4h-4L16,4zM1.27,1.27L0,2.55l2,2L2,20c0,1.1 0.9,2 2,2h15.46l2,2 1.27,-1.27L1.27,1.27zM10,12.55L11.45,14L10,14v-1.45zM4,6.55L5.45,8L4,8L4,6.55zM8,20L4,20v-4h4v4zM8,14L4,14v-4h3.45l0.55,0.55L8,14zM14,20h-4v-4h3.45l0.55,0.54L14,20zM16,20v-1.46L17.46,20L16,20z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_grid_on.xml
Normal file
10
app/src/main/res/drawable/ic_grid_on.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#000000"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,2L4,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM8,20L4,20v-4h4v4zM8,14L4,14v-4h4v4zM8,8L4,8L4,4h4v4zM14,20h-4v-4h4v4zM14,14h-4v-4h4v4zM14,8h-4L10,4h4v4zM20,20h-4v-4h4v4zM20,14h-4v-4h4v4zM20,8h-4L16,4h4v4z" />
|
||||
</vector>
|
@ -4,6 +4,11 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@android:color/black">
|
||||
|
||||
<emu.skyline.views.AlignmentGridView
|
||||
android:id="@+id/alignment_grid"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<emu.skyline.input.onscreen.OnScreenControllerView
|
||||
android:id="@+id/on_screen_controller_view"
|
||||
android:layout_width="match_parent"
|
||||
|
Loading…
Reference in New Issue
Block a user