diff --git a/app_pojavlauncher/src/main/assets/components/lwjgl3/lwjgl-glfw-classes.jar b/app_pojavlauncher/src/main/assets/components/lwjgl3/lwjgl-glfw-classes.jar index d7a05d5e41..4141a08bb8 100644 Binary files a/app_pojavlauncher/src/main/assets/components/lwjgl3/lwjgl-glfw-classes.jar and b/app_pojavlauncher/src/main/assets/components/lwjgl3/lwjgl-glfw-classes.jar differ diff --git a/app_pojavlauncher/src/main/assets/components/lwjgl3/version b/app_pojavlauncher/src/main/assets/components/lwjgl3/version index c3b857f256..a4b0232d5d 100644 --- a/app_pojavlauncher/src/main/assets/components/lwjgl3/version +++ b/app_pojavlauncher/src/main/assets/components/lwjgl3/version @@ -1 +1 @@ -1720909595576 \ No newline at end of file +1732218529630 \ No newline at end of file diff --git a/app_pojavlauncher/src/main/java/com/kdt/CustomSeekbar.java b/app_pojavlauncher/src/main/java/com/kdt/CustomSeekbar.java new file mode 100644 index 0000000000..050276e3e2 --- /dev/null +++ b/app_pojavlauncher/src/main/java/com/kdt/CustomSeekbar.java @@ -0,0 +1,138 @@ +package com.kdt; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.AttributeSet; +import android.widget.SeekBar; + +/** + * Seekbar with ability to handle ranges and increments + */ +@SuppressLint("AppCompatCustomView") +public class CustomSeekbar extends SeekBar { + private int mMin = 0; + private int mIncrement = 1; + private SeekBar.OnSeekBarChangeListener mListener; + + /** When using increments, this flag is used to prevent double calls to the listener */ + private boolean mInternalChanges = false; + + public CustomSeekbar(Context context) { + super(context); + setup(); + } + + public CustomSeekbar(Context context, AttributeSet attrs) { + super(context, attrs); + setup(); + } + + public CustomSeekbar(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(); + } + + public CustomSeekbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + setup(); + } + + public void setIncrement(int increment) { + mIncrement = increment; + } + + public void setRange(int min, int max) { + mMin = min; + setMax(max - min); + } + + @Override + public synchronized void setProgress(int progress) { + super.setProgress(applyIncrement(progress - mMin)); + } + + @Override + public void setProgress(int progress, boolean animate) { + super.setProgress(applyIncrement(progress - mMin), animate); + } + + @Override + public synchronized int getProgress() { + return applyIncrement(super.getProgress() + mMin); + } + + @Override + public synchronized void setMin(int min) { + super.setMin(min); + mMin = min; + } + + /** + * Wrapper to allow for a listener to be set around the internal listener + */ + @Override + public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) { + mListener = l; + } + + public void setup() { + super.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + /** Store the previous progress to prevent double calls with increments */ + private int previousProgress = 0; + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (mInternalChanges) return; + mInternalChanges = true; + + progress += mMin; + progress = applyIncrement(progress); + + if (progress != previousProgress) { + if (mListener != null) { + previousProgress = progress; + mListener.onProgressChanged(seekBar, progress, fromUser); + } + } + + // Forces the thumb to snap to the increment + setProgress(progress); + mInternalChanges = false; + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + if (mInternalChanges) return; + + if (mListener != null) { + mListener.onStartTrackingTouch(seekBar); + } + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + if (mInternalChanges) return; + mInternalChanges = true; + + setProgress(seekBar.getProgress()); + + if (mListener != null) { + mListener.onStopTrackingTouch(seekBar); + } + mInternalChanges = false; + } + }); + } + + /** + * Apply increment to the progress + * @param progress Progress to apply increment to + * @return Progress with increment applied + */ + private int applyIncrement(int progress) { + if (mIncrement < 1) return progress; + + progress = progress / mIncrement; + progress = progress * mIncrement; + return progress; + } +} diff --git a/app_pojavlauncher/src/main/java/com/kdt/SideDialogView.java b/app_pojavlauncher/src/main/java/com/kdt/SideDialogView.java new file mode 100644 index 0000000000..5570295105 --- /dev/null +++ b/app_pojavlauncher/src/main/java/com/kdt/SideDialogView.java @@ -0,0 +1,246 @@ +package com.kdt; + +import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.CallSuper; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.core.content.res.ResourcesCompat; + +import net.kdt.pojavlaunch.R; +import net.kdt.pojavlaunch.Tools; + +/** + * The base class for side dialog views + * A side dialog is a dialog appearing from one side of the screen + */ +public abstract class SideDialogView { + + private final ViewGroup mParent; + private final @LayoutRes int mLayoutId; + private ViewGroup mDialogLayout; + private DefocusableScrollView mScrollView; + protected View mDialogContent; + + protected final int mMargin; + private ObjectAnimator mSideDialogAnimator; + protected boolean mDisplaying = false; + /* Whether the layout is built */ + private boolean mIsInstantiated = false; + + /* UI elements */ + private Button mStartButton, mEndButton; + private TextView mTitleTextview; + private View mTitleDivider; + + /* Data to store when the UI element has yet to be inflated */ + private @StringRes int mStartButtonStringId, mEndButtonStringId, mTitleStringId; + private View.OnClickListener mStartButtonListener, mEndButtonListener; + + + public SideDialogView(Context context, ViewGroup parent, @LayoutRes int layoutId) { + mMargin = context.getResources().getDimensionPixelOffset(R.dimen._20sdp); + mParent = parent; + mLayoutId = layoutId; + } + + public void setTitle(@StringRes int textId) { + mTitleStringId = textId; + if (mIsInstantiated) { + mTitleTextview.setText(textId); + mTitleTextview.setVisibility(View.VISIBLE); + mTitleDivider.setVisibility(View.VISIBLE); + } + } + + public final void setStartButtonListener(@StringRes int textId, @Nullable View.OnClickListener listener) { + mStartButtonStringId = textId; + mStartButtonListener = listener; + if (mIsInstantiated) setButton(mStartButton, textId, listener); + } + + public final void setEndButtonListener(@StringRes int textId, @Nullable View.OnClickListener listener) { + mEndButtonStringId = textId; + mEndButtonListener = listener; + if (mIsInstantiated) setButton(mEndButton, textId, listener); + } + + private void setButton(@NonNull Button button, @StringRes int textId, @Nullable View.OnClickListener listener) { + button.setText(textId); + button.setOnClickListener(listener); + button.setVisibility(View.VISIBLE); + } + + + private void inflateLayout() { + if(mIsInstantiated) { + Log.w("SideDialogView", "Layout already inflated"); + return; + } + + // Inflate layouts + mDialogLayout = (ViewGroup) LayoutInflater.from(mParent.getContext()).inflate(R.layout.dialog_side_dialog, mParent, false); + mScrollView = mDialogLayout.findViewById(R.id.side_dialog_scrollview); + mStartButton = mDialogLayout.findViewById(R.id.side_dialog_start_button); + mEndButton = mDialogLayout.findViewById(R.id.side_dialog_end_button); + mTitleTextview = mDialogLayout.findViewById(R.id.side_dialog_title_textview); + mTitleDivider = mDialogLayout.findViewById(R.id.side_dialog_title_divider); + + LayoutInflater.from(mParent.getContext()).inflate(mLayoutId, mScrollView, true); + mDialogContent = mScrollView.getChildAt(0); + + // Attach layouts + mParent.addView(mDialogLayout); + + mSideDialogAnimator = ObjectAnimator.ofFloat(mDialogLayout, "x", 0).setDuration(600); + mSideDialogAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); + + mDialogLayout.setElevation(10); + mDialogLayout.setTranslationZ(10); + + mDialogLayout.setVisibility(View.VISIBLE); + mDialogLayout.setBackground(ResourcesCompat.getDrawable(mDialogLayout.getResources(), R.drawable.background_control_editor, null)); + + //TODO offset better according to view width + mDialogLayout.setX(-mDialogLayout.getResources().getDimensionPixelOffset(R.dimen._280sdp)); + mIsInstantiated = true; + + // Set up UI elements + if (mTitleStringId != 0) setTitle(mTitleStringId); + if (mStartButtonStringId != 0) setStartButtonListener(mStartButtonStringId, mStartButtonListener); + if (mEndButtonStringId != 0) setEndButtonListener(mEndButtonStringId, mEndButtonListener); + } + + /** Destroy the layout, cleanup variables */ + private void deflateLayout() { + if(!mIsInstantiated) { + Log.w("SideDialogView", "Layout not inflated"); + return; + } + + mParent.removeView(mDialogLayout); + mIsInstantiated = false; + + mScrollView = null; + mSideDialogAnimator = null; + mDialogLayout = null; + mDialogContent = null; + mTitleTextview = null; + mTitleDivider = null; + mStartButton = null; + mEndButton = null; + } + + + /** + * Slide the layout into the visible screen area + */ + @CallSuper + public final void appear(boolean fromRight) { + if (!mIsInstantiated) { + inflateLayout(); + onInflate(); + } + + // To avoid UI sizing issue when the dialog is not fully inflated + onAppear(); + Tools.runOnUiThread(() -> { + if (fromRight) { + if (!mDisplaying || !isAtRight()) { + mSideDialogAnimator.setFloatValues(currentDisplayMetrics.widthPixels, currentDisplayMetrics.widthPixels - mScrollView.getWidth() - mMargin); + mSideDialogAnimator.start(); + mDisplaying = true; + } + } else { + if (!mDisplaying || isAtRight()) { + mSideDialogAnimator.setFloatValues(-mDialogLayout.getWidth(), mMargin); + mSideDialogAnimator.start(); + mDisplaying = true; + } + } + }); + } + + protected final boolean isAtRight() { + return mDialogLayout.getX() > currentDisplayMetrics.widthPixels / 2f; + } + + /** + * Slide out the layout + * @param destroy Whether the layout should be destroyed after disappearing. + * Recommended to be true if the layout is not going to be used anymore + */ + @CallSuper + public final void disappear(boolean destroy) { + if (!mDisplaying) { + if(destroy) { + onDisappear(); + onDestroy(); + deflateLayout(); + } + return; + } + + mDisplaying = false; + if (isAtRight()) + mSideDialogAnimator.setFloatValues(currentDisplayMetrics.widthPixels - mDialogLayout.getWidth() - mMargin, currentDisplayMetrics.widthPixels); + else + mSideDialogAnimator.setFloatValues(mMargin, -mDialogLayout.getWidth()); + + if(destroy) { + onDisappear(); + onDestroy(); + mSideDialogAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSideDialogAnimator.removeListener(this); + deflateLayout(); + } + }); + } + + mSideDialogAnimator.start(); + } + + /** @return Whether the dialog is currently displaying */ + public final boolean isDisplaying(){ + return mDisplaying; + } + + /** + * Called when the dialog is inflated, ideal for setting up UI elements bindings + */ + protected void onInflate() {} + + /** + * Called after the dialog has appeared + */ + protected void onAppear() {} + + /** + * Called after the dialog has disappeared + */ + protected void onDisappear() {} + + /** + * Called before the dialog gets destroyed (removing views from parent) + * Ideal for cleaning up resources + */ + protected void onDestroy() {} + + +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/JavaGUILauncherActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/JavaGUILauncherActivity.java index 9e7d99c97d..6db1ccbf2f 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/JavaGUILauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/JavaGUILauncherActivity.java @@ -89,8 +89,8 @@ protected void onCreate(Bundle savedInstanceState) { mMousePointerImageView.post(() -> { ViewGroup.LayoutParams params = mMousePointerImageView.getLayoutParams(); - params.width = (int) (36 / 100f * LauncherPreferences.PREF_MOUSESCALE); - params.height = (int) (54 / 100f * LauncherPreferences.PREF_MOUSESCALE); + params.width = (int) (36 * LauncherPreferences.PREF_MOUSESCALE); + params.height = (int) (54 * LauncherPreferences.PREF_MOUSESCALE); }); mTouchPad.setOnTouchListener(new View.OnTouchListener() { @@ -177,7 +177,7 @@ public boolean onTouch(View v, MotionEvent event) { getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { - MainActivity.dialogForceClose(JavaGUILauncherActivity.this); + Tools.dialogForceClose(JavaGUILauncherActivity.this); } }); } @@ -336,7 +336,7 @@ void sendScaledMousePosition(float x, float y){ } public void forceClose(View v) { - MainActivity.dialogForceClose(this); + Tools.dialogForceClose(this); } public void openLogOutput(View v) { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java index 766c835dc3..50eb96e747 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MainActivity.java @@ -1,6 +1,7 @@ package net.kdt.pojavlaunch; import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_ENABLE_GYRO; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_SUSTAINED_PERFORMANCE; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_USE_ALTERNATE_SURFACE; import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_VIRTUAL_MOUSE_START; @@ -27,15 +28,12 @@ import android.util.Log; import android.view.InputDevice; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.webkit.MimeTypeMap; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; -import android.widget.SeekBar; -import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; @@ -55,9 +53,11 @@ import net.kdt.pojavlaunch.customcontrols.keyboard.LwjglCharSender; import net.kdt.pojavlaunch.customcontrols.keyboard.TouchCharInput; import net.kdt.pojavlaunch.customcontrols.mouse.GyroControl; +import net.kdt.pojavlaunch.customcontrols.mouse.HotbarView; import net.kdt.pojavlaunch.customcontrols.mouse.Touchpad; import net.kdt.pojavlaunch.lifecycle.ContextExecutor; import net.kdt.pojavlaunch.prefs.LauncherPreferences; +import net.kdt.pojavlaunch.prefs.QuickSettingSideDialog; import net.kdt.pojavlaunch.services.GameService; import net.kdt.pojavlaunch.utils.JREUtils; import net.kdt.pojavlaunch.utils.MCOptionUtils; @@ -84,7 +84,8 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe private ListView navDrawer; private View mDrawerPullButton; private GyroControl mGyroControl = null; - public static ControlLayout mControlLayout; + private ControlLayout mControlLayout; + private HotbarView mHotbarView; MinecraftProfile minecraftProfile; @@ -94,6 +95,8 @@ public class MainActivity extends BaseActivity implements ControlButtonMenuListe public AdapterView.OnItemClickListener ingameControlsEditorListener; private GameService.LocalBinder mServiceBinder; + private QuickSettingSideDialog mQuickSettingSideDialog; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -106,7 +109,8 @@ public void onCreate(Bundle savedInstanceState) { initLayout(R.layout.activity_basemain); CallbackBridge.addGrabListener(touchpad); CallbackBridge.addGrabListener(minecraftGLView); - if(LauncherPreferences.PREF_ENABLE_GYRO) mGyroControl = new GyroControl(this); + + mGyroControl = new GyroControl(this); // Enabling this on TextureView results in a broken white result if(PREF_USE_ALTERNATE_SURFACE) getWindow().setBackgroundDrawable(null); @@ -185,12 +189,10 @@ protected void initLayout(int resId) { android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.menu_ingame)); gameActionClickListener = (parent, view, position, id) -> { switch(position) { - case 0: dialogForceClose(MainActivity.this); break; - case 1: openLogOutput(); break; - case 2: dialogSendCustomKey(); break; - case 3: adjustMouseSpeedLive(); break; - case 4: adjustGyroSensitivityLive(); break; - case 5: openCustomControls(); break; + case 0: openLogOutput(); break; + case 1: dialogSendCustomKey(); break; + case 2: openQuickSettings(); break; + case 3: openCustomControls(); break; } drawerLayout.closeDrawers(); }; @@ -198,7 +200,6 @@ protected void initLayout(int resId) { navDrawer.setOnItemClickListener(gameActionClickListener); drawerLayout.closeDrawers(); - final String finalVersion = version; minecraftGLView.setSurfaceReadyListener(() -> { try { @@ -240,8 +241,12 @@ private void loadControls() { @Override public void onAttachedToWindow() { - LauncherPreferences.computeNotchSize(this); - loadControls(); + // Post to get the correct display dimensions after layout. + mControlLayout.post(()->{ + LauncherPreferences.computeNotchSize(this); + Tools.getDisplayMetrics(this); + loadControls(); + }); } /** Boilerplate binding */ @@ -255,21 +260,25 @@ private void bindValues(){ mControlLayout = findViewById(R.id.main_control_layout); touchCharInput = findViewById(R.id.mainTouchCharInput); mDrawerPullButton = findViewById(R.id.drawer_button); + mHotbarView = findViewById(R.id.hotbar_view); } @Override public void onResume() { super.onResume(); - if(mGyroControl != null) mGyroControl.enable(); + if(PREF_ENABLE_GYRO) mGyroControl.enable(); CallbackBridge.nativeSetWindowAttrib(LwjglGlfwKeycode.GLFW_HOVERED, 1); } @Override protected void onPause() { - if(mGyroControl != null) mGyroControl.disable(); + mGyroControl.disable(); if (CallbackBridge.isGrabbing()){ sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_ESCAPE); } + if(mQuickSettingSideDialog != null) { + mQuickSettingSideDialog.cancel(); + } CallbackBridge.nativeSetWindowAttrib(LwjglGlfwKeycode.GLFW_HOVERED, 0); super.onPause(); } @@ -299,9 +308,16 @@ public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); if(mGyroControl != null) mGyroControl.updateOrientation(); - Tools.updateWindowSize(this); - minecraftGLView.refreshSize(); - runOnUiThread(() -> mControlLayout.refreshControlButtonPositions()); + // Layout resize is practically guaranteed on a configuration change, and `onConfigurationChanged` + // does not implicitly start a layout. So, request a layout and expect the screen dimensions to be valid after the] + // post. + mControlLayout.requestLayout(); + mControlLayout.post(()->{ + // Child of mControlLayout, so refreshing size here is correct + minecraftGLView.refreshSize(); + Tools.updateWindowSize(this); + mControlLayout.refreshControlButtonPositions(); + }); } @Override @@ -326,14 +342,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } - public static void fullyExit() { - android.os.Process.killProcess(android.os.Process.myPid()); - } - - public static boolean isAndroid8OrHigher() { - return Build.VERSION.SDK_INT >= 26; - } - private void runCraft(String versionId, JMinecraftVersionList.Version version) throws Throwable { if(Tools.LOCAL_RENDERER == null) { Tools.LOCAL_RENDERER = LauncherPreferences.PREF_RENDERER; @@ -347,7 +355,7 @@ private void runCraft(String versionId, JMinecraftVersionList.Version version) t } MinecraftAccount minecraftAccount = PojavProfile.getCurrentProfileContent(this, null); Logger.appendToLog("--------- Starting game with Launcher Debug!"); - printLauncherInfo(versionId, Tools.isValidString(minecraftProfile.javaArgs) ? minecraftProfile.javaArgs : LauncherPreferences.PREF_CUSTOM_JAVA_ARGS); + Tools.printLauncherInfo(versionId, Tools.isValidString(minecraftProfile.javaArgs) ? minecraftProfile.javaArgs : LauncherPreferences.PREF_CUSTOM_JAVA_ARGS); JREUtils.redirectAndPrintJRELog(); LauncherProfiles.load(); int requiredJavaVersion = 8; @@ -357,15 +365,6 @@ private void runCraft(String versionId, JMinecraftVersionList.Version version) t Tools.runOnUiThread(()-> mServiceBinder.isActive = false); } - private void printLauncherInfo(String gameVersion, String javaArguments) { - Logger.appendToLog("Info: Launcher version: " + BuildConfig.VERSION_NAME); - Logger.appendToLog("Info: Architecture: " + Architecture.archAsString(Tools.DEVICE_ARCHITECTURE)); - Logger.appendToLog("Info: Device model: " + Build.MANUFACTURER + " " +Build.MODEL); - Logger.appendToLog("Info: API version: " + Build.VERSION.SDK_INT); - Logger.appendToLog("Info: Selected Minecraft version: " + gameVersion); - Logger.appendToLog("Info: Custom Java arguments: \"" + javaArguments + "\""); - } - private void dialogSendCustomKey() { AlertDialog.Builder dialog = new AlertDialog.Builder(this); dialog.setTitle(R.string.control_customkey); @@ -388,6 +387,29 @@ private void openLogOutput() { loggerView.setVisibility(View.VISIBLE); } + private void openQuickSettings() { + if(mQuickSettingSideDialog == null) { + mQuickSettingSideDialog = new QuickSettingSideDialog(this, mControlLayout) { + @Override + public void onResolutionChanged() { + minecraftGLView.refreshSize(); + mHotbarView.onResolutionChanged(); + } + + @Override + public void onGyroStateChanged() { + mGyroControl.updateOrientation(); + if (PREF_ENABLE_GYRO) { + mGyroControl.enable(); + } else { + mGyroControl.disable(); + } + } + }; + } + mQuickSettingSideDialog.appear(true); + } + public static void toggleMouse(Context ctx) { if (CallbackBridge.isGrabbing()) return; @@ -396,19 +418,6 @@ public static void toggleMouse(Context ctx) { Toast.LENGTH_SHORT).show(); } - public static void dialogForceClose(Context ctx) { - new AlertDialog.Builder(ctx) - .setMessage(R.string.mcn_exit_confirm) - .setNegativeButton(android.R.string.cancel, null) - .setPositiveButton(android.R.string.ok, (p1, p2) -> { - try { - fullyExit(); - } catch (Throwable th) { - Log.w(Tools.APP_NAME, "Could not enable System.exit() method!", th); - } - }).show(); - } - @Override public boolean dispatchKeyEvent(KeyEvent event) { if(isInEditor) { @@ -433,83 +442,6 @@ public static void switchKeyboardState() { if(touchCharInput != null) touchCharInput.switchKeyboardState(); } - - int tmpMouseSpeed; - public void adjustMouseSpeedLive() { - AlertDialog.Builder b = new AlertDialog.Builder(this); - b.setTitle(R.string.mcl_setting_title_mousespeed); - View v = LayoutInflater.from(this).inflate(R.layout.dialog_live_mouse_speed_editor,null); - final SeekBar sb = v.findViewById(R.id.mouseSpeed); - final TextView tv = v.findViewById(R.id.mouseSpeedTV); - sb.setMax(275); - tmpMouseSpeed = (int) ((LauncherPreferences.PREF_MOUSESPEED*100)); - sb.setProgress(tmpMouseSpeed-25); - tv.setText(getString(R.string.percent_format, tmpMouseSpeed)); - sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int i, boolean b) { - tmpMouseSpeed = i+25; - tv.setText(getString(R.string.percent_format, tmpMouseSpeed)); - } - @Override - public void onStartTrackingTouch(SeekBar seekBar) {} - @Override - public void onStopTrackingTouch(SeekBar seekBar) {} - }); - b.setView(v); - b.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { - LauncherPreferences.PREF_MOUSESPEED = ((float)tmpMouseSpeed)/100f; - LauncherPreferences.DEFAULT_PREF.edit().putInt("mousespeed",tmpMouseSpeed).apply(); - dialogInterface.dismiss(); - System.gc(); - }); - b.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> { - dialogInterface.dismiss(); - System.gc(); - }); - b.show(); - } - - int tmpGyroSensitivity; - public void adjustGyroSensitivityLive() { - if(!LauncherPreferences.PREF_ENABLE_GYRO) { - Toast.makeText(this, R.string.toast_turn_on_gyro, Toast.LENGTH_LONG).show(); - return; - } - AlertDialog.Builder b = new AlertDialog.Builder(this); - b.setTitle(R.string.preference_gyro_sensitivity_title); - View v = LayoutInflater.from(this).inflate(R.layout.dialog_live_mouse_speed_editor,null); - final SeekBar sb = v.findViewById(R.id.mouseSpeed); - final TextView tv = v.findViewById(R.id.mouseSpeedTV); - sb.setMax(275); - tmpGyroSensitivity = (int) ((LauncherPreferences.PREF_GYRO_SENSITIVITY*100)); - sb.setProgress(tmpGyroSensitivity -25); - tv.setText(getString(R.string.percent_format, tmpGyroSensitivity)); - sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int i, boolean b) { - tmpGyroSensitivity = i+25; - tv.setText(getString(R.string.percent_format, tmpGyroSensitivity)); - } - @Override - public void onStartTrackingTouch(SeekBar seekBar) {} - @Override - public void onStopTrackingTouch(SeekBar seekBar) {} - }); - b.setView(v); - b.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { - LauncherPreferences.PREF_GYRO_SENSITIVITY = ((float) tmpGyroSensitivity)/100f; - LauncherPreferences.DEFAULT_PREF.edit().putInt("gyroSensitivity", tmpGyroSensitivity).apply(); - dialogInterface.dismiss(); - System.gc(); - }); - b.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> { - dialogInterface.dismiss(); - System.gc(); - }); - b.show(); - } - public static void openLink(String link) { Context ctx = touchpad.getContext(); // no more better way to obtain a context statically ((Activity)ctx).runOnUiThread(() -> { @@ -582,10 +514,10 @@ public void onClickedMenu() { @Override public void exitEditor() { try { - MainActivity.mControlLayout.loadLayout((CustomControls)null); - MainActivity.mControlLayout.setModifiable(false); + mControlLayout.loadLayout((CustomControls)null); + mControlLayout.setModifiable(false); System.gc(); - MainActivity.mControlLayout.loadLayout( + mControlLayout.loadLayout( minecraftProfile.controlFile == null ? LauncherPreferences.PREF_DEFAULTCTRL_PATH : Tools.CTRLMAP_PATH + "/" + minecraftProfile.controlFile); @@ -593,7 +525,7 @@ public void exitEditor() { } catch (IOException e) { Tools.showError(this,e); } - //((MainActivity) this).mControlLayout.loadLayout((CustomControls)null); + navDrawer.setAdapter(gameActionArrayAdapter); navDrawer.setOnItemClickListener(gameActionClickListener); isInEditor = false; @@ -629,7 +561,7 @@ private boolean checkCaptureDispatchConditions(MotionEvent event) { @Override public boolean dispatchTrackballEvent(MotionEvent ev) { - if(MainActivity.isAndroid8OrHigher() && checkCaptureDispatchConditions(ev)) + if(Tools.isAndroid8OrHigher() && checkCaptureDispatchConditions(ev)) return minecraftGLView.dispatchCapturedPointerEvent(ev); else return super.dispatchTrackballEvent(ev); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java index 2c460558f9..dd9f5f10f2 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/MinecraftGLSurface.java @@ -66,8 +66,6 @@ public class MinecraftGLSurface extends View implements GrabListener { .remapRightTrigger(true) .remapDpad(true)); - /* Resolution scaler option, allow downsizing a window */ - private final float mScaleFactor = LauncherPreferences.PREF_SCALE_FACTOR/100f; /* Sensitivity, adjusted according to screen size */ private final double mSensitivityFactor = (1.4 * (1080f/ Tools.getDisplayMetrics((Activity) getContext()).heightPixels)); @@ -78,7 +76,7 @@ public class MinecraftGLSurface extends View implements GrabListener { View mSurface; private final InGameEventProcessor mIngameProcessor = new InGameEventProcessor(mSensitivityFactor); - private final InGUIEventProcessor mInGUIProcessor = new InGUIEventProcessor(mScaleFactor); + private final InGUIEventProcessor mInGUIProcessor = new InGUIEventProcessor(); private TouchEventProcessor mCurrentTouchProcessor = mInGUIProcessor; private AndroidPointerCapture mPointerCapture; private boolean mLastGrabState = false; @@ -95,7 +93,7 @@ public MinecraftGLSurface(Context context, AttributeSet attributeSet) { @RequiresApi(api = Build.VERSION_CODES.O) private void setUpPointerCapture(AbstractTouchpad touchpad) { if(mPointerCapture != null) mPointerCapture.detach(); - mPointerCapture = new AndroidPointerCapture(touchpad, this, mScaleFactor); + mPointerCapture = new AndroidPointerCapture(touchpad, this); } /** Initialize the view and all its settings @@ -105,7 +103,7 @@ private void setUpPointerCapture(AbstractTouchpad touchpad) { * when the cursor is not grabbed */ public void start(boolean isAlreadyRunning, AbstractTouchpad touchpad){ - if(MainActivity.isAndroid8OrHigher()) setUpPointerCapture(touchpad); + if(Tools.isAndroid8OrHigher()) setUpPointerCapture(touchpad); mInGUIProcessor.setAbstractTouchpad(touchpad); if(LauncherPreferences.PREF_USE_ALTERNATE_SURFACE){ SurfaceView surfaceView = new SurfaceView(getContext()); @@ -188,7 +186,7 @@ public boolean onTouchEvent(MotionEvent e) { for (int i = 0; i < e.getPointerCount(); i++) { int toolType = e.getToolType(i); if(toolType == MotionEvent.TOOL_TYPE_MOUSE) { - if(MainActivity.isAndroid8OrHigher() && + if(Tools.isAndroid8OrHigher() && mPointerCapture != null) { mPointerCapture.handleAutomaticCapture(); return true; @@ -197,7 +195,7 @@ public boolean onTouchEvent(MotionEvent e) { // Mouse found if(CallbackBridge.isGrabbing()) return false; - CallbackBridge.sendCursorPos( e.getX(i) * mScaleFactor, e.getY(i) * mScaleFactor); + CallbackBridge.sendCursorPos( e.getX(i) * LauncherPreferences.PREF_SCALE_FACTOR, e.getY(i) * LauncherPreferences.PREF_SCALE_FACTOR); return true; //mouse event handled successfully } if (mIngameProcessor == null || mInGUIProcessor == null) return true; @@ -236,8 +234,8 @@ public boolean dispatchGenericMotionEvent(MotionEvent event) { switch(event.getActionMasked()) { case MotionEvent.ACTION_HOVER_MOVE: - CallbackBridge.mouseX = (event.getX(mouseCursorIndex) * mScaleFactor); - CallbackBridge.mouseY = (event.getY(mouseCursorIndex) * mScaleFactor); + CallbackBridge.mouseX = (event.getX(mouseCursorIndex) * LauncherPreferences.PREF_SCALE_FACTOR); + CallbackBridge.mouseY = (event.getY(mouseCursorIndex) * LauncherPreferences.PREF_SCALE_FACTOR); CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY); return true; case MotionEvent.ACTION_SCROLL: @@ -326,14 +324,22 @@ public static boolean sendMouseButtonUnconverted(int button, boolean status) { return true; } - - - - /** Called when the size need to be set at any point during the surface lifecycle **/ public void refreshSize(){ - windowWidth = Tools.getDisplayFriendlyRes(Tools.currentDisplayMetrics.widthPixels, mScaleFactor); - windowHeight = Tools.getDisplayFriendlyRes(Tools.currentDisplayMetrics.heightPixels, mScaleFactor); + refreshSize(false); + } + + /** Same as refreshSize, but allows you to force an immediate size update **/ + public void refreshSize(boolean immediate) { + if(isInLayout() && !immediate) { + post(this::refreshSize); + return; + } + // Use the width and height of the View instead of display dimensions to avoid + // getting squiched/stretched due to inconsistencies between the layout and + // screen dimensions. + windowWidth = Tools.getDisplayFriendlyRes(getWidth(), LauncherPreferences.PREF_SCALE_FACTOR); + windowHeight = Tools.getDisplayFriendlyRes(getHeight(), LauncherPreferences.PREF_SCALE_FACTOR); if(mSurface == null){ Log.w("MGLSurface", "Attempt to refresh size on null surface"); return; @@ -355,8 +361,9 @@ public void refreshSize(){ } private void realStart(Surface surface){ - // Initial size set - refreshSize(); + // Initial size set. Request immedate refresh, otherwise the initial width and height for the game + // may be broken/unknown. + refreshSize(true); //Load Minecraft options: MCOptionUtils.set("fullscreen", "off"); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavApplication.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavApplication.java index 581183654b..e8f280cfe7 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavApplication.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/PojavApplication.java @@ -52,7 +52,7 @@ public void onCreate() { } FatalErrorActivity.showError(PojavApplication.this, crashFile.getAbsolutePath(), storagePermAllowed, th); - MainActivity.fullyExit(); + Tools.fullyExit(); }); try { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java index 431e8c1fbe..9bc091e470 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -19,6 +19,8 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; +import android.hardware.Sensor; +import android.hardware.SensorManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -31,6 +33,9 @@ import android.util.DisplayMetrics; import android.util.Log; import android.view.View; +import android.view.Window; +import android.view.WindowInsets; +import android.view.WindowInsetsController; import android.view.WindowManager; import android.widget.EditText; import android.widget.TextView; @@ -38,6 +43,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.NotificationManagerCompat; @@ -506,10 +512,14 @@ public static DisplayMetrics getDisplayMetrics(Activity activity) { return displayMetrics; } - public static void setFullscreen(Activity activity, boolean fullscreen) { + @SuppressWarnings("deprecation") + private static void setFullscreenLegacy(Activity activity, boolean fullscreen) { final View decorView = activity.getWindow().getDecorView(); View.OnSystemUiVisibilityChangeListener visibilityChangeListener = visibility -> { - if(fullscreen){ + boolean multiWindowMode = SDK_INT >= 24 && activity.isInMultiWindowMode(); + // When in multi-window mode, asking for fullscreen makes no sense (cause the launcher runs in a window) + // So, ignore the fullscreen setting when activity is in multi window mode + if(fullscreen && !multiWindowMode){ if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_FULLSCREEN @@ -527,11 +537,67 @@ public static void setFullscreen(Activity activity, boolean fullscreen) { visibilityChangeListener.onSystemUiVisibilityChange(decorView.getSystemUiVisibility()); //call it once since the UI state may not change after the call, so the activity wont become fullscreen } + @RequiresApi(Build.VERSION_CODES.R) + private static void setFullscreenSdk30(Activity activity, boolean fullscreen) { + final Window window = activity.getWindow(); + final View decorView = window.getDecorView(); + final int insetControllerFlags = WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars(); + final View.OnApplyWindowInsetsListener windowInsetsListener = (view, windowInsets) ->{ + WindowInsetsController windowInsetsController = decorView.getWindowInsetsController(); + if(windowInsetsController == null) return windowInsets; + boolean multiWindowMode = activity.isInMultiWindowMode(); + // Emulate the behaviour of the legacy function using the new flags + if(fullscreen && !multiWindowMode) { + windowInsetsController.hide(insetControllerFlags); + windowInsetsController.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + window.setDecorFitsSystemWindows(false); + }else { + windowInsetsController.show(insetControllerFlags); + // Both of the constants below have the exact same numerical value, but + // for some reason the one that works below Android S was removed + // from the acceptable constants for setSystemBarsBehaviour + if (SDK_INT >= Build.VERSION_CODES.S) { + windowInsetsController.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT); + }else { + // noinspection WrongConstant + windowInsetsController.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE); + } + window.setDecorFitsSystemWindows(true); + } + return windowInsets; + }; + decorView.setOnApplyWindowInsetsListener(windowInsetsListener); + windowInsetsListener.onApplyWindowInsets(decorView, null); + } + + public static void setFullscreen(Activity activity, boolean fullscreen) { + if (SDK_INT >= Build.VERSION_CODES.R) { + setFullscreenSdk30(activity, fullscreen); + }else { + setFullscreenLegacy(activity, fullscreen); + } + } + public static DisplayMetrics currentDisplayMetrics; public static void updateWindowSize(Activity activity) { currentDisplayMetrics = getDisplayMetrics(activity); + View dimensionView = activity.findViewById(R.id.dimension_tracker); + + if(dimensionView != null) { + int width = dimensionView.getWidth(); + int height = dimensionView.getHeight(); + if(width != 0 && height != 0) { + Log.i("Tools", "Using dimension_tracker for display dimensions; W="+width+" H="+height); + CallbackBridge.physicalWidth = width; + CallbackBridge.physicalHeight = height; + return; + }else{ + Log.e("Tools","Dimension tracker detected but dimensions out of date. Please check usage.", new Exception()); + } + } + CallbackBridge.physicalWidth = currentDisplayMetrics.widthPixels; CallbackBridge.physicalHeight = currentDisplayMetrics.heightPixels; } @@ -606,7 +672,7 @@ private static void showError(final Context ctx, final int titleId, final String .setPositiveButton(android.R.string.ok, (p1, p2) -> { if(exitIfOk) { if (ctx instanceof MainActivity) { - MainActivity.fullyExit(); + fullyExit(); } else if (ctx instanceof Activity) { ((Activity) ctx).finish(); } @@ -618,7 +684,7 @@ private static void showError(final Context ctx, final int titleId, final String mgr.setPrimaryClip(ClipData.newPlainText("error", printToString(e))); if(exitIfOk) { if (ctx instanceof MainActivity) { - MainActivity.fullyExit(); + fullyExit(); } else { ((Activity) ctx).finish(); } @@ -890,6 +956,24 @@ public static void downloadFile(String urlInput, String nameOutput) throws IOExc File file = new File(nameOutput); DownloadUtils.downloadFile(urlInput, file); } + + public static boolean isAndroid8OrHigher() { + return SDK_INT >= 26; + } + + public static void fullyExit() { + android.os.Process.killProcess(android.os.Process.myPid()); + } + + public static void printLauncherInfo(String gameVersion, String javaArguments) { + Logger.appendToLog("Info: Launcher version: " + BuildConfig.VERSION_NAME); + Logger.appendToLog("Info: Architecture: " + Architecture.archAsString(DEVICE_ARCHITECTURE)); + Logger.appendToLog("Info: Device model: " + Build.MANUFACTURER + " " +Build.MODEL); + Logger.appendToLog("Info: API version: " + SDK_INT); + Logger.appendToLog("Info: Selected Minecraft version: " + gameVersion); + Logger.appendToLog("Info: Custom Java arguments: \"" + javaArguments + "\""); + } + public interface DownloaderFeedback { void updateProgress(int curr, int max); } @@ -1243,4 +1327,22 @@ public static void releaseRenderersCache() { sCompatibleRenderers = null; System.gc(); } + + public static boolean deviceSupportsGyro(@NonNull Context context) { + return ((SensorManager)context.getSystemService(Context.SENSOR_SERVICE)).getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null; + + } + + public static void dialogForceClose(Context ctx) { + new android.app.AlertDialog.Builder(ctx) + .setMessage(R.string.mcn_exit_confirm) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok, (p1, p2) -> { + try { + Tools.fullyExit(); + } catch (Throwable th) { + Log.w(Tools.APP_NAME, "Could not enable System.exit() method!", th); + } + }).show(); + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/colorselector/ColorSelector.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/colorselector/ColorSelector.java index dd614af164..f94b8351ad 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/colorselector/ColorSelector.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/colorselector/ColorSelector.java @@ -5,76 +5,79 @@ import android.graphics.Color; import android.text.Editable; import android.text.TextWatcher; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import androidx.annotation.Nullable; +import com.kdt.SideDialogView; + import net.kdt.pojavlaunch.R; -public class ColorSelector implements HueSelectionListener, RectangleSelectionListener, AlphaSelectionListener, TextWatcher{ +public class ColorSelector extends SideDialogView implements HueSelectionListener, RectangleSelectionListener, AlphaSelectionListener, TextWatcher{ private static final int ALPHA_MASK = ~(0xFF << 24); - private final View mRootView; - private final HueView mHueView; - private final SVRectangleView mLuminosityIntensityView; - private final AlphaView mAlphaView; - private final ColorSideBySideView mColorView; - private final EditText mTextView; + private HueView mHueView; + private SVRectangleView mLuminosityIntensityView; + private AlphaView mAlphaView; + private ColorSideBySideView mColorView; + private EditText mTextView; private ColorSelectionListener mColorSelectionListener; private final float[] mHueTemplate = new float[] {0,1,1}; private final float[] mHsvSelected = new float[] {360,1,1}; private int mAlphaSelected = 0xff; - private final ColorStateList mTextColors; + private ColorStateList mTextColors; private boolean mWatch = true; private boolean mAlphaEnabled = true; - /** - * Creates a color selector dialog for this Context. - * @param context Context used for this ColorSelector dialog - * @param colorSelectionListener Color selection listener to which the events will be sent to. Can be null. - */ public ColorSelector(Context context, ViewGroup parent, @Nullable ColorSelectionListener colorSelectionListener) { + super(context, parent, R.layout.dialog_color_selector); + mColorSelectionListener = colorSelectionListener; + } - mRootView = LayoutInflater.from(context).inflate(R.layout.dialog_color_selector,parent, false); - mHueView = mRootView.findViewById(R.id.color_selector_hue_view); - mLuminosityIntensityView = mRootView.findViewById(R.id.color_selector_rectangle_view); - mAlphaView = mRootView.findViewById(R.id.color_selector_alpha_view); - mColorView = mRootView.findViewById(R.id.color_selector_color_view); - mTextView = mRootView.findViewById(R.id.color_selector_hex_edit); + @Override + protected void onInflate() { + super.onInflate(); + // Initialize the view contents + mHueView = mDialogContent.findViewById(R.id.color_selector_hue_view); + mLuminosityIntensityView = mDialogContent.findViewById(R.id.color_selector_rectangle_view); + mAlphaView = mDialogContent.findViewById(R.id.color_selector_alpha_view); + mColorView = mDialogContent.findViewById(R.id.color_selector_color_view); + mTextView = mDialogContent.findViewById(R.id.color_selector_hex_edit); runColor(Color.RED); mHueView.setHueSelectionListener(this); mLuminosityIntensityView.setRectSelectionListener(this); mAlphaView.setAlphaSelectionListener(this); mTextView.addTextChangedListener(this); mTextColors = mTextView.getTextColors(); - - mColorSelectionListener = colorSelectionListener; - - parent.addView(mRootView); - } - - /** @return The root view, mainly for position manipulation purposes */ - public View getRootView(){ - return mRootView; + mAlphaView.setVisibility(mAlphaEnabled ? View.VISIBLE : View.GONE); + + // Set elevation to show above other side dialogs. + // Jank, should be done better + View contentParent = mDialogContent.findViewById(R.id.side_dialog_scrollview); + if(contentParent != null) { + ViewGroup dialogLayout = (ViewGroup) mDialogContent.getParent(); + dialogLayout.setElevation(11); + dialogLayout.setTranslationZ(11); + } } /** * Shows the color selector with the default (red) color selected. */ - public void show() { - show(Color.RED); + public void show(boolean fromRight) { + show(fromRight, Color.RED); } /** * Shows the color selector with the desired ARGB color selected * @param previousColor the desired ARGB color */ - public void show(int previousColor) { + public void show(boolean fromRight, int previousColor) { + appear(fromRight); runColor(previousColor); // initialize dispatchColorChange(); // set the hex text } @@ -157,8 +160,10 @@ public void setColorSelectionListener(ColorSelectionListener listener){ public void setAlphaEnabled(boolean alphaEnabled){ mAlphaEnabled = alphaEnabled; - mAlphaView.setVisibility(alphaEnabled ? View.VISIBLE : View.GONE); - mAlphaView.setAlpha(255); + if(mAlphaView != null) { + mAlphaView.setVisibility(alphaEnabled ? View.VISIBLE : View.GONE); + mAlphaView.setAlpha(255); + } } private void notifyColorSelector(int color){ diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlLayout.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlLayout.java index 19f5e3cd97..cc031aeff6 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlLayout.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/ControlLayout.java @@ -1,7 +1,6 @@ package net.kdt.pojavlaunch.customcontrols; import static android.content.Context.INPUT_METHOD_SERVICE; -import static net.kdt.pojavlaunch.MainActivity.mControlLayout; import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics; import static org.lwjgl.glfw.CallbackBridge.isGrabbing; @@ -34,7 +33,7 @@ import net.kdt.pojavlaunch.customcontrols.buttons.ControlSubButton; import net.kdt.pojavlaunch.customcontrols.handleview.ActionRow; import net.kdt.pojavlaunch.customcontrols.handleview.ControlHandleView; -import net.kdt.pojavlaunch.customcontrols.handleview.EditControlPopup; +import net.kdt.pojavlaunch.customcontrols.handleview.EditControlSideDialog; import net.kdt.pojavlaunch.prefs.LauncherPreferences; import java.io.File; @@ -54,7 +53,7 @@ public class ControlLayout extends FrameLayout { private boolean mIsModified; private boolean mControlVisible = false; - private EditControlPopup mControlPopup = null; + private EditControlSideDialog mControlDialog = null; private ControlHandleView mHandleView; private ControlButtonMenuListener mMenuListener; public ActionRow mActionRow = null; @@ -297,9 +296,9 @@ public void refreshControlButtonPositions(){ @Override public void onViewRemoved(View child) { super.onViewRemoved(child); - if(child instanceof ControlInterface && mControlPopup != null){ - mControlPopup.disappearColor(); - mControlPopup.disappear(); + if(child instanceof ControlInterface && mControlDialog != null){ + mControlDialog.disappearColor(); + mControlDialog.disappear(false); } } @@ -308,22 +307,23 @@ public void onViewRemoved(View child) { * to the button at hand. */ public void editControlButton(ControlInterface button){ - if(mControlPopup == null){ + if(mControlDialog == null){ // When the panel is null, it needs to inflate first. // So inflate it, then process it on the next frame - mControlPopup = new EditControlPopup(getContext(), this); + mControlDialog = new EditControlSideDialog(getContext(), this); post(() -> editControlButton(button)); return; } - mControlPopup.internalChanges = true; - mControlPopup.setCurrentlyEditedButton(button); - button.loadEditValues(mControlPopup); + mControlDialog.internalChanges = true; + mControlDialog.setCurrentlyEditedButton(button); - mControlPopup.internalChanges = false; + mControlDialog.appear(button.getControlView().getX() + button.getControlView().getWidth()/2f < currentDisplayMetrics.widthPixels/2f); + button.loadEditValues(mControlDialog); - mControlPopup.appear(button.getControlView().getX() + button.getControlView().getWidth()/2f < currentDisplayMetrics.widthPixels/2f); - mControlPopup.disappearColor(); + mControlDialog.internalChanges = false; + + mControlDialog.disappearColor(); if(mHandleView == null){ mHandleView = new ControlHandleView(getContext()); @@ -336,8 +336,7 @@ public void editControlButton(ControlInterface button){ /** Swap the panel if the button position requires it */ public void adaptPanelPosition(){ - if(mControlPopup != null) - mControlPopup.adaptPanelPosition(); + if(mControlDialog != null) mControlDialog.adaptPanelPosition(); } @@ -401,14 +400,14 @@ public void onTouch(View v, MotionEvent ev) { @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { - if (mModifiable && event.getActionMasked() != MotionEvent.ACTION_UP || mControlPopup == null) + if (mModifiable && event.getActionMasked() != MotionEvent.ACTION_UP || mControlDialog == null) return true; InputMethodManager imm = (InputMethodManager) getContext().getSystemService(INPUT_METHOD_SERVICE); // When the input window cannot be hidden, it returns false if(!imm.hideSoftInputFromWindow(getWindowToken(), 0)){ - if(mControlPopup.disappearLayer()){ + if(mControlDialog.disappearLayer()){ mActionRow.setFollowedButton(null); mHandleView.hide(); } @@ -421,9 +420,9 @@ public void removeEditWindow() { // When the input window cannot be hidden, it returns false imm.hideSoftInputFromWindow(getWindowToken(), 0); - if(mControlPopup != null) { - mControlPopup.disappearColor(); - mControlPopup.disappear(); + if(mControlDialog != null) { + mControlDialog.disappearColor(); + mControlDialog.disappear(true); } if(mActionRow != null) mActionRow.setFollowedButton(null); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlButton.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlButton.java index 888c85e5de..5fb9d687ba 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlButton.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlButton.java @@ -20,7 +20,7 @@ import net.kdt.pojavlaunch.R; import net.kdt.pojavlaunch.customcontrols.ControlData; import net.kdt.pojavlaunch.customcontrols.ControlLayout; -import net.kdt.pojavlaunch.customcontrols.handleview.EditControlPopup; +import net.kdt.pojavlaunch.customcontrols.handleview.EditControlSideDialog; import net.kdt.pojavlaunch.prefs.LauncherPreferences; import org.lwjgl.glfw.CallbackBridge; @@ -89,7 +89,7 @@ protected void onDraw(Canvas canvas) { } - public void loadEditValues(EditControlPopup editControlPopup){ + public void loadEditValues(EditControlSideDialog editControlPopup){ editControlPopup.loadValues(getProperties()); } @@ -207,7 +207,7 @@ private void sendSpecialKey(int keycode, boolean isDown){ break; case ControlData.SPECIALBTN_TOGGLECTRL: - if(isDown)MainActivity.mControlLayout.toggleControlVisible(); + if(isDown)getControlLayoutParent().toggleControlVisible(); break; case ControlData.SPECIALBTN_VIRTUALMOUSE: diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlDrawer.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlDrawer.java index cc022ea479..2c31604615 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlDrawer.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlDrawer.java @@ -4,13 +4,12 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.widget.Toast; import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.customcontrols.ControlData; import net.kdt.pojavlaunch.customcontrols.ControlDrawerData; import net.kdt.pojavlaunch.customcontrols.ControlLayout; -import net.kdt.pojavlaunch.customcontrols.handleview.EditControlPopup; +import net.kdt.pojavlaunch.customcontrols.handleview.EditControlSideDialog; import java.util.ArrayList; @@ -183,7 +182,7 @@ public ControlDrawerData getDrawerData() { } @Override - public void loadEditValues(EditControlPopup editControlPopup) { + public void loadEditValues(EditControlSideDialog editControlPopup) { editControlPopup.loadValues(drawerData); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlInterface.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlInterface.java index a46380a950..569ddfefc9 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlInterface.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlInterface.java @@ -20,7 +20,7 @@ import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.customcontrols.ControlData; import net.kdt.pojavlaunch.customcontrols.ControlLayout; -import net.kdt.pojavlaunch.customcontrols.handleview.EditControlPopup; +import net.kdt.pojavlaunch.customcontrols.handleview.EditControlSideDialog; import org.lwjgl.glfw.CallbackBridge; @@ -61,7 +61,7 @@ default void setVisible(boolean isVisible) { /** * Load the values and hide non useful forms */ - void loadEditValues(EditControlPopup editControlPopup); + void loadEditValues(EditControlSideDialog editControlDialog); @Override default void onGrabState(boolean isGrabbing) { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlJoystick.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlJoystick.java index e261d0873f..f9cbe21864 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlJoystick.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlJoystick.java @@ -19,7 +19,7 @@ import net.kdt.pojavlaunch.customcontrols.ControlJoystickData; import net.kdt.pojavlaunch.customcontrols.ControlLayout; import net.kdt.pojavlaunch.customcontrols.gamepad.GamepadJoystick; -import net.kdt.pojavlaunch.customcontrols.handleview.EditControlPopup; +import net.kdt.pojavlaunch.customcontrols.handleview.EditControlSideDialog; import org.lwjgl.glfw.CallbackBridge; @@ -121,7 +121,7 @@ public void setBackground() { public void sendKeyPresses(boolean isDown) {/*STUB since non swipeable*/ } @Override - public void loadEditValues(EditControlPopup editControlPopup) { + public void loadEditValues(EditControlSideDialog editControlPopup) { editControlPopup.loadJoystickValues(mControlData); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlSubButton.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlSubButton.java index 5a7242dd2d..8bdb4fea7a 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlSubButton.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/buttons/ControlSubButton.java @@ -2,13 +2,12 @@ import android.annotation.SuppressLint; import android.view.MotionEvent; -import android.view.View; import android.view.ViewGroup; import net.kdt.pojavlaunch.customcontrols.ControlData; import net.kdt.pojavlaunch.customcontrols.ControlDrawerData; import net.kdt.pojavlaunch.customcontrols.ControlLayout; -import net.kdt.pojavlaunch.customcontrols.handleview.EditControlPopup; +import net.kdt.pojavlaunch.customcontrols.handleview.EditControlSideDialog; @SuppressLint("ViewConstructor") public class ControlSubButton extends ControlButton { @@ -91,7 +90,7 @@ public void snapAndAlign(float x, float y) { } @Override - public void loadEditValues(EditControlPopup editControlPopup) { + public void loadEditValues(EditControlSideDialog editControlPopup) { editControlPopup.loadSubButtonValues(getProperties(), parentDrawer.drawerData.orientation); } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/EditControlPopup.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/EditControlPopup.java deleted file mode 100644 index 1e215484e3..0000000000 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/EditControlPopup.java +++ /dev/null @@ -1,653 +0,0 @@ -package net.kdt.pojavlaunch.customcontrols.handleview; - - -import static android.view.View.GONE; -import static android.view.View.VISIBLE; -import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics; - -import android.animation.ObjectAnimator; -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Color; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.Interpolator; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.SeekBar; -import android.widget.Spinner; -import android.widget.Switch; -import android.widget.TextView; - -import androidx.constraintlayout.widget.ConstraintLayout; - -import com.kdt.DefocusableScrollView; - -import net.kdt.pojavlaunch.EfficientAndroidLWJGLKeycode; -import net.kdt.pojavlaunch.R; -import net.kdt.pojavlaunch.colorselector.ColorSelector; -import net.kdt.pojavlaunch.customcontrols.ControlData; -import net.kdt.pojavlaunch.customcontrols.ControlDrawerData; -import net.kdt.pojavlaunch.customcontrols.ControlJoystickData; -import net.kdt.pojavlaunch.customcontrols.buttons.ControlDrawer; -import net.kdt.pojavlaunch.customcontrols.buttons.ControlInterface; - -import java.util.List; - -/** - * Class providing a sort of popup on top of a Layout, allowing to edit a given ControlButton - */ -public class EditControlPopup { - protected final Spinner[] mKeycodeSpinners = new Spinner[4]; - private final DefocusableScrollView mScrollView; - private final ColorSelector mColorSelector; - - private final ObjectAnimator mEditPopupAnimator; - private final ObjectAnimator mColorEditorAnimator; - private final int mMargin; - public boolean internalChanges = false; // True when we programmatically change stuff. - private final View.OnLayoutChangeListener mLayoutChangedListener = new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - if (internalChanges) return; - - internalChanges = true; - int width = (int) (safeParseFloat(mWidthEditText.getText().toString())); - - if (width >= 0 && Math.abs(right - width) > 1) { - mWidthEditText.setText(String.valueOf(right - left)); - } - int height = (int) (safeParseFloat(mHeightEditText.getText().toString())); - if (height >= 0 && Math.abs(bottom - height) > 1) { - mHeightEditText.setText(String.valueOf(bottom - top)); - } - - internalChanges = false; - } - }; - protected EditText mNameEditText, mWidthEditText, mHeightEditText; - @SuppressLint("UseSwitchCompatOrMaterialCode") - protected Switch mToggleSwitch, mPassthroughSwitch, mSwipeableSwitch, mForwardLockSwitch, mAbsoluteTrackingSwitch; - protected Spinner mOrientationSpinner; - protected TextView[] mKeycodeTextviews = new TextView[4]; - protected SeekBar mStrokeWidthSeekbar, mCornerRadiusSeekbar, mAlphaSeekbar; - protected TextView mStrokePercentTextView, mCornerRadiusPercentTextView, mAlphaPercentTextView; - protected TextView mSelectBackgroundColor, mSelectStrokeColor; - protected ArrayAdapter mAdapter; - protected List mSpecialArray; - protected CheckBox mDisplayInGameCheckbox, mDisplayInMenuCheckbox; - private ConstraintLayout mRootView; - private boolean mDisplaying = false; - private boolean mDisplayingColor = false; - private ControlInterface mCurrentlyEditedButton; - // Decorative textviews - private TextView mOrientationTextView, mMappingTextView, mNameTextView, - mCornerRadiusTextView, mVisibilityTextView, mSizeTextview, mSizeXTextView; - - - public EditControlPopup(Context context, ViewGroup parent) { - mScrollView = (DefocusableScrollView) LayoutInflater.from(context).inflate(R.layout.dialog_control_button_setting, parent, false); - parent.addView(mScrollView); - - mMargin = context.getResources().getDimensionPixelOffset(R.dimen._20sdp); - - mColorSelector = new ColorSelector(context, parent, null); - mColorSelector.getRootView().setElevation(11); - mColorSelector.getRootView().setTranslationZ(11); - mColorSelector.getRootView().setX(-context.getResources().getDimensionPixelOffset(R.dimen._280sdp)); - - mEditPopupAnimator = ObjectAnimator.ofFloat(mScrollView, "x", 0).setDuration(600); - mColorEditorAnimator = ObjectAnimator.ofFloat(mColorSelector.getRootView(), "x", 0).setDuration(600); - Interpolator decelerate = new AccelerateDecelerateInterpolator(); - mEditPopupAnimator.setInterpolator(decelerate); - mColorEditorAnimator.setInterpolator(decelerate); - - mScrollView.setElevation(10); - mScrollView.setTranslationZ(10); - mScrollView.setX(-context.getResources().getDimensionPixelOffset(R.dimen._280sdp)); - - bindLayout(); - loadAdapter(); - - setupRealTimeListeners(); - } - - public static void setPercentageText(TextView textView, int progress) { - textView.setText(textView.getContext().getString(R.string.percent_format, progress)); - } - - /** - * Slide the layout into the visible screen area - */ - public void appear(boolean fromRight) { - disappearColor(); // When someone jumps from a button to another - - if (fromRight) { - if (!mDisplaying || !isAtRight()) { - mEditPopupAnimator.setFloatValues(currentDisplayMetrics.widthPixels, currentDisplayMetrics.widthPixels - mScrollView.getWidth() - mMargin); - mEditPopupAnimator.start(); - } - } else { - if (!mDisplaying || isAtRight()) { - mEditPopupAnimator.setFloatValues(-mScrollView.getWidth(), mMargin); - mEditPopupAnimator.start(); - } - } - - mDisplaying = true; - } - - /** - * Slide out the layout - */ - public void disappear() { - if (!mDisplaying) return; - - mDisplaying = false; - if (isAtRight()) - mEditPopupAnimator.setFloatValues(currentDisplayMetrics.widthPixels - mScrollView.getWidth() - mMargin, currentDisplayMetrics.widthPixels); - else - mEditPopupAnimator.setFloatValues(mMargin, -mScrollView.getWidth()); - - mEditPopupAnimator.start(); - } - - /** - * Slide the layout into the visible screen area - */ - public void appearColor(boolean fromRight, int color) { - if (fromRight) { - if (!mDisplayingColor || !isAtRight()) { - mColorEditorAnimator.setFloatValues(currentDisplayMetrics.widthPixels, currentDisplayMetrics.widthPixels - mScrollView.getWidth() - mMargin); - mColorEditorAnimator.start(); - } - } else { - if (!mDisplayingColor || isAtRight()) { - mColorEditorAnimator.setFloatValues(-mScrollView.getWidth(), mMargin); - mColorEditorAnimator.start(); - } - } - - // Adjust the color selector to have the same size as the control settings - ViewGroup.LayoutParams params = mColorSelector.getRootView().getLayoutParams(); - params.height = mScrollView.getHeight(); - mColorSelector.getRootView().setLayoutParams(params); - - mDisplayingColor = true; - mColorSelector.show(color == -1 ? Color.WHITE : color); - } - - /** - * Slide out the layout - */ - public void disappearColor() { - if (!mDisplayingColor) return; - - mDisplayingColor = false; - if (isAtRight()) - mColorEditorAnimator.setFloatValues(currentDisplayMetrics.widthPixels - mScrollView.getWidth() - mMargin, currentDisplayMetrics.widthPixels); - else - mColorEditorAnimator.setFloatValues(mMargin, -mScrollView.getWidth()); - - mColorEditorAnimator.start(); - } - - /** - * Slide out the first visible layer. - * - * @return True if the last layer is disappearing - */ - public boolean disappearLayer() { - if (mDisplayingColor) { - disappearColor(); - return false; - } else { - disappear(); - return true; - } - } - - /** - * Switch the panels position if needed - */ - public void adaptPanelPosition() { - if (mDisplaying) { - boolean isAtRight = mCurrentlyEditedButton.getControlView().getX() + mCurrentlyEditedButton.getControlView().getWidth() / 2f < currentDisplayMetrics.widthPixels / 2f; - appear(isAtRight); - } - } - - public void destroy() { - ((ViewGroup) mScrollView.getParent()).removeView(mColorSelector.getRootView()); - ((ViewGroup) mScrollView.getParent()).removeView(mScrollView); - } - - private void loadAdapter() { - //Initialize adapter for keycodes - mAdapter = new ArrayAdapter<>(mRootView.getContext(), R.layout.item_centered_textview); - mSpecialArray = ControlData.buildSpecialButtonArray(); - - mAdapter.addAll(mSpecialArray); - mAdapter.addAll(EfficientAndroidLWJGLKeycode.generateKeyName()); - mAdapter.setDropDownViewResource(android.R.layout.simple_list_item_single_choice); - - for (Spinner spinner : mKeycodeSpinners) { - spinner.setAdapter(mAdapter); - } - - // Orientation spinner - ArrayAdapter adapter = new ArrayAdapter<>(mScrollView.getContext(), android.R.layout.simple_spinner_item); - adapter.addAll(ControlDrawerData.getOrientations()); - adapter.setDropDownViewResource(android.R.layout.simple_list_item_single_choice); - - mOrientationSpinner.setAdapter(adapter); - } - - private void setDefaultVisibilitySetting() { - for (int i = 0; i < mRootView.getChildCount(); ++i) { - mRootView.getChildAt(i).setVisibility(VISIBLE); - } - for(Spinner s : mKeycodeSpinners) { - s.setVisibility(View.INVISIBLE); - } - } - - private boolean isAtRight() { - return mScrollView.getX() > currentDisplayMetrics.widthPixels / 2f; - } - - /* LOADING VALUES */ - - /** - * Load values for basic control data - */ - public void loadValues(ControlData data) { - setDefaultVisibilitySetting(); - mOrientationTextView.setVisibility(GONE); - mOrientationSpinner.setVisibility(GONE); - mForwardLockSwitch.setVisibility(GONE); - mAbsoluteTrackingSwitch.setVisibility(GONE); - - mNameEditText.setText(data.name); - mWidthEditText.setText(String.valueOf(data.getWidth())); - mHeightEditText.setText(String.valueOf(data.getHeight())); - - mAlphaSeekbar.setProgress((int) (data.opacity * 100)); - mStrokeWidthSeekbar.setProgress((int) data.strokeWidth * 10); - mCornerRadiusSeekbar.setProgress((int) data.cornerRadius); - - setPercentageText(mAlphaPercentTextView, (int) (data.opacity * 100)); - setPercentageText(mStrokePercentTextView, (int) data.strokeWidth * 10); - setPercentageText(mCornerRadiusPercentTextView, (int) data.cornerRadius); - - mToggleSwitch.setChecked(data.isToggle); - mPassthroughSwitch.setChecked(data.passThruEnabled); - mSwipeableSwitch.setChecked(data.isSwipeable); - - mDisplayInGameCheckbox.setChecked(data.displayInGame); - mDisplayInMenuCheckbox.setChecked(data.displayInMenu); - - for (int i = 0; i < data.keycodes.length; i++) { - if (data.keycodes[i] < 0) { - mKeycodeSpinners[i].setSelection(data.keycodes[i] + mSpecialArray.size()); - } else { - mKeycodeSpinners[i].setSelection(EfficientAndroidLWJGLKeycode.getIndexByValue(data.keycodes[i]) + mSpecialArray.size()); - } - } - } - - /** - * Load values for extended control data - */ - public void loadValues(ControlDrawerData data) { - loadValues(data.properties); - - mOrientationSpinner.setSelection( - ControlDrawerData.orientationToInt(data.orientation)); - - mMappingTextView.setVisibility(GONE); - for (int i = 0; i < mKeycodeSpinners.length; i++) { - mKeycodeSpinners[i].setVisibility(GONE); - mKeycodeTextviews[i].setVisibility(GONE); - } - - mOrientationTextView.setVisibility(VISIBLE); - mOrientationSpinner.setVisibility(VISIBLE); - - mSwipeableSwitch.setVisibility(View.GONE); - mPassthroughSwitch.setVisibility(View.GONE); - mToggleSwitch.setVisibility(View.GONE); - } - - /** - * Load values for the joystick - */ - public void loadJoystickValues(ControlJoystickData data) { - loadValues(data); - - mMappingTextView.setVisibility(GONE); - for (int i = 0; i < mKeycodeSpinners.length; i++) { - mKeycodeSpinners[i].setVisibility(GONE); - mKeycodeTextviews[i].setVisibility(GONE); - } - - mNameTextView.setVisibility(GONE); - mNameEditText.setVisibility(GONE); - - mCornerRadiusTextView.setVisibility(GONE); - mCornerRadiusSeekbar.setVisibility(GONE); - mCornerRadiusPercentTextView.setVisibility(GONE); - - mSwipeableSwitch.setVisibility(View.GONE); - mPassthroughSwitch.setVisibility(View.GONE); - mToggleSwitch.setVisibility(View.GONE); - - mForwardLockSwitch.setVisibility(VISIBLE); - mForwardLockSwitch.setChecked(data.forwardLock); - - mAbsoluteTrackingSwitch.setVisibility(VISIBLE); - mAbsoluteTrackingSwitch.setChecked(data.absolute); - } - - /** - * Load values for sub buttons - */ - public void loadSubButtonValues(ControlData data, ControlDrawerData.Orientation drawerOrientation) { - loadValues(data); - - // Size linked to the parent drawer depending on the drawer settings - if(drawerOrientation != ControlDrawerData.Orientation.FREE){ - mSizeTextview.setVisibility(GONE); - mSizeXTextView.setVisibility(GONE); - mWidthEditText.setVisibility(GONE); - mHeightEditText.setVisibility(GONE); - } - - // No conditional, already depends on the parent drawer visibility - mVisibilityTextView.setVisibility(GONE); - mDisplayInMenuCheckbox.setVisibility(GONE); - mDisplayInGameCheckbox.setVisibility(GONE); - } - - - private void bindLayout() { - mRootView = mScrollView.findViewById(R.id.edit_layout); - mNameEditText = mScrollView.findViewById(R.id.editName_editText); - mWidthEditText = mScrollView.findViewById(R.id.editSize_editTextX); - mHeightEditText = mScrollView.findViewById(R.id.editSize_editTextY); - mToggleSwitch = mScrollView.findViewById(R.id.checkboxToggle); - mPassthroughSwitch = mScrollView.findViewById(R.id.checkboxPassThrough); - mSwipeableSwitch = mScrollView.findViewById(R.id.checkboxSwipeable); - mForwardLockSwitch = mScrollView.findViewById(R.id.checkboxForwardLock); - mAbsoluteTrackingSwitch = mScrollView.findViewById(R.id.checkboxAbsoluteFingerTracking); - mKeycodeSpinners[0] = mScrollView.findViewById(R.id.editMapping_spinner_1); - mKeycodeSpinners[1] = mScrollView.findViewById(R.id.editMapping_spinner_2); - mKeycodeSpinners[2] = mScrollView.findViewById(R.id.editMapping_spinner_3); - mKeycodeSpinners[3] = mScrollView.findViewById(R.id.editMapping_spinner_4); - mKeycodeTextviews[0] = mScrollView.findViewById(R.id.mapping_1_textview); - mKeycodeTextviews[1] = mScrollView.findViewById(R.id.mapping_2_textview); - mKeycodeTextviews[2] = mScrollView.findViewById(R.id.mapping_3_textview); - mKeycodeTextviews[3] = mScrollView.findViewById(R.id.mapping_4_textview); - mOrientationSpinner = mScrollView.findViewById(R.id.editOrientation_spinner); - mStrokeWidthSeekbar = mScrollView.findViewById(R.id.editStrokeWidth_seekbar); - mCornerRadiusSeekbar = mScrollView.findViewById(R.id.editCornerRadius_seekbar); - mAlphaSeekbar = mScrollView.findViewById(R.id.editButtonOpacity_seekbar); - mSelectBackgroundColor = mScrollView.findViewById(R.id.editBackgroundColor_textView); - mSelectStrokeColor = mScrollView.findViewById(R.id.editStrokeColor_textView); - mStrokePercentTextView = mScrollView.findViewById(R.id.editStrokeWidth_textView_percent); - mAlphaPercentTextView = mScrollView.findViewById(R.id.editButtonOpacity_textView_percent); - mCornerRadiusPercentTextView = mScrollView.findViewById(R.id.editCornerRadius_textView_percent); - mDisplayInGameCheckbox = mScrollView.findViewById(R.id.visibility_game_checkbox); - mDisplayInMenuCheckbox = mScrollView.findViewById(R.id.visibility_menu_checkbox); - - //Decorative stuff - mMappingTextView = mScrollView.findViewById(R.id.editMapping_textView); - mOrientationTextView = mScrollView.findViewById(R.id.editOrientation_textView); - mNameTextView = mScrollView.findViewById(R.id.editName_textView); - mCornerRadiusTextView = mScrollView.findViewById(R.id.editCornerRadius_textView); - mVisibilityTextView = mScrollView.findViewById(R.id.visibility_textview); - mSizeTextview = mScrollView.findViewById(R.id.editSize_textView); - mSizeXTextView = mScrollView.findViewById(R.id.editSize_x_textView); - } - - /** - * A long function linking all the displayed data on the popup and, - * the currently edited mCurrentlyEditedButton - */ - public void setupRealTimeListeners() { - mNameEditText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - if (internalChanges) return; - - mCurrentlyEditedButton.getProperties().name = s.toString(); - - // Cheap and unoptimized, doesn't break the abstraction layer - mCurrentlyEditedButton.setProperties(mCurrentlyEditedButton.getProperties(), false); - } - }); - - mWidthEditText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - if (internalChanges) return; - - float width = safeParseFloat(s.toString()); - if (width >= 0) { - mCurrentlyEditedButton.getProperties().setWidth(width); - mCurrentlyEditedButton.updateProperties(); - } - } - }); - - mHeightEditText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - if (internalChanges) return; - - float height = safeParseFloat(s.toString()); - if (height >= 0) { - mCurrentlyEditedButton.getProperties().setHeight(height); - mCurrentlyEditedButton.updateProperties(); - } - } - }); - - mSwipeableSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (internalChanges) return; - mCurrentlyEditedButton.getProperties().isSwipeable = isChecked; - }); - mToggleSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (internalChanges) return; - mCurrentlyEditedButton.getProperties().isToggle = isChecked; - }); - mPassthroughSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (internalChanges) return; - mCurrentlyEditedButton.getProperties().passThruEnabled = isChecked; - }); - mForwardLockSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (internalChanges) return; - if(mCurrentlyEditedButton.getProperties() instanceof ControlJoystickData){ - ((ControlJoystickData) mCurrentlyEditedButton.getProperties()).forwardLock = isChecked; - } - }); - mAbsoluteTrackingSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (internalChanges) return; - if(mCurrentlyEditedButton.getProperties() instanceof ControlJoystickData){ - ((ControlJoystickData) mCurrentlyEditedButton.getProperties()).absolute = isChecked; - } - }); - - mAlphaSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (internalChanges) return; - mCurrentlyEditedButton.getProperties().opacity = mAlphaSeekbar.getProgress() / 100f; - mCurrentlyEditedButton.getControlView().setAlpha(mAlphaSeekbar.getProgress() / 100f); - setPercentageText(mAlphaPercentTextView, progress); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - }); - - mStrokeWidthSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (internalChanges) return; - mCurrentlyEditedButton.getProperties().strokeWidth = mStrokeWidthSeekbar.getProgress() / 10F; - mCurrentlyEditedButton.setBackground(); - setPercentageText(mStrokePercentTextView, progress); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - }); - - mCornerRadiusSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (internalChanges) return; - mCurrentlyEditedButton.getProperties().cornerRadius = mCornerRadiusSeekbar.getProgress(); - mCurrentlyEditedButton.setBackground(); - setPercentageText(mCornerRadiusPercentTextView, progress); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - }); - - - for (int i = 0; i < mKeycodeSpinners.length; ++i) { - int finalI = i; - mKeycodeTextviews[i].setOnClickListener(v -> mKeycodeSpinners[finalI].performClick()); - - mKeycodeSpinners[i].setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - // Side note, spinner listeners are fired later than all the other ones. - // Meaning the internalChanges bool is useless here. - if (position < mSpecialArray.size()) { - mCurrentlyEditedButton.getProperties().keycodes[finalI] = mKeycodeSpinners[finalI].getSelectedItemPosition() - mSpecialArray.size(); - } else { - mCurrentlyEditedButton.getProperties().keycodes[finalI] = EfficientAndroidLWJGLKeycode.getValueByIndex(mKeycodeSpinners[finalI].getSelectedItemPosition() - mSpecialArray.size()); - } - mKeycodeTextviews[finalI].setText((String) mKeycodeSpinners[finalI].getSelectedItem()); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); - } - - - mOrientationSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - // Side note, spinner listeners are fired later than all the other ones. - // Meaning the internalChanges bool is useless here. - - if (mCurrentlyEditedButton instanceof ControlDrawer) { - ((ControlDrawer) mCurrentlyEditedButton).drawerData.orientation = ControlDrawerData.intToOrientation(mOrientationSpinner.getSelectedItemPosition()); - ((ControlDrawer) mCurrentlyEditedButton).syncButtons(); - } - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); - - mDisplayInGameCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (internalChanges) return; - mCurrentlyEditedButton.getProperties().displayInGame = isChecked; - }); - - mDisplayInMenuCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (internalChanges) return; - mCurrentlyEditedButton.getProperties().displayInMenu = isChecked; - }); - - mSelectStrokeColor.setOnClickListener(v -> { - mColorSelector.setAlphaEnabled(false); - mColorSelector.setColorSelectionListener(color -> { - mCurrentlyEditedButton.getProperties().strokeColor = color; - mCurrentlyEditedButton.setBackground(); - }); - appearColor(isAtRight(), mCurrentlyEditedButton.getProperties().strokeColor); - }); - - mSelectBackgroundColor.setOnClickListener(v -> { - mColorSelector.setAlphaEnabled(true); - mColorSelector.setColorSelectionListener(color -> { - mCurrentlyEditedButton.getProperties().bgColor = color; - mCurrentlyEditedButton.setBackground(); - }); - appearColor(isAtRight(), mCurrentlyEditedButton.getProperties().bgColor); - }); - } - - private float safeParseFloat(String string) { - float out = -1; // -1 - try { - out = Float.parseFloat(string); - } catch (NumberFormatException e) { - Log.e("EditControlPopup", e.toString()); - } - return out; - } - - public void setCurrentlyEditedButton(ControlInterface button) { - if (mCurrentlyEditedButton != null) - mCurrentlyEditedButton.getControlView().removeOnLayoutChangeListener(mLayoutChangedListener); - mCurrentlyEditedButton = button; - mCurrentlyEditedButton.getControlView().addOnLayoutChangeListener(mLayoutChangedListener); - } -} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/EditControlSideDialog.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/EditControlSideDialog.java new file mode 100644 index 0000000000..14bf4dde4f --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/EditControlSideDialog.java @@ -0,0 +1,486 @@ +package net.kdt.pojavlaunch.customcontrols.handleview; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Color; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.SeekBar; +import android.widget.Spinner; +import android.widget.Switch; +import android.widget.TextView; + +import com.kdt.SideDialogView; + +import net.kdt.pojavlaunch.EfficientAndroidLWJGLKeycode; +import net.kdt.pojavlaunch.R; +import net.kdt.pojavlaunch.Tools; +import net.kdt.pojavlaunch.colorselector.ColorSelector; +import net.kdt.pojavlaunch.customcontrols.ControlData; +import net.kdt.pojavlaunch.customcontrols.ControlDrawerData; +import net.kdt.pojavlaunch.customcontrols.ControlJoystickData; +import net.kdt.pojavlaunch.customcontrols.buttons.ControlDrawer; +import net.kdt.pojavlaunch.customcontrols.buttons.ControlInterface; +import net.kdt.pojavlaunch.utils.interfaces.SimpleItemSelectedListener; +import net.kdt.pojavlaunch.utils.interfaces.SimpleSeekBarListener; +import net.kdt.pojavlaunch.utils.interfaces.SimpleTextWatcher; + +import java.util.List; + +public class EditControlSideDialog extends SideDialogView { + + private final Spinner[] mKeycodeSpinners = new Spinner[4]; + public boolean internalChanges = false; // True when we programmatically change stuff. + private final View.OnLayoutChangeListener mLayoutChangedListener = new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + if (internalChanges) return; + + internalChanges = true; + int width = (int) (safeParseFloat(mWidthEditText.getText().toString())); + + if (width >= 0 && Math.abs(right - width) > 1) { + mWidthEditText.setText(String.valueOf(right - left)); + } + int height = (int) (safeParseFloat(mHeightEditText.getText().toString())); + if (height >= 0 && Math.abs(bottom - height) > 1) { + mHeightEditText.setText(String.valueOf(bottom - top)); + } + + internalChanges = false; + } + }; + private EditText mNameEditText, mWidthEditText, mHeightEditText; + @SuppressLint("UseSwitchCompatOrMaterialCode") + private Switch mToggleSwitch, mPassthroughSwitch, mSwipeableSwitch, mForwardLockSwitch, mAbsoluteTrackingSwitch; + private Spinner mOrientationSpinner; + private final TextView[] mKeycodeTextviews = new TextView[4]; + private SeekBar mStrokeWidthSeekbar, mCornerRadiusSeekbar, mAlphaSeekbar; + private TextView mStrokePercentTextView, mCornerRadiusPercentTextView, mAlphaPercentTextView; + private TextView mSelectBackgroundColor, mSelectStrokeColor; + private ArrayAdapter mAdapter; + private List mSpecialArray; + private CheckBox mDisplayInGameCheckbox, mDisplayInMenuCheckbox; + private ControlInterface mCurrentlyEditedButton; + // Decorative textviews + private TextView mOrientationTextView, mMappingTextView, mNameTextView, + mCornerRadiusTextView, mVisibilityTextView, mSizeTextview, mSizeXTextView; + + // Color selector related stuff + private ColorSelector mColorSelector; + private final ViewGroup mParent; + + public EditControlSideDialog(Context context, ViewGroup parent) { + super(context, parent, R.layout.dialog_control_button_setting); + mParent = parent; + } + + @Override + protected void onInflate() { + bindLayout(); + buildColorSelector(); + loadAdapter(); + setupRealTimeListeners(); + } + + @Override + protected void onDestroy() { + mColorSelector.disappear(true); + } + + private void buildColorSelector() { + mColorSelector = new ColorSelector(mParent.getContext(), mParent, null); + } + + /** + * Slide the layout into the visible screen area + */ + public void appearColor(boolean fromRight, int color) { + mColorSelector.show(fromRight, color == -1 ? Color.WHITE : color); + } + + /** + * Slide out the layout + */ + public void disappearColor() { + mColorSelector.disappear(false); + } + + /** + * Slide out the first visible layer. + * + * @return True if the last layer is disappearing + */ + public boolean disappearLayer() { + if (mColorSelector.isDisplaying()) { + disappearColor(); + return false; + } else { + disappear(false); + return true; + } + } + + /** + * Switch the panels position if needed + */ + public void adaptPanelPosition() { + if (mDisplaying) { + boolean isAtRight = mCurrentlyEditedButton.getControlView().getX() + mCurrentlyEditedButton.getControlView().getWidth() / 2f < currentDisplayMetrics.widthPixels / 2f; + appear(isAtRight); + if (mColorSelector.isDisplaying()) { + Tools.runOnUiThread(() -> appearColor(isAtRight, mCurrentlyEditedButton.getProperties().bgColor)); + } + } + } + + public static void setPercentageText(TextView textView, int progress) { + textView.setText(textView.getContext().getString(R.string.percent_format, progress)); + } + + /* LOADING VALUES */ + + /** + * Load values for basic control data + */ + public void loadValues(ControlData data) { + setDefaultVisibilitySetting(); + mOrientationTextView.setVisibility(GONE); + mOrientationSpinner.setVisibility(GONE); + mForwardLockSwitch.setVisibility(GONE); + mAbsoluteTrackingSwitch.setVisibility(GONE); + + mNameEditText.setText(data.name); + mWidthEditText.setText(String.valueOf(data.getWidth())); + mHeightEditText.setText(String.valueOf(data.getHeight())); + + mAlphaSeekbar.setProgress((int) (data.opacity * 100)); + mStrokeWidthSeekbar.setProgress((int) data.strokeWidth * 10); + mCornerRadiusSeekbar.setProgress((int) data.cornerRadius); + + setPercentageText(mAlphaPercentTextView, (int) (data.opacity * 100)); + setPercentageText(mStrokePercentTextView, (int) data.strokeWidth * 10); + setPercentageText(mCornerRadiusPercentTextView, (int) data.cornerRadius); + + mToggleSwitch.setChecked(data.isToggle); + mPassthroughSwitch.setChecked(data.passThruEnabled); + mSwipeableSwitch.setChecked(data.isSwipeable); + + mDisplayInGameCheckbox.setChecked(data.displayInGame); + mDisplayInMenuCheckbox.setChecked(data.displayInMenu); + + for (int i = 0; i < data.keycodes.length; i++) { + if (data.keycodes[i] < 0) { + mKeycodeSpinners[i].setSelection(data.keycodes[i] + mSpecialArray.size()); + } else { + mKeycodeSpinners[i].setSelection(EfficientAndroidLWJGLKeycode.getIndexByValue(data.keycodes[i]) + mSpecialArray.size()); + } + } + } + + /** + * Load values for extended control data + */ + public void loadValues(ControlDrawerData data) { + loadValues(data.properties); + + mOrientationSpinner.setSelection( + ControlDrawerData.orientationToInt(data.orientation)); + + mMappingTextView.setVisibility(GONE); + for (int i = 0; i < mKeycodeSpinners.length; i++) { + mKeycodeSpinners[i].setVisibility(GONE); + mKeycodeTextviews[i].setVisibility(GONE); + } + + mOrientationTextView.setVisibility(VISIBLE); + mOrientationSpinner.setVisibility(VISIBLE); + + mSwipeableSwitch.setVisibility(View.GONE); + mPassthroughSwitch.setVisibility(View.GONE); + mToggleSwitch.setVisibility(View.GONE); + } + + /** + * Load values for the joystick + */ + public void loadJoystickValues(ControlJoystickData data) { + loadValues(data); + + mMappingTextView.setVisibility(GONE); + for (int i = 0; i < mKeycodeSpinners.length; i++) { + mKeycodeSpinners[i].setVisibility(GONE); + mKeycodeTextviews[i].setVisibility(GONE); + } + + mNameTextView.setVisibility(GONE); + mNameEditText.setVisibility(GONE); + + mCornerRadiusTextView.setVisibility(GONE); + mCornerRadiusSeekbar.setVisibility(GONE); + mCornerRadiusPercentTextView.setVisibility(GONE); + + mSwipeableSwitch.setVisibility(View.GONE); + mPassthroughSwitch.setVisibility(View.GONE); + mToggleSwitch.setVisibility(View.GONE); + + mForwardLockSwitch.setVisibility(VISIBLE); + mForwardLockSwitch.setChecked(data.forwardLock); + + mAbsoluteTrackingSwitch.setVisibility(VISIBLE); + mAbsoluteTrackingSwitch.setChecked(data.absolute); + } + + /** + * Load values for sub buttons + */ + public void loadSubButtonValues(ControlData data, ControlDrawerData.Orientation drawerOrientation) { + loadValues(data); + + // Size linked to the parent drawer depending on the drawer settings + if(drawerOrientation != ControlDrawerData.Orientation.FREE){ + mSizeTextview.setVisibility(GONE); + mSizeXTextView.setVisibility(GONE); + mWidthEditText.setVisibility(GONE); + mHeightEditText.setVisibility(GONE); + } + + // No conditional, already depends on the parent drawer visibility + mVisibilityTextView.setVisibility(GONE); + mDisplayInMenuCheckbox.setVisibility(GONE); + mDisplayInGameCheckbox.setVisibility(GONE); + } + + private void loadAdapter() { + //Initialize adapter for keycodes + mAdapter = new ArrayAdapter<>(mDialogContent.getContext(), R.layout.item_centered_textview); + mSpecialArray = ControlData.buildSpecialButtonArray(); + + mAdapter.addAll(mSpecialArray); + mAdapter.addAll(EfficientAndroidLWJGLKeycode.generateKeyName()); + mAdapter.setDropDownViewResource(android.R.layout.simple_list_item_single_choice); + + for (Spinner spinner : mKeycodeSpinners) { + spinner.setAdapter(mAdapter); + } + + // Orientation spinner + ArrayAdapter adapter = new ArrayAdapter<>(mDialogContent.getContext(), android.R.layout.simple_spinner_item); + adapter.addAll(ControlDrawerData.getOrientations()); + adapter.setDropDownViewResource(android.R.layout.simple_list_item_single_choice); + + mOrientationSpinner.setAdapter(adapter); + } + + private void setDefaultVisibilitySetting() { + for (int i = 0; i < ((ViewGroup)mDialogContent).getChildCount(); ++i) { + ((ViewGroup)mDialogContent).getChildAt(i).setVisibility(VISIBLE); + } + for(Spinner s : mKeycodeSpinners) { + s.setVisibility(View.INVISIBLE); + } + } + + private void bindLayout() { + mNameEditText = mDialogContent.findViewById(R.id.editName_editText); + mWidthEditText = mDialogContent.findViewById(R.id.editSize_editTextX); + mHeightEditText = mDialogContent.findViewById(R.id.editSize_editTextY); + mToggleSwitch = mDialogContent.findViewById(R.id.checkboxToggle); + mPassthroughSwitch = mDialogContent.findViewById(R.id.checkboxPassThrough); + mSwipeableSwitch = mDialogContent.findViewById(R.id.checkboxSwipeable); + mForwardLockSwitch = mDialogContent.findViewById(R.id.checkboxForwardLock); + mAbsoluteTrackingSwitch = mDialogContent.findViewById(R.id.checkboxAbsoluteFingerTracking); + mKeycodeSpinners[0] = mDialogContent.findViewById(R.id.editMapping_spinner_1); + mKeycodeSpinners[1] = mDialogContent.findViewById(R.id.editMapping_spinner_2); + mKeycodeSpinners[2] = mDialogContent.findViewById(R.id.editMapping_spinner_3); + mKeycodeSpinners[3] = mDialogContent.findViewById(R.id.editMapping_spinner_4); + mKeycodeTextviews[0] = mDialogContent.findViewById(R.id.mapping_1_textview); + mKeycodeTextviews[1] = mDialogContent.findViewById(R.id.mapping_2_textview); + mKeycodeTextviews[2] = mDialogContent.findViewById(R.id.mapping_3_textview); + mKeycodeTextviews[3] = mDialogContent.findViewById(R.id.mapping_4_textview); + mOrientationSpinner = mDialogContent.findViewById(R.id.editOrientation_spinner); + mStrokeWidthSeekbar = mDialogContent.findViewById(R.id.editStrokeWidth_seekbar); + mCornerRadiusSeekbar = mDialogContent.findViewById(R.id.editCornerRadius_seekbar); + mAlphaSeekbar = mDialogContent.findViewById(R.id.editButtonOpacity_seekbar); + mSelectBackgroundColor = mDialogContent.findViewById(R.id.editBackgroundColor_textView); + mSelectStrokeColor = mDialogContent.findViewById(R.id.editStrokeColor_textView); + mStrokePercentTextView = mDialogContent.findViewById(R.id.editStrokeWidth_textView_percent); + mAlphaPercentTextView = mDialogContent.findViewById(R.id.editButtonOpacity_textView_percent); + mCornerRadiusPercentTextView = mDialogContent.findViewById(R.id.editCornerRadius_textView_percent); + mDisplayInGameCheckbox = mDialogContent.findViewById(R.id.visibility_game_checkbox); + mDisplayInMenuCheckbox = mDialogContent.findViewById(R.id.visibility_menu_checkbox); + + //Decorative stuff + mMappingTextView = mDialogContent.findViewById(R.id.editMapping_textView); + mOrientationTextView = mDialogContent.findViewById(R.id.editOrientation_textView); + mNameTextView = mDialogContent.findViewById(R.id.editName_textView); + mCornerRadiusTextView = mDialogContent.findViewById(R.id.editCornerRadius_textView); + mVisibilityTextView = mDialogContent.findViewById(R.id.visibility_textview); + mSizeTextview = mDialogContent.findViewById(R.id.editSize_textView); + mSizeXTextView = mDialogContent.findViewById(R.id.editSize_x_textView); + } + + /** + * A long function linking all the displayed data on the popup and, + * the currently edited mCurrentlyEditedButton + */ + private void setupRealTimeListeners() { + mNameEditText.addTextChangedListener((SimpleTextWatcher) s -> { + if (internalChanges) return; + + mCurrentlyEditedButton.getProperties().name = s.toString(); + + // Cheap and unoptimized, doesn't break the abstraction layer + mCurrentlyEditedButton.setProperties(mCurrentlyEditedButton.getProperties(), false); + }); + + mWidthEditText.addTextChangedListener((SimpleTextWatcher) s -> { + if (internalChanges) return; + + float width = safeParseFloat(s.toString()); + if (width >= 0) { + mCurrentlyEditedButton.getProperties().setWidth(width); + mCurrentlyEditedButton.updateProperties(); + } + }); + + mHeightEditText.addTextChangedListener((SimpleTextWatcher) s -> { + if (internalChanges) return; + + float height = safeParseFloat(s.toString()); + if (height >= 0) { + mCurrentlyEditedButton.getProperties().setHeight(height); + mCurrentlyEditedButton.updateProperties(); + } + }); + + mSwipeableSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (internalChanges) return; + mCurrentlyEditedButton.getProperties().isSwipeable = isChecked; + }); + mToggleSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (internalChanges) return; + mCurrentlyEditedButton.getProperties().isToggle = isChecked; + }); + mPassthroughSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (internalChanges) return; + mCurrentlyEditedButton.getProperties().passThruEnabled = isChecked; + }); + mForwardLockSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (internalChanges) return; + if(mCurrentlyEditedButton.getProperties() instanceof ControlJoystickData){ + ((ControlJoystickData) mCurrentlyEditedButton.getProperties()).forwardLock = isChecked; + } + }); + mAbsoluteTrackingSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (internalChanges) return; + if(mCurrentlyEditedButton.getProperties() instanceof ControlJoystickData){ + ((ControlJoystickData) mCurrentlyEditedButton.getProperties()).absolute = isChecked; + } + }); + + mAlphaSeekbar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> { + if (internalChanges) return; + mCurrentlyEditedButton.getProperties().opacity = mAlphaSeekbar.getProgress() / 100f; + mCurrentlyEditedButton.getControlView().setAlpha(mAlphaSeekbar.getProgress() / 100f); + setPercentageText(mAlphaPercentTextView, progress); + }); + + mStrokeWidthSeekbar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> { + if (internalChanges) return; + mCurrentlyEditedButton.getProperties().strokeWidth = mStrokeWidthSeekbar.getProgress() / 10F; + mCurrentlyEditedButton.setBackground(); + setPercentageText(mStrokePercentTextView, progress); + }); + + mCornerRadiusSeekbar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> { + if (internalChanges) return; + mCurrentlyEditedButton.getProperties().cornerRadius = mCornerRadiusSeekbar.getProgress(); + mCurrentlyEditedButton.setBackground(); + setPercentageText(mCornerRadiusPercentTextView, progress); + }); + + + for (int i = 0; i < mKeycodeSpinners.length; ++i) { + int finalI = i; + mKeycodeTextviews[i].setOnClickListener(v -> mKeycodeSpinners[finalI].performClick()); + + mKeycodeSpinners[i].setOnItemSelectedListener((SimpleItemSelectedListener) (parent, view, position, id) -> { + // Side note, spinner listeners are fired later than all the other ones. + // Meaning the internalChanges bool is useless here. + if (position < mSpecialArray.size()) { + mCurrentlyEditedButton.getProperties().keycodes[finalI] = mKeycodeSpinners[finalI].getSelectedItemPosition() - mSpecialArray.size(); + } else { + mCurrentlyEditedButton.getProperties().keycodes[finalI] = EfficientAndroidLWJGLKeycode.getValueByIndex(mKeycodeSpinners[finalI].getSelectedItemPosition() - mSpecialArray.size()); + } + mKeycodeTextviews[finalI].setText((String) mKeycodeSpinners[finalI].getSelectedItem()); + }); + } + + + mOrientationSpinner.setOnItemSelectedListener((SimpleItemSelectedListener) (parent, view, position, id) -> { + // Side note, spinner listeners are fired later than all the other ones. + // Meaning the internalChanges bool is useless here. + + if (mCurrentlyEditedButton instanceof ControlDrawer) { + ((ControlDrawer) mCurrentlyEditedButton).drawerData.orientation = ControlDrawerData.intToOrientation(mOrientationSpinner.getSelectedItemPosition()); + ((ControlDrawer) mCurrentlyEditedButton).syncButtons(); + } + }); + + mDisplayInGameCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (internalChanges) return; + mCurrentlyEditedButton.getProperties().displayInGame = isChecked; + }); + + mDisplayInMenuCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (internalChanges) return; + mCurrentlyEditedButton.getProperties().displayInMenu = isChecked; + }); + + mSelectStrokeColor.setOnClickListener(v -> { + mColorSelector.setAlphaEnabled(false); + mColorSelector.setColorSelectionListener(color -> { + mCurrentlyEditedButton.getProperties().strokeColor = color; + mCurrentlyEditedButton.setBackground(); + }); + appearColor(isAtRight(), mCurrentlyEditedButton.getProperties().strokeColor); + }); + + mSelectBackgroundColor.setOnClickListener(v -> { + mColorSelector.setAlphaEnabled(true); + mColorSelector.setColorSelectionListener(color -> { + mCurrentlyEditedButton.getProperties().bgColor = color; + mCurrentlyEditedButton.setBackground(); + }); + appearColor(isAtRight(), mCurrentlyEditedButton.getProperties().bgColor); + }); + } + + private float safeParseFloat(String string) { + float out = -1; // -1 + try { + out = Float.parseFloat(string); + } catch (NumberFormatException e) { + Log.e("EditControlPopup", e.toString()); + } + return out; + } + + public void setCurrentlyEditedButton(ControlInterface button) { + if (mCurrentlyEditedButton != null) + mCurrentlyEditedButton.getControlView().removeOnLayoutChangeListener(mLayoutChangedListener); + mCurrentlyEditedButton = button; + mCurrentlyEditedButton.getControlView().addOnLayoutChangeListener(mLayoutChangedListener); + } + +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/AndroidPointerCapture.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/AndroidPointerCapture.java index ec512ee0a0..38f6dd4ca7 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/AndroidPointerCapture.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/AndroidPointerCapture.java @@ -8,9 +8,9 @@ import androidx.annotation.RequiresApi; -import net.kdt.pojavlaunch.MainActivity; import net.kdt.pojavlaunch.MinecraftGLSurface; import net.kdt.pojavlaunch.Tools; +import net.kdt.pojavlaunch.prefs.LauncherPreferences; import org.lwjgl.glfw.CallbackBridge; @@ -19,7 +19,6 @@ public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChan private static final float TOUCHPAD_SCROLL_THRESHOLD = 1; private final AbstractTouchpad mTouchpad; private final View mHostView; - private final float mScaleFactor; private final float mMousePrescale = Tools.dpToPx(1); private final PointerTracker mPointerTracker = new PointerTracker(); private final Scroller mScroller = new Scroller(TOUCHPAD_SCROLL_THRESHOLD); @@ -28,8 +27,7 @@ public class AndroidPointerCapture implements ViewTreeObserver.OnWindowFocusChan private int mInputDeviceIdentifier; private boolean mDeviceSupportsRelativeAxis; - public AndroidPointerCapture(AbstractTouchpad touchpad, View hostView, float scaleFactor) { - this.mScaleFactor = scaleFactor; + public AndroidPointerCapture(AbstractTouchpad touchpad, View hostView) { this.mTouchpad = touchpad; this.mHostView = hostView; hostView.setOnCapturedPointerListener(this); @@ -87,8 +85,8 @@ public boolean onCapturedPointer(View view, MotionEvent event) { } } else { // Position is updated by many events, hence it is send regardless of the event value - CallbackBridge.mouseX += (mVector[0] * mScaleFactor); - CallbackBridge.mouseY += (mVector[1] * mScaleFactor); + CallbackBridge.mouseX += (mVector[0] * LauncherPreferences.PREF_SCALE_FACTOR); + CallbackBridge.mouseY += (mVector[1] * LauncherPreferences.PREF_SCALE_FACTOR); CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY); } @@ -130,7 +128,7 @@ private void reinitializeDeviceSpecificProperties(InputDevice inputDevice) { @Override public void onWindowFocusChanged(boolean hasFocus) { - if(hasFocus && MainActivity.isAndroid8OrHigher()) mHostView.requestPointerCapture(); + if(hasFocus && Tools.isAndroid8OrHigher()) mHostView.requestPointerCapture(); } public void detach() { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/GyroControl.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/GyroControl.java index 4ead091c13..ba0f7ec44b 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/GyroControl.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/GyroControl.java @@ -21,6 +21,9 @@ public class GyroControl implements SensorEventListener, GrabListener { /* How much distance has to be moved before taking into account the gyro */ private static final float SINGLE_AXIS_LOW_PASS_THRESHOLD = 1.13F; private static final float MULTI_AXIS_LOW_PASS_THRESHOLD = 1.3F; + // Warmup period of 2 since the first read from the sensor seems to produce a bogus value, + // which creates a far too large of a difference on the Y axis once actual sensor data comes in + private static final int ROTATION_VECTOR_WARMUP_PERIOD = 2; private final WindowManager mWindowManager; private int mSurfaceRotation; @@ -28,7 +31,7 @@ public class GyroControl implements SensorEventListener, GrabListener { private final Sensor mSensor; private final OrientationCorrectionListener mCorrectionListener; private boolean mShouldHandleEvents; - private boolean mFirstPass; + private int mWarmup; private float xFactor; // -1 or 1 depending on device orientation private float yFactor; private boolean mSwapXY; @@ -64,7 +67,7 @@ public GyroControl(Activity activity) { public void enable() { if(mSensor == null) return; - mFirstPass = true; + mWarmup = ROTATION_VECTOR_WARMUP_PERIOD; mSensorManager.registerListener(this, mSensor, 1000 * LauncherPreferences.PREF_GYRO_SAMPLE_RATE); mCorrectionListener.enable(); mShouldHandleEvents = CallbackBridge.isGrabbing(); @@ -75,6 +78,7 @@ public void disable() { if(mSensor == null) return; mSensorManager.unregisterListener(this); mCorrectionListener.disable(); + mStoredX = mStoredY = 0; resetDamper(); CallbackBridge.removeGrabListener(this); } @@ -87,8 +91,8 @@ public void onSensorChanged(SensorEvent sensorEvent) { SensorManager.getRotationMatrixFromVector(mCurrentRotation, sensorEvent.values); - if(mFirstPass){ // Setup initial position - mFirstPass = false; + if(mWarmup > 0){ // Setup initial position + mWarmup--; return; } SensorManager.getAngleChange(mAngleDifference, mCurrentRotation, mPreviousRotation); @@ -161,7 +165,7 @@ public void onAccuracyChanged(Sensor sensor, int i) {} @Override public void onGrabState(boolean isGrabbing) { - mFirstPass = true; + mWarmup = ROTATION_VECTOR_WARMUP_PERIOD; mShouldHandleEvents = isGrabbing; } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarView.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarView.java index cd2ff5d1ce..522b0fe5b8 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarView.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/HotbarView.java @@ -26,7 +26,6 @@ public class HotbarView extends View implements MCOptionUtils.MCOptionListener, LwjglGlfwKeycode.GLFW_KEY_4, LwjglGlfwKeycode.GLFW_KEY_5, LwjglGlfwKeycode.GLFW_KEY_6, LwjglGlfwKeycode.GLFW_KEY_7, LwjglGlfwKeycode.GLFW_KEY_8, LwjglGlfwKeycode.GLFW_KEY_9}; private final DropGesture mDropGesture = new DropGesture(new Handler(Looper.getMainLooper())); - private final float mScaleFactor = LauncherPreferences.PREF_SCALE_FACTOR/100f; private int mWidth; private int mLastIndex; private int mGuiScale; @@ -122,7 +121,14 @@ private boolean isLastEventInGesture(int actionMasked) { } private int mcScale(int input) { - return (int)((mGuiScale * input)/ mScaleFactor); + return (int)((mGuiScale * input) / LauncherPreferences.PREF_SCALE_FACTOR); + } + + /** Forces the view to reposition itself. */ + public void onResolutionChanged() { + if(getParent() == null) return; + mGuiScale = MCOptionUtils.getMcScale(); + post(this::repositionView); } @Override diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGUIEventProcessor.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGUIEventProcessor.java index 16369048a2..21958d6afa 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGUIEventProcessor.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGUIEventProcessor.java @@ -19,12 +19,10 @@ public class InGUIEventProcessor implements TouchEventProcessor { private AbstractTouchpad mTouchpad; private boolean mIsMouseDown = false; private float mStartX, mStartY; - private final float mScaleFactor; private final Scroller mScroller = new Scroller(FINGER_SCROLL_THRESHOLD); - public InGUIEventProcessor(float scaleFactor) { + public InGUIEventProcessor() { mSingleTapDetector = new TapDetector(1, TapDetector.DETECTION_METHOD_BOTH); - mScaleFactor = scaleFactor; } @Override @@ -91,7 +89,7 @@ public void setAbstractTouchpad(AbstractTouchpad touchpad) { } private void sendTouchCoordinates(float x, float y) { - CallbackBridge.sendCursorPos( x * mScaleFactor, y * mScaleFactor); + CallbackBridge.sendCursorPos( x * LauncherPreferences.PREF_SCALE_FACTOR, y * LauncherPreferences.PREF_SCALE_FACTOR); } private void enableMouse() { @@ -105,8 +103,8 @@ private void disableMouse() { } private void setGestureStart(MotionEvent event) { - mStartX = event.getX() * mScaleFactor; - mStartY = event.getY() * mScaleFactor; + mStartX = event.getX() * LauncherPreferences.PREF_SCALE_FACTOR; + mStartY = event.getY() * LauncherPreferences.PREF_SCALE_FACTOR; } private void resetGesture() { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGameEventProcessor.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGameEventProcessor.java index a5838a458f..f4e5fe7639 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGameEventProcessor.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/InGameEventProcessor.java @@ -2,6 +2,7 @@ import android.os.Handler; import android.os.Looper; +import android.util.Log; import android.view.MotionEvent; import net.kdt.pojavlaunch.prefs.LauncherPreferences; @@ -32,8 +33,8 @@ public boolean processTouchEvent(MotionEvent motionEvent) { case MotionEvent.ACTION_MOVE: mTracker.trackEvent(motionEvent); float[] motionVector = mTracker.getMotionVector(); - CallbackBridge.mouseX += motionVector[0] * mSensitivity; - CallbackBridge.mouseY += motionVector[1] * mSensitivity; + CallbackBridge.mouseX += (float) (motionVector[0] * mSensitivity); + CallbackBridge.mouseY += (float) (motionVector[1] * mSensitivity); CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY); if(LauncherPreferences.PREF_DISABLE_GESTURES) break; checkGestures(); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/Touchpad.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/Touchpad.java index 1feee772be..7a1f71d861 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/Touchpad.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/Touchpad.java @@ -1,7 +1,6 @@ package net.kdt.pojavlaunch.customcontrols.mouse; import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics; -import static net.kdt.pojavlaunch.prefs.LauncherPreferences.DEFAULT_PREF; import android.content.Context; import android.graphics.Canvas; @@ -29,8 +28,6 @@ public class Touchpad extends View implements GrabListener, AbstractTouchpad { /* Mouse pointer icon used by the touchpad */ private Drawable mMousePointerDrawable; private float mMouseX, mMouseY; - /* Resolution scaler option, allow downsizing a window */ - private final float mScaleFactor = DEFAULT_PREF.getInt("resolutionRatio",100)/100f; public Touchpad(@NonNull Context context) { this(context, null); } @@ -68,7 +65,7 @@ public void placeMouseAt(float x, float y) { } private void sendMousePosition() { - CallbackBridge.sendCursorPos((mMouseX * mScaleFactor), (mMouseY * mScaleFactor)); + CallbackBridge.sendCursorPos((mMouseX * LauncherPreferences.PREF_SCALE_FACTOR), (mMouseY * LauncherPreferences.PREF_SCALE_FACTOR)); } private void updateMousePosition() { @@ -93,8 +90,8 @@ private void init(){ assert mMousePointerDrawable != null; mMousePointerDrawable.setBounds( 0, 0, - (int) (36 / 100f * LauncherPreferences.PREF_MOUSESCALE), - (int) (54 / 100f * LauncherPreferences.PREF_MOUSESCALE) + (int) (36 * LauncherPreferences.PREF_MOUSESCALE), + (int) (54 * LauncherPreferences.PREF_MOUSESCALE) ); setFocusable(false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java index 15d54a4842..5e73275034 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/LauncherPreferences.java @@ -36,7 +36,7 @@ public class LauncherPreferences { public static boolean PREF_IGNORE_NOTCH = false; public static int PREF_NOTCH_SIZE = 0; public static float PREF_BUTTONSIZE = 100f; - public static float PREF_MOUSESCALE = 100f; + public static float PREF_MOUSESCALE = 1f; public static int PREF_LONGPRESS_TRIGGER = 300; public static String PREF_DEFAULTCTRL_PATH = Tools.CTRLDEF_FILE; public static String PREF_CUSTOM_JAVA_ARGS; @@ -53,7 +53,7 @@ public class LauncherPreferences { public static boolean PREF_ARC_CAPES = false; public static boolean PREF_USE_ALTERNATE_SURFACE = true; public static boolean PREF_JAVA_SANDBOX = true; - public static int PREF_SCALE_FACTOR = 100; + public static float PREF_SCALE_FACTOR = 1f; public static boolean PREF_ENABLE_GYRO = false; public static float PREF_GYRO_SENSITIVITY = 1f; public static int PREF_GYRO_SAMPLE_RATE = 16; @@ -82,7 +82,7 @@ public static void loadPreferences(Context ctx) { PREF_RENDERER = DEFAULT_PREF.getString("renderer", "opengles2"); PREF_BUTTONSIZE = DEFAULT_PREF.getInt("buttonscale", 100); - PREF_MOUSESCALE = DEFAULT_PREF.getInt("mousescale", 100); + PREF_MOUSESCALE = DEFAULT_PREF.getInt("mousescale", 100)/100f; PREF_MOUSESPEED = ((float)DEFAULT_PREF.getInt("mousespeed",100))/100f; PREF_HIDE_SIDEBAR = DEFAULT_PREF.getBoolean("hideSidebar", false); PREF_IGNORE_NOTCH = DEFAULT_PREF.getBoolean("ignoreNotch", false); @@ -103,7 +103,7 @@ public static void loadPreferences(Context ctx) { PREF_ARC_CAPES = DEFAULT_PREF.getBoolean("arc_capes",false); PREF_USE_ALTERNATE_SURFACE = DEFAULT_PREF.getBoolean("alternate_surface", false); PREF_JAVA_SANDBOX = DEFAULT_PREF.getBoolean("java_sandbox", true); - PREF_SCALE_FACTOR = DEFAULT_PREF.getInt("resolutionRatio", 100); + PREF_SCALE_FACTOR = DEFAULT_PREF.getInt("resolutionRatio", 100)/100f; PREF_ENABLE_GYRO = DEFAULT_PREF.getBoolean("enableGyro", false); PREF_GYRO_SENSITIVITY = ((float)DEFAULT_PREF.getInt("gyroSensitivity", 100))/100f; PREF_GYRO_SAMPLE_RATE = DEFAULT_PREF.getInt("gyroSampleRate", 16); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/QuickSettingSideDialog.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/QuickSettingSideDialog.java new file mode 100644 index 0000000000..47ebb7a809 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/QuickSettingSideDialog.java @@ -0,0 +1,259 @@ +package net.kdt.pojavlaunch.prefs; + +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_DISABLE_GESTURES; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_ENABLE_GYRO; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_GYRO_INVERT_X; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_GYRO_INVERT_Y; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_GYRO_SENSITIVITY; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_LONGPRESS_TRIGGER; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_MOUSESPEED; +import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_SCALE_FACTOR; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.SharedPreferences; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Switch; +import android.widget.TextView; + +import com.kdt.CustomSeekbar; + +import net.kdt.pojavlaunch.R; +import net.kdt.pojavlaunch.Tools; +import net.kdt.pojavlaunch.utils.interfaces.SimpleSeekBarListener; + +/** + * Side dialog for quick settings that you can change in game + * The implementation has to take action on some preference changes + */ +public abstract class QuickSettingSideDialog extends com.kdt.SideDialogView { + + private SharedPreferences.Editor mEditor; + @SuppressLint("UseSwitchCompatOrMaterialCode") + private Switch mGyroSwitch, mGyroXSwitch, mGyroYSwitch, mGestureSwitch; + private CustomSeekbar mGyroSensitivityBar, mMouseSpeedBar, mGestureDelayBar, mResolutionBar; + private TextView mGyroSensitivityText, mGyroSensitivityDisplayText, mMouseSpeedText, mGestureDelayText, mGestureDelayDisplayText, mResolutionText; + + private boolean mOriginalGyroEnabled, mOriginalGyroXEnabled, mOriginalGyroYEnabled, mOriginalGestureDisabled; + private float mOriginalGyroSensitivity, mOriginalMouseSpeed, mOriginalResolution; + private int mOriginalGestureDelay; + + public QuickSettingSideDialog(Context context, ViewGroup parent) { + super(context, parent, R.layout.dialog_quick_setting); + setTitle(R.string.quick_setting_title); + setupCancelButton(); + } + + @Override + protected void onInflate() { + bindLayout(); + Tools.runOnUiThread(() -> { + this.setupListeners(); + this.updateGyroCompatibility(); + }); + } + + @Override + protected void onDestroy() { + removeListeners(); + } + + private void bindLayout() { + // Bind layout elements + mGyroSwitch = mDialogContent.findViewById(R.id.checkboxGyro); + mGyroXSwitch = mDialogContent.findViewById(R.id.checkboxGyroX); + mGyroYSwitch = mDialogContent.findViewById(R.id.checkboxGyroY); + mGestureSwitch = mDialogContent.findViewById(R.id.checkboxGesture); + + mGyroSensitivityBar = mDialogContent.findViewById(R.id.editGyro_seekbar); + mMouseSpeedBar = mDialogContent.findViewById(R.id.editMouseSpeed_seekbar); + mGestureDelayBar = mDialogContent.findViewById(R.id.editGestureDelay_seekbar); + mResolutionBar = mDialogContent.findViewById(R.id.editResolution_seekbar); + + mGyroSensitivityText = mDialogContent.findViewById(R.id.editGyro_textView_percent); + mGyroSensitivityDisplayText = mDialogContent.findViewById(R.id.editGyro_textView); + mMouseSpeedText = mDialogContent.findViewById(R.id.editMouseSpeed_textView_percent); + mGestureDelayText = mDialogContent.findViewById(R.id.editGestureDelay_textView_percent); + mGestureDelayDisplayText = mDialogContent.findViewById(R.id.editGestureDelay_textView); + mResolutionText = mDialogContent.findViewById(R.id.editResolution_textView_percent); + } + + private void setupListeners() { + mEditor = LauncherPreferences.DEFAULT_PREF.edit(); + + mOriginalGyroEnabled = PREF_ENABLE_GYRO; + mOriginalGyroXEnabled = PREF_GYRO_INVERT_X; + mOriginalGyroYEnabled = PREF_GYRO_INVERT_Y; + mOriginalGestureDisabled = PREF_DISABLE_GESTURES; + + mOriginalGyroSensitivity = PREF_GYRO_SENSITIVITY; + mOriginalMouseSpeed = PREF_MOUSESPEED; + mOriginalGestureDelay = PREF_LONGPRESS_TRIGGER; + mOriginalResolution = PREF_SCALE_FACTOR; + + mGyroSwitch.setChecked(mOriginalGyroEnabled); + mGyroXSwitch.setChecked(mOriginalGyroXEnabled); + mGyroYSwitch.setChecked(mOriginalGyroYEnabled); + mGestureSwitch.setChecked(mOriginalGestureDisabled); + + mGyroSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + PREF_ENABLE_GYRO = isChecked; + onGyroStateChanged(); + updateGyroVisibility(isChecked); + mEditor.putBoolean("enableGyro", isChecked); + }); + + mGyroXSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + PREF_GYRO_INVERT_X = isChecked; + onGyroStateChanged(); + mEditor.putBoolean("gyroInvertX", isChecked); + }); + + mGyroYSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + PREF_GYRO_INVERT_Y = isChecked; + onGyroStateChanged(); + mEditor.putBoolean("gyroInvertY", isChecked); + }); + + mGestureSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> { + PREF_DISABLE_GESTURES = isChecked; + updateGestureVisibility(isChecked); + mEditor.putBoolean("disableGestures", isChecked); + }); + + mGyroSensitivityBar.setRange(25, 300); + mGyroSensitivityBar.setIncrement(5); + mGyroSensitivityBar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> { + PREF_GYRO_SENSITIVITY = progress / 100f; + mEditor.putInt("gyroSensitivity", progress); + setSeekTextPercent(mGyroSensitivityText, progress); + }); + mGyroSensitivityBar.setProgress((int) (mOriginalGyroSensitivity * 100f)); + setSeekTextPercent(mGyroSensitivityText, mGyroSensitivityBar.getProgress()); + + mMouseSpeedBar.setRange(25, 300); + mMouseSpeedBar.setIncrement(5); + mMouseSpeedBar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> { + PREF_MOUSESPEED = progress / 100f; + mEditor.putInt("mousespeed", progress); + setSeekTextPercent(mMouseSpeedText, progress); + }); + mMouseSpeedBar.setProgress((int) (mOriginalMouseSpeed * 100f)); + setSeekTextPercent(mMouseSpeedText, mMouseSpeedBar.getProgress()); + + mGestureDelayBar.setRange(100, 1000); + mGestureDelayBar.setIncrement(10); + mGestureDelayBar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> { + PREF_LONGPRESS_TRIGGER = progress; + mEditor.putInt("timeLongPressTrigger", progress); + setSeekTextMillisecond(mGestureDelayText, progress); + }); + mGestureDelayBar.setProgress(mOriginalGestureDelay); + setSeekTextMillisecond(mGestureDelayText, mGestureDelayBar.getProgress()); + + mResolutionBar.setRange(25, 100); + mResolutionBar.setIncrement(5); + mResolutionBar.setOnSeekBarChangeListener((SimpleSeekBarListener) (seekBar, progress, fromUser) -> { + PREF_SCALE_FACTOR = progress/100f; + mEditor.putInt("resolutionRatio", progress); + setSeekTextPercent(mResolutionText, progress); + onResolutionChanged(); + }); + mResolutionBar.setProgress((int) (mOriginalResolution * 100)); + setSeekTextPercent(mResolutionText, mResolutionBar.getProgress()); + + + updateGyroVisibility(mOriginalGyroEnabled); + updateGestureVisibility(mOriginalGestureDisabled); + } + + private static void setSeekTextMillisecond(TextView target, int value) { + setSeekText(target, R.string.millisecond_format, value); + } + + private static void setSeekTextPercent(TextView target, int value) { + setSeekText(target, R.string.percent_format, value); + } + + private static void setSeekText(TextView target, int format, int value) { + target.setText(target.getContext().getString(format, value)); + } + + private void updateGyroVisibility(boolean isEnabled) { + int visibility = isEnabled ? View.VISIBLE : View.GONE; + mGyroXSwitch.setVisibility(visibility); + mGyroYSwitch.setVisibility(visibility); + + mGyroSensitivityBar.setVisibility(visibility); + mGyroSensitivityText.setVisibility(visibility); + mGyroSensitivityDisplayText.setVisibility(visibility); + } + + private void updateGyroCompatibility() { + boolean isGyroAvailable = Tools.deviceSupportsGyro(mDialogContent.getContext()); + if (!isGyroAvailable) { + mGyroSwitch.setVisibility(View.GONE); + updateGestureVisibility(false); + } + } + + private void updateGestureVisibility(boolean isDisabled) { + int visibility = isDisabled ? View.GONE : View.VISIBLE; + mGestureDelayBar.setVisibility(visibility); + mGestureDelayText.setVisibility(visibility); + mGestureDelayDisplayText.setVisibility(visibility); + } + + private void removeListeners() { + mGyroSwitch.setOnCheckedChangeListener(null); + mGyroXSwitch.setOnCheckedChangeListener(null); + mGyroYSwitch.setOnCheckedChangeListener(null); + mGestureSwitch.setOnCheckedChangeListener(null); + + mGyroSensitivityBar.setOnSeekBarChangeListener(null); + mMouseSpeedBar.setOnSeekBarChangeListener(null); + mGestureDelayBar.setOnSeekBarChangeListener(null); + mResolutionBar.setOnSeekBarChangeListener(null); + } + + private void setupCancelButton() { + setStartButtonListener(android.R.string.cancel, v -> cancel()); + setEndButtonListener(android.R.string.ok, v -> { + mEditor.apply(); + disappear(true); + }); + } + + /** Resets all settings to their original values */ + public void cancel() { + // Reset all settings if we were editing + if(isDisplaying()) { + PREF_ENABLE_GYRO = mOriginalGyroEnabled; + PREF_GYRO_INVERT_X = mOriginalGyroXEnabled; + PREF_GYRO_INVERT_Y = mOriginalGyroYEnabled; + PREF_DISABLE_GESTURES = mOriginalGestureDisabled; + + PREF_GYRO_SENSITIVITY = mOriginalGyroSensitivity; + PREF_MOUSESPEED = mOriginalMouseSpeed; + PREF_LONGPRESS_TRIGGER = mOriginalGestureDelay; + PREF_SCALE_FACTOR = mOriginalResolution; + + onGyroStateChanged(); + onResolutionChanged(); + } + + disappear(true); + } + + /** Called when the resolution is changed. Use {@link LauncherPreferences#PREF_SCALE_FACTOR} */ + public abstract void onResolutionChanged(); + + /** Called when the gyro state is changed. + * Use {@link LauncherPreferences#PREF_ENABLE_GYRO} + * Use {@link LauncherPreferences#PREF_GYRO_INVERT_X} + * Use {@link LauncherPreferences#PREF_GYRO_INVERT_Y} + */ + public abstract void onGyroStateChanged(); + +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceControlFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceControlFragment.java index a7400b8922..88ac51ffd8 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceControlFragment.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/prefs/screens/LauncherPreferenceControlFragment.java @@ -9,6 +9,7 @@ import androidx.preference.PreferenceCategory; import net.kdt.pojavlaunch.R; +import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.prefs.CustomSeekBarPreference; import net.kdt.pojavlaunch.prefs.LauncherPreferences; @@ -19,7 +20,7 @@ public void onCreatePreferences(Bundle b, String str) { // Get values int longPressTrigger = LauncherPreferences.PREF_LONGPRESS_TRIGGER; int prefButtonSize = (int) LauncherPreferences.PREF_BUTTONSIZE; - int mouseScale = (int) LauncherPreferences.PREF_MOUSESCALE; + int mouseScale = (int) (LauncherPreferences.PREF_MOUSESCALE * 100); int gyroSampleRate = LauncherPreferences.PREF_GYRO_SAMPLE_RATE; float mouseSpeed = LauncherPreferences.PREF_MOUSESPEED; float gyroSpeed = LauncherPreferences.PREF_GYRO_SENSITIVITY; @@ -62,7 +63,7 @@ public void onCreatePreferences(Bundle b, String str) { Context context = getContext(); if(context != null) { - mGyroAvailable = ((SensorManager)context.getSystemService(Context.SENSOR_SERVICE)).getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null; + mGyroAvailable = Tools.deviceSupportsGyro(context); } PreferenceCategory gyroCategory = requirePreference("gyroCategory", PreferenceCategory.class); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java index fb85224ef2..da5cfa9c89 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java @@ -323,7 +323,7 @@ public static void launchJavaVM(final AppCompatActivity activity, final Runtime LifecycleAwareAlertDialog.haltOnDialog(activity.getLifecycle(), activity, dialogCreator); } - MainActivity.fullyExit(); + Tools.fullyExit(); } /** @@ -355,8 +355,8 @@ public static List getJavaArgs(Context ctx, String runtimeHome, String u //"-Dorg.lwjgl.util.DebugFunctions=true", //"-Dorg.lwjgl.util.DebugLoader=true", // GLFW Stub width height - "-Dglfwstub.windowWidth=" + Tools.getDisplayFriendlyRes(currentDisplayMetrics.widthPixels, LauncherPreferences.PREF_SCALE_FACTOR/100F), - "-Dglfwstub.windowHeight=" + Tools.getDisplayFriendlyRes(currentDisplayMetrics.heightPixels, LauncherPreferences.PREF_SCALE_FACTOR/100F), + "-Dglfwstub.windowWidth=" + Tools.getDisplayFriendlyRes(currentDisplayMetrics.widthPixels, LauncherPreferences.PREF_SCALE_FACTOR), + "-Dglfwstub.windowHeight=" + Tools.getDisplayFriendlyRes(currentDisplayMetrics.heightPixels, LauncherPreferences.PREF_SCALE_FACTOR), "-Dglfwstub.initEgl=false", "-Dext.net.resolvPath=" +resolvFile, "-Dlog4j2.formatMsgNoLookups=true", //Log4j RCE mitigation diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/interfaces/SimpleItemSelectedListener.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/interfaces/SimpleItemSelectedListener.java new file mode 100644 index 0000000000..3fe039f1a1 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/interfaces/SimpleItemSelectedListener.java @@ -0,0 +1,15 @@ +package net.kdt.pojavlaunch.utils.interfaces; + +import android.view.View; +import android.widget.AdapterView; + +/** + * Most interfaces implementations of {@link AdapterView.OnItemSelectedListener} + * only implement the {@link AdapterView.OnItemSelectedListener#onItemSelected(AdapterView, View, int, long)} onItemClick method. + * This class provides a default for other methods. + */ +public interface SimpleItemSelectedListener extends AdapterView.OnItemSelectedListener { + @Override + default void onNothingSelected(AdapterView parent) { + } +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/interfaces/SimpleSeekBarListener.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/interfaces/SimpleSeekBarListener.java new file mode 100644 index 0000000000..ee88f5ceb9 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/interfaces/SimpleSeekBarListener.java @@ -0,0 +1,17 @@ +package net.kdt.pojavlaunch.utils.interfaces; + +import android.widget.SeekBar; + +/** + * Most interfaces implementations of {@link SeekBar.OnSeekBarChangeListener} + * only implement the onProgressChanged method. This class provides a default for other methods. + */ +public interface SimpleSeekBarListener extends SeekBar.OnSeekBarChangeListener { + @Override + default void onStartTrackingTouch(android.widget.SeekBar seekBar) { + } + + @Override + default void onStopTrackingTouch(android.widget.SeekBar seekBar) { + } +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/interfaces/SimpleTextWatcher.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/interfaces/SimpleTextWatcher.java new file mode 100644 index 0000000000..63c2f772a7 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/interfaces/SimpleTextWatcher.java @@ -0,0 +1,16 @@ +package net.kdt.pojavlaunch.utils.interfaces; + +import android.text.Editable; +import android.text.TextWatcher; + +/** + * Most interfaces implementations of {@link TextWatcher} only implement the afterTextChanged method. + * This class provides a default for other methods. + */ +public interface SimpleTextWatcher extends TextWatcher { + @Override + default void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + default void onTextChanged(CharSequence s, int start, int before, int count) {} +} diff --git a/app_pojavlauncher/src/main/jni/egl_bridge.c b/app_pojavlauncher/src/main/jni/egl_bridge.c index 029bdeff3e..30b89631bb 100644 --- a/app_pojavlauncher/src/main/jni/egl_bridge.c +++ b/app_pojavlauncher/src/main/jni/egl_bridge.c @@ -212,11 +212,14 @@ int pojavInitOpenGL() { return 0; } +extern void updateMonitorSize(int width, int height); + EXTERNAL_API int pojavInit() { ANativeWindow_acquire(pojav_environ->pojavWindow); pojav_environ->savedWidth = ANativeWindow_getWidth(pojav_environ->pojavWindow); pojav_environ->savedHeight = ANativeWindow_getHeight(pojav_environ->pojavWindow); ANativeWindow_setBuffersGeometry(pojav_environ->pojavWindow,pojav_environ->savedWidth,pojav_environ->savedHeight,AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM); + updateMonitorSize(pojav_environ->savedWidth, pojav_environ->savedHeight); pojavInitOpenGL(); return 1; } diff --git a/app_pojavlauncher/src/main/jni/environ/environ.h b/app_pojavlauncher/src/main/jni/environ/environ.h index 4d73f1fb34..fbfdb1b095 100644 --- a/app_pojavlauncher/src/main/jni/environ/environ.h +++ b/app_pojavlauncher/src/main/jni/environ/environ.h @@ -24,11 +24,9 @@ typedef void GLFW_invoke_Char_func(void* window, unsigned int codepoint); typedef void GLFW_invoke_CharMods_func(void* window, unsigned int codepoint, int mods); typedef void GLFW_invoke_CursorEnter_func(void* window, int entered); typedef void GLFW_invoke_CursorPos_func(void* window, double xpos, double ypos); -typedef void GLFW_invoke_FramebufferSize_func(void* window, int width, int height); typedef void GLFW_invoke_Key_func(void* window, int key, int scancode, int action, int mods); typedef void GLFW_invoke_MouseButton_func(void* window, int button, int action, int mods); typedef void GLFW_invoke_Scroll_func(void* window, double xoffset, double yoffset); -typedef void GLFW_invoke_WindowSize_func(void* window, int width, int height); struct pojav_environ_s { struct ANativeWindow* pojavWindow; @@ -46,6 +44,7 @@ struct pojav_environ_s { jmethodID method_onGrabStateChanged; jmethodID method_glftSetWindowAttrib; jmethodID method_internalWindowSizeChanged; + jmethodID method_internalChangeMonitorSize; jclass bridgeClazz; jclass vmGlfwClass; jboolean isGrabbing; @@ -57,6 +56,7 @@ struct pojav_environ_s { JNIEnv* dalvikJNIEnvPtr_ANDROID; long showingWindow; bool isInputReady, isCursorEntered, isUseStackQueueCall, shouldUpdateMouse; + bool shouldUpdateMonitorSize, monitorSizeConsumed; int savedWidth, savedHeight; #define ADD_CALLBACK_WWIN(NAME) \ GLFW_invoke_##NAME##_func* GLFW_invoke_##NAME; @@ -64,11 +64,9 @@ struct pojav_environ_s { ADD_CALLBACK_WWIN(CharMods); ADD_CALLBACK_WWIN(CursorEnter); ADD_CALLBACK_WWIN(CursorPos); - ADD_CALLBACK_WWIN(FramebufferSize); ADD_CALLBACK_WWIN(Key); ADD_CALLBACK_WWIN(MouseButton); ADD_CALLBACK_WWIN(Scroll); - ADD_CALLBACK_WWIN(WindowSize); #undef ADD_CALLBACK_WWIN }; diff --git a/app_pojavlauncher/src/main/jni/input_bridge_v3.c b/app_pojavlauncher/src/main/jni/input_bridge_v3.c index 5b50a82282..4bd94f6e1a 100644 --- a/app_pojavlauncher/src/main/jni/input_bridge_v3.c +++ b/app_pojavlauncher/src/main/jni/input_bridge_v3.c @@ -26,11 +26,9 @@ #define EVENT_TYPE_CHAR 1000 #define EVENT_TYPE_CHAR_MODS 1001 #define EVENT_TYPE_CURSOR_ENTER 1002 -#define EVENT_TYPE_FRAMEBUFFER_SIZE 1004 #define EVENT_TYPE_KEY 1005 #define EVENT_TYPE_MOUSE_BUTTON 1006 #define EVENT_TYPE_SCROLL 1007 -#define EVENT_TYPE_WINDOW_SIZE 1008 jint (*orig_ProcessImpl_forkAndExec)(JNIEnv *env, jobject process, jint mode, jbyteArray helperpath, jbyteArray prog, jbyteArray argBlock, jint argc, jbyteArray envBlock, jint envc, jbyteArray dir, jintArray std_fds, jboolean redirectErrorStream); @@ -52,7 +50,8 @@ jint JNI_OnLoad(JavaVM* vm, __attribute__((unused)) void* reserved) { (*vm)->GetEnv(vm, (void**) &pojav_environ->runtimeJNIEnvPtr_JRE, JNI_VERSION_1_4); pojav_environ->vmGlfwClass = (*pojav_environ->runtimeJNIEnvPtr_JRE)->NewGlobalRef(pojav_environ->runtimeJNIEnvPtr_JRE, (*pojav_environ->runtimeJNIEnvPtr_JRE)->FindClass(pojav_environ->runtimeJNIEnvPtr_JRE, "org/lwjgl/glfw/GLFW")); pojav_environ->method_glftSetWindowAttrib = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetStaticMethodID(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, "glfwSetWindowAttrib", "(JII)V"); - pojav_environ->method_internalWindowSizeChanged = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetStaticMethodID(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, "internalWindowSizeChanged", "(JII)V"); + pojav_environ->method_internalWindowSizeChanged = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetStaticMethodID(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, "internalWindowSizeChanged", "(J)V"); + pojav_environ->method_internalChangeMonitorSize = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetStaticMethodID(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, "internalChangeMonitorSize", "(II)V"); jfieldID field_keyDownBuffer = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetStaticFieldID(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, "keyDownBuffer", "Ljava/nio/ByteBuffer;"); jobject keyDownBufferJ = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetStaticObjectField(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, field_keyDownBuffer); pojav_environ->keyDownBuffer = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetDirectBufferAddress(pojav_environ->runtimeJNIEnvPtr_JRE, keyDownBufferJ); @@ -86,16 +85,17 @@ ADD_CALLBACK_WWIN(Char) ADD_CALLBACK_WWIN(CharMods) ADD_CALLBACK_WWIN(CursorEnter) ADD_CALLBACK_WWIN(CursorPos) -ADD_CALLBACK_WWIN(FramebufferSize) ADD_CALLBACK_WWIN(Key) ADD_CALLBACK_WWIN(MouseButton) ADD_CALLBACK_WWIN(Scroll) -ADD_CALLBACK_WWIN(WindowSize) #undef ADD_CALLBACK_WWIN -void handleFramebufferSizeJava(long window, int w, int h) { - (*pojav_environ->runtimeJNIEnvPtr_JRE)->CallStaticVoidMethod(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, pojav_environ->method_internalWindowSizeChanged, (long)window, w, h); +void updateMonitorSize(int width, int height) { + (*pojav_environ->runtimeJNIEnvPtr_JRE)->CallStaticVoidMethod(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, pojav_environ->method_internalChangeMonitorSize, width, height); +} +void updateWindowSize(void* window) { + (*pojav_environ->runtimeJNIEnvPtr_JRE)->CallStaticVoidMethod(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, pojav_environ->method_internalWindowSizeChanged, (jlong)window); } void pojavPumpEvents(void* window) { @@ -103,6 +103,9 @@ void pojavPumpEvents(void* window) { pojav_environ->GLFW_invoke_CursorPos(window, floor(pojav_environ->cursorX), floor(pojav_environ->cursorY)); } + if(pojav_environ->shouldUpdateMonitorSize) { + updateWindowSize(window); + } size_t index = pojav_environ->outEventIndex; size_t targetIndex = pojav_environ->outTargetIndex; @@ -125,14 +128,6 @@ void pojavPumpEvents(void* window) { case EVENT_TYPE_SCROLL: if(pojav_environ->GLFW_invoke_Scroll) pojav_environ->GLFW_invoke_Scroll(window, event.i1, event.i2); break; - case EVENT_TYPE_FRAMEBUFFER_SIZE: - handleFramebufferSizeJava(pojav_environ->showingWindow, event.i1, event.i2); - if(pojav_environ->GLFW_invoke_FramebufferSize) pojav_environ->GLFW_invoke_FramebufferSize(window, event.i1, event.i2); - break; - case EVENT_TYPE_WINDOW_SIZE: - handleFramebufferSizeJava(pojav_environ->showingWindow, event.i1, event.i2); - if(pojav_environ->GLFW_invoke_WindowSize) pojav_environ->GLFW_invoke_WindowSize(window, event.i1, event.i2); - break; } index++; @@ -162,6 +157,12 @@ void pojavStartPumping() { pojav_environ->cLastY = pojav_environ->cursorY; pojav_environ->shouldUpdateMouse = true; } + if(pojav_environ->shouldUpdateMonitorSize) { + // Perform a monitor size update here to avoid doing it on every single window + updateMonitorSize(pojav_environ->savedWidth, pojav_environ->savedHeight); + // Mark the monitor size as consumed (since GLFW was made aware of it) + pojav_environ->monitorSizeConsumed = true; + } } /** Prepare the library for the next round of new events */ @@ -170,8 +171,16 @@ void pojavStopPumping() { // New events may have arrived while pumping, so remove only the difference before the start and end of execution atomic_fetch_sub_explicit(&pojav_environ->eventCounter, pojav_environ->inEventCount, memory_order_acquire); - // Make sure the next frame won't send mouse updates if it's unnecessary + // Make sure the next frame won't send mouse or monitor updates if it's unnecessary pojav_environ->shouldUpdateMouse = false; + // Only reset the update flag if the monitor size was consumed by pojavStartPumping. This + // will delay the update to next frame if it had occured between pojavStartPumping and pojavStopPumping, + // but it's better than not having it apply at all + if(pojav_environ->shouldUpdateMonitorSize && pojav_environ->monitorSizeConsumed) { + pojav_environ->shouldUpdateMonitorSize = false; + pojav_environ->monitorSizeConsumed = false; + } + } JNIEXPORT void JNICALL @@ -502,23 +511,15 @@ void noncritical_send_mouse_button(__attribute__((unused)) JNIEnv* env, __attrib void critical_send_screen_size(jint width, jint height) { pojav_environ->savedWidth = width; pojav_environ->savedHeight = height; - if (pojav_environ->isInputReady) { - if (pojav_environ->GLFW_invoke_FramebufferSize) { - if (pojav_environ->isUseStackQueueCall) { - sendData(EVENT_TYPE_FRAMEBUFFER_SIZE, width, height, 0, 0); - } else { - pojav_environ->GLFW_invoke_FramebufferSize((void*) pojav_environ->showingWindow, width, height); - } - } - - if (pojav_environ->GLFW_invoke_WindowSize) { - if (pojav_environ->isUseStackQueueCall) { - sendData(EVENT_TYPE_WINDOW_SIZE, width, height, 0, 0); - } else { - pojav_environ->GLFW_invoke_WindowSize((void*) pojav_environ->showingWindow, width, height); - } - } - } + // Even if there was call to pojavStartPumping that consumed the size, this call + // might happen right after it (or right before pojavStopPumping) + // So unmark the size as "consumed" + pojav_environ->monitorSizeConsumed = false; + pojav_environ->shouldUpdateMonitorSize = true; + // Don't use the direct updates for screen dimensions. + // This is done to ensure that we have predictable conditions to correctly call + // updateMonitorSize() and updateWindowSize() while on the render thread with an attached + // JNIEnv. } void noncritical_send_screen_size(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint width, jint height) { @@ -541,7 +542,7 @@ void noncritical_send_scroll(__attribute__((unused)) JNIEnv* env, __attribute__( JNIEXPORT void JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetShowingWindow(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jlong window) { - pojav_environ->showingWindow = (long) window; + pojav_environ->showingWindow = (jlong) window; } JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetWindowAttrib(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint attrib, jint value) { diff --git a/app_pojavlauncher/src/main/res/layout/activity_basemain.xml b/app_pojavlauncher/src/main/res/layout/activity_basemain.xml index c97c03d065..a7a95d639a 100644 --- a/app_pojavlauncher/src/main/res/layout/activity_basemain.xml +++ b/app_pojavlauncher/src/main/res/layout/activity_basemain.xml @@ -20,6 +20,8 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + @@ -74,7 +77,6 @@ android:layout_gravity="right" android:background="?attr/colorBackgroundFloating" android:fitsSystemWindows="false" - app:menu="@menu/menu_runopt" android:id="@+id/main_navigation_view"/> diff --git a/app_pojavlauncher/src/main/res/layout/dialog_control_button_setting.xml b/app_pojavlauncher/src/main/res/layout/dialog_control_button_setting.xml index 08508c24a4..048d12c3bd 100644 --- a/app_pojavlauncher/src/main/res/layout/dialog_control_button_setting.xml +++ b/app_pojavlauncher/src/main/res/layout/dialog_control_button_setting.xml @@ -1,507 +1,500 @@ - + android:paddingHorizontal="@dimen/padding_moderate" + android:paddingVertical="@dimen/padding_moderate" + app:layout_optimizationLevel="standard|dimensions|chains"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + android:gravity="center" + android:paddingEnd="5dp" + android:text="@string/customctrl_corner_radius" + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/editStrokeWidth_seekbar" /> + + + + + + + + + + + + + + + + + + + + + + diff --git a/app_pojavlauncher/src/main/res/layout/dialog_quick_setting.xml b/app_pojavlauncher/src/main/res/layout/dialog_quick_setting.xml new file mode 100644 index 0000000000..435781a042 --- /dev/null +++ b/app_pojavlauncher/src/main/res/layout/dialog_quick_setting.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app_pojavlauncher/src/main/res/layout/dialog_side_dialog.xml b/app_pojavlauncher/src/main/res/layout/dialog_side_dialog.xml new file mode 100644 index 0000000000..053eb485e4 --- /dev/null +++ b/app_pojavlauncher/src/main/res/layout/dialog_side_dialog.xml @@ -0,0 +1,72 @@ + + + + + + + + + + +