3
0
Fork 0
forked from suyu/suyu

android: Convert InputOverlayDrawableJoystick to Kotlin

This commit is contained in:
Charles Lombardo 2023-03-11 00:34:09 -05:00 committed by bunnei
parent 5c8372a566
commit 42b3e72e96
2 changed files with 205 additions and 243 deletions

View file

@ -1,243 +0,0 @@
/**
* Copyright 2013 Dolphin Emulator Project
* Licensed under GPLv2+
* Refer to the license.txt file included.
*/
package org.yuzu.yuzu_emu.overlay;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.view.MotionEvent;
import org.yuzu.yuzu_emu.NativeLibrary;
import org.yuzu.yuzu_emu.NativeLibrary.ButtonType;
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings;
/**
* Custom {@link BitmapDrawable} that is capable
* of storing it's own ID.
*/
public final class InputOverlayDrawableJoystick {
// The ID value what type of joystick this Drawable represents.
private int mJoystickId;
// The ID value what type of button this Drawable represents.
private int mButtonId;
// The ID value what motion event is tracking
private int mTrackId = -1;
private float mXAxis;
private float mYAxis;
private int mControlPositionX, mControlPositionY;
private int mWidth;
private int mHeight;
private Rect mVirtBounds;
private Rect mOrigBounds;
private BitmapDrawable mOuterBitmap;
private BitmapDrawable mDefaultStateInnerBitmap;
private BitmapDrawable mPressedStateInnerBitmap;
private BitmapDrawable mBoundsBoxBitmap;
private boolean mPressedState = false;
/**
* Constructor
*
* @param res {@link Resources} instance.
* @param bitmapOuter {@link Bitmap} which represents the outer non-movable part of the joystick.
* @param bitmapInnerDefault {@link Bitmap} which represents the default inner movable part of the joystick.
* @param bitmapInnerPressed {@link Bitmap} which represents the pressed inner movable part of the joystick.
* @param rectOuter {@link Rect} which represents the outer joystick bounds.
* @param rectInner {@link Rect} which represents the inner joystick bounds.
* @param joystick Identifier for which joystick this is.
*/
public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter,
Bitmap bitmapInnerDefault, Bitmap bitmapInnerPressed,
Rect rectOuter, Rect rectInner, int joystick, int button) {
mJoystickId = joystick;
mButtonId = button;
mOuterBitmap = new BitmapDrawable(res, bitmapOuter);
mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault);
mPressedStateInnerBitmap = new BitmapDrawable(res, bitmapInnerPressed);
mBoundsBoxBitmap = new BitmapDrawable(res, bitmapOuter);
mWidth = bitmapOuter.getWidth();
mHeight = bitmapOuter.getHeight();
setBounds(rectOuter);
mDefaultStateInnerBitmap.setBounds(rectInner);
mPressedStateInnerBitmap.setBounds(rectInner);
mVirtBounds = getBounds();
mOrigBounds = mOuterBitmap.copyBounds();
mBoundsBoxBitmap.setAlpha(0);
mBoundsBoxBitmap.setBounds(getVirtBounds());
SetInnerBounds();
}
public void draw(Canvas canvas) {
mOuterBitmap.draw(canvas);
getCurrentStateBitmapDrawable().draw(canvas);
mBoundsBoxBitmap.draw(canvas);
}
public boolean updateStatus(MotionEvent event) {
int pointerIndex = event.getActionIndex();
int xPosition = (int) event.getX(pointerIndex);
int yPosition = (int) event.getY(pointerIndex);
int pointerId = event.getPointerId(pointerIndex);
int motion_event = event.getAction() & MotionEvent.ACTION_MASK;
boolean isActionDown = motion_event == MotionEvent.ACTION_DOWN || motion_event == MotionEvent.ACTION_POINTER_DOWN;
boolean isActionUp = motion_event == MotionEvent.ACTION_UP || motion_event == MotionEvent.ACTION_POINTER_UP;
if (isActionDown) {
if (!getBounds().contains(xPosition, yPosition)) {
return false;
}
mPressedState = true;
mOuterBitmap.setAlpha(0);
mBoundsBoxBitmap.setAlpha(255);
if (EmulationMenuSettings.getJoystickRelCenter()) {
getVirtBounds().offset(xPosition - getVirtBounds().centerX(),
yPosition - getVirtBounds().centerY());
}
mBoundsBoxBitmap.setBounds(getVirtBounds());
mTrackId = pointerId;
}
if (isActionUp) {
if (mTrackId != pointerId) {
return false;
}
mPressedState = false;
mXAxis = 0.0f;
mYAxis = 0.0f;
mOuterBitmap.setAlpha(255);
mBoundsBoxBitmap.setAlpha(0);
setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right,
mOrigBounds.bottom));
setBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right,
mOrigBounds.bottom));
SetInnerBounds();
mTrackId = -1;
return true;
}
if (mTrackId == -1)
return false;
for (int i = 0; i < event.getPointerCount(); i++) {
if (mTrackId != event.getPointerId(i)) {
continue;
}
float touchX = event.getX(i);
float touchY = event.getY(i);
float maxY = getVirtBounds().bottom;
float maxX = getVirtBounds().right;
touchX -= getVirtBounds().centerX();
maxX -= getVirtBounds().centerX();
touchY -= getVirtBounds().centerY();
maxY -= getVirtBounds().centerY();
final float AxisX = touchX / maxX;
final float AxisY = touchY / maxY;
final float oldXAxis = mXAxis;
final float oldYAxis = mYAxis;
// Clamp the circle pad input to a circle
final float angle = (float) Math.atan2(AxisY, AxisX);
float radius = (float) Math.sqrt(AxisX * AxisX + AxisY * AxisY);
if (radius > 1.0f) {
radius = 1.0f;
}
mXAxis = ((float) Math.cos(angle) * radius);
mYAxis = ((float) Math.sin(angle) * radius);
SetInnerBounds();
return oldXAxis != mXAxis && oldYAxis != mYAxis;
}
return false;
}
private void SetInnerBounds() {
int X = getVirtBounds().centerX() + (int) ((mXAxis) * (getVirtBounds().width() / 2));
int Y = getVirtBounds().centerY() + (int) ((mYAxis) * (getVirtBounds().height() / 2));
if (X > getVirtBounds().centerX() + (getVirtBounds().width() / 2))
X = getVirtBounds().centerX() + (getVirtBounds().width() / 2);
if (X < getVirtBounds().centerX() - (getVirtBounds().width() / 2))
X = getVirtBounds().centerX() - (getVirtBounds().width() / 2);
if (Y > getVirtBounds().centerY() + (getVirtBounds().height() / 2))
Y = getVirtBounds().centerY() + (getVirtBounds().height() / 2);
if (Y < getVirtBounds().centerY() - (getVirtBounds().height() / 2))
Y = getVirtBounds().centerY() - (getVirtBounds().height() / 2);
int width = mPressedStateInnerBitmap.getBounds().width() / 2;
int height = mPressedStateInnerBitmap.getBounds().height() / 2;
mDefaultStateInnerBitmap.setBounds(X - width, Y - height, X + width, Y + height);
mPressedStateInnerBitmap.setBounds(mDefaultStateInnerBitmap.getBounds());
}
public void setPosition(int x, int y) {
mControlPositionX = x;
mControlPositionY = y;
}
private BitmapDrawable getCurrentStateBitmapDrawable() {
return mPressedState ? mPressedStateInnerBitmap : mDefaultStateInnerBitmap;
}
/**
* Gets this InputOverlayDrawableJoystick's button ID.
*
* @return this InputOverlayDrawableJoystick's button ID.
*/
public int getJoystickId() {
return mJoystickId;
}
public float getXAxis() {
return mXAxis;
}
public float getYAxis() {
// Nintendo joysticks have y axis inverted
return -mYAxis;
}
public int getButtonId() {
return mButtonId;
}
public int getTrackId() {
return mTrackId;
}
public int getButtonStatus() {
// TODO: Add button support
return NativeLibrary.ButtonState.RELEASED;
}
public Rect getBounds() {
return mOuterBitmap.getBounds();
}
public void setBounds(Rect bounds) {
mOuterBitmap.setBounds(bounds);
}
private Rect getVirtBounds() {
return mVirtBounds;
}
private void setVirtBounds(Rect bounds) {
mVirtBounds = bounds;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
}

View file

@ -0,0 +1,205 @@
package org.yuzu.yuzu_emu.overlay
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
/**
* Custom [BitmapDrawable] that is capable
* of storing it's own ID.
*
* @param res [Resources] instance.
* @param bitmapOuter [Bitmap] which represents the outer non-movable part of the joystick.
* @param bitmapInnerDefault [Bitmap] which represents the default inner movable part of the joystick.
* @param bitmapInnerPressed [Bitmap] which represents the pressed inner movable part of the joystick.
* @param rectOuter [Rect] which represents the outer joystick bounds.
* @param rectInner [Rect] which represents the inner joystick bounds.
* @param joystickId The ID value what type of joystick this Drawable represents.
* @param buttonId The ID value what type of button this Drawable represents.
*/
class InputOverlayDrawableJoystick(
res: Resources,
bitmapOuter: Bitmap,
bitmapInnerDefault: Bitmap,
bitmapInnerPressed: Bitmap,
rectOuter: Rect,
rectInner: Rect,
val joystickId: Int,
val buttonId: Int
) {
// The ID value what motion event is tracking
var trackId = -1
var xAxis = 0f
private var yAxis = 0f
private var controlPositionX = 0
private var controlPositionY = 0
val width: Int
val height: Int
private var virtBounds: Rect
private val origBounds: Rect
private val outerBitmap: BitmapDrawable
private val defaultStateInnerBitmap: BitmapDrawable
private val pressedStateInnerBitmap: BitmapDrawable
private val boundsBoxBitmap: BitmapDrawable
private var pressedState = false
// TODO: Add button support
val buttonStatus: Int
get() =
NativeLibrary.ButtonState.RELEASED
var bounds: Rect?
get() = outerBitmap.bounds
set(bounds) {
outerBitmap.bounds = bounds!!
}
// Nintendo joysticks have y axis inverted
val realYAxis: Float
get() = -yAxis
private val currentStateBitmapDrawable: BitmapDrawable
get() = if (pressedState) pressedStateInnerBitmap else defaultStateInnerBitmap
init {
outerBitmap = BitmapDrawable(res, bitmapOuter)
defaultStateInnerBitmap = BitmapDrawable(res, bitmapInnerDefault)
pressedStateInnerBitmap = BitmapDrawable(res, bitmapInnerPressed)
boundsBoxBitmap = BitmapDrawable(res, bitmapOuter)
width = bitmapOuter.width
height = bitmapOuter.height
bounds = rectOuter
defaultStateInnerBitmap.bounds = rectInner
pressedStateInnerBitmap.bounds = rectInner
virtBounds = bounds!!
origBounds = outerBitmap.copyBounds()
boundsBoxBitmap.alpha = 0
boundsBoxBitmap.bounds = virtBounds
setInnerBounds()
}
fun draw(canvas: Canvas?) {
outerBitmap.draw(canvas!!)
currentStateBitmapDrawable.draw(canvas)
boundsBoxBitmap.draw(canvas)
}
fun updateStatus(event: MotionEvent): Boolean {
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt()
val pointerId = event.getPointerId(pointerIndex)
val motionEvent = event.action and MotionEvent.ACTION_MASK
val isActionDown =
motionEvent == MotionEvent.ACTION_DOWN || motionEvent == MotionEvent.ACTION_POINTER_DOWN
val isActionUp =
motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
if (isActionDown) {
if (!bounds!!.contains(xPosition, yPosition)) {
return false
}
pressedState = true
outerBitmap.alpha = 0
boundsBoxBitmap.alpha = 255
if (EmulationMenuSettings.joystickRelCenter) {
virtBounds.offset(
xPosition - virtBounds.centerX(),
yPosition - virtBounds.centerY()
)
}
boundsBoxBitmap.bounds = virtBounds
trackId = pointerId
}
if (isActionUp) {
if (trackId != pointerId) {
return false
}
pressedState = false
xAxis = 0.0f
yAxis = 0.0f
outerBitmap.alpha = 255
boundsBoxBitmap.alpha = 0
virtBounds = Rect(
origBounds.left,
origBounds.top,
origBounds.right,
origBounds.bottom
)
bounds = Rect(
origBounds.left,
origBounds.top,
origBounds.right,
origBounds.bottom
)
setInnerBounds()
trackId = -1
return true
}
if (trackId == -1) return false
for (i in 0 until event.pointerCount) {
if (trackId != event.getPointerId(i)) {
continue
}
var touchX = event.getX(i)
var touchY = event.getY(i)
var maxY = virtBounds.bottom.toFloat()
var maxX = virtBounds.right.toFloat()
touchX -= virtBounds.centerX().toFloat()
maxX -= virtBounds.centerX().toFloat()
touchY -= virtBounds.centerY().toFloat()
maxY -= virtBounds.centerY().toFloat()
val axisX = touchX / maxX
val axisY = touchY / maxY
val oldXAxis = xAxis
val oldYAxis = yAxis
// Clamp the circle pad input to a circle
val angle = atan2(axisY.toDouble(), axisX.toDouble()).toFloat()
var radius = sqrt((axisX * axisX + axisY * axisY).toDouble()).toFloat()
if (radius > 1.0f) {
radius = 1.0f
}
xAxis = cos(angle.toDouble()).toFloat() * radius
yAxis = sin(angle.toDouble()).toFloat() * radius
setInnerBounds()
return oldXAxis != xAxis && oldYAxis != yAxis
}
return false
}
private fun setInnerBounds() {
var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt()
var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt()
if (x > virtBounds.centerX() + virtBounds.width() / 2) x =
virtBounds.centerX() + virtBounds.width() / 2
if (x < virtBounds.centerX() - virtBounds.width() / 2) x =
virtBounds.centerX() - virtBounds.width() / 2
if (y > virtBounds.centerY() + virtBounds.height() / 2) y =
virtBounds.centerY() + virtBounds.height() / 2
if (y < virtBounds.centerY() - virtBounds.height() / 2) y =
virtBounds.centerY() - virtBounds.height() / 2
val width = pressedStateInnerBitmap.bounds.width() / 2
val height = pressedStateInnerBitmap.bounds.height() / 2
defaultStateInnerBitmap.setBounds(
x - width,
y - height,
x + width,
y + height
)
pressedStateInnerBitmap.bounds = defaultStateInnerBitmap.bounds
}
fun setPosition(x: Int, y: Int) {
controlPositionX = x
controlPositionY = y
}
}