From 29605ed6b75e1aabb74dc92b918e404ec811a7ec Mon Sep 17 00:00:00 2001 From: Mathias-Boulay Date: Fri, 29 Nov 2024 18:22:39 +0100 Subject: [PATCH 1/5] QoL: make the settings button easier to click Bigger hitbox, but not visually --- .../customcontrols/handleview/DrawerPullButton.java | 11 +++++------ .../src/main/res/layout/activity_basemain.xml | 5 +++-- .../src/main/res/layout/activity_custom_controls.xml | 5 +++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/DrawerPullButton.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/DrawerPullButton.java index fd6fc4714f..fb90307527 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/DrawerPullButton.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/handleview/DrawerPullButton.java @@ -16,23 +16,22 @@ public class DrawerPullButton extends View { public DrawerPullButton(Context context) {super(context); init();} public DrawerPullButton(Context context, @Nullable AttributeSet attrs) {super(context, attrs); init();} - private final Paint mPaint = new Paint(); + private final Paint mBackgroundPaint = new Paint(); private VectorDrawableCompat mDrawable; private void init(){ mDrawable = VectorDrawableCompat.create(getContext().getResources(), R.drawable.ic_sharp_settings_24, null); setAlpha(0.33f); + mBackgroundPaint.setColor(Color.BLACK); } @Override protected void onDraw(Canvas canvas) { - mPaint.setColor(Color.BLACK); - canvas.drawArc(0,-getHeight(),getWidth(), getHeight(), 0, 180, true, mPaint); + canvas.drawArc(getPaddingLeft(),-getHeight() + getPaddingBottom(),getWidth() - getPaddingRight(), getHeight() - getPaddingBottom(), 0, 180, true, mBackgroundPaint); - mPaint.setColor(Color.WHITE); - mDrawable.setBounds(0, 0, getHeight(), getHeight()); + mDrawable.setBounds(getPaddingLeft()/2, getPaddingTop()/2, getHeight() - getPaddingRight()/2, getHeight() - getPaddingBottom()/2); canvas.save(); - canvas.translate((getWidth()-getHeight())/2f, 0); + canvas.translate((getWidth()-getHeight())/2f, -getPaddingBottom()/2f); mDrawable.draw(canvas); canvas.restore(); } diff --git a/app_pojavlauncher/src/main/res/layout/activity_basemain.xml b/app_pojavlauncher/src/main/res/layout/activity_basemain.xml index a7a95d639a..bd7f5f7a15 100644 --- a/app_pojavlauncher/src/main/res/layout/activity_basemain.xml +++ b/app_pojavlauncher/src/main/res/layout/activity_basemain.xml @@ -51,8 +51,9 @@ Date: Fri, 29 Nov 2024 18:57:24 +0100 Subject: [PATCH 2/5] QoL(profile editor): make the text react like a button This increases the space you can use, better for left handed people or large hands --- .../fragments/ProfileEditorFragment.java | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ProfileEditorFragment.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ProfileEditorFragment.java index 088747b110..501b0147a7 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ProfileEditorFragment.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/fragments/ProfileEditorFragment.java @@ -107,7 +107,28 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat Tools.removeCurrentFragment(requireActivity()); }); - mGameDirButton.setOnClickListener(v -> { + + View.OnClickListener gameDirListener = getGameDirListener(); + mGameDirButton.setOnClickListener(gameDirListener); + mDefaultPath.setOnClickListener(gameDirListener); + + View.OnClickListener controlSelectListener = getControlSelectListener(); + mControlSelectButton.setOnClickListener(controlSelectListener); + mDefaultControl.setOnClickListener(controlSelectListener); + + // Setup the expendable list behavior + View.OnClickListener versionSelectListener = getVersionSelectListener(); + mVersionSelectButton.setOnClickListener(versionSelectListener); + mDefaultVersion.setOnClickListener(versionSelectListener); + + // Set up the icon change click listener + mProfileIcon.setOnClickListener(v -> CropperUtils.startCropper(mCropperLauncher)); + + loadValues(LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE, ""), view.getContext()); + } + + private View.OnClickListener getGameDirListener() { + return v -> { Bundle bundle = new Bundle(2); bundle.putBoolean(FileSelectorFragment.BUNDLE_SELECT_FOLDER, true); bundle.putString(FileSelectorFragment.BUNDLE_ROOT_PATH, Tools.DIR_GAME_HOME); @@ -116,9 +137,11 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat Tools.swapFragment(requireActivity(), FileSelectorFragment.class, FileSelectorFragment.TAG, bundle); - }); + }; + } - mControlSelectButton.setOnClickListener(v -> { + private View.OnClickListener getControlSelectListener() { + return v -> { Bundle bundle = new Bundle(3); bundle.putBoolean(FileSelectorFragment.BUNDLE_SELECT_FOLDER, false); bundle.putString(FileSelectorFragment.BUNDLE_ROOT_PATH, Tools.CTRLMAP_PATH); @@ -126,20 +149,14 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat Tools.swapFragment(requireActivity(), FileSelectorFragment.class, FileSelectorFragment.TAG, bundle); - }); + }; + } - // Setup the expendable list behavior - mVersionSelectButton.setOnClickListener(v -> VersionSelectorDialog.open(v.getContext(), false, (id, snapshot)->{ + private View.OnClickListener getVersionSelectListener() { + return v -> VersionSelectorDialog.open(v.getContext(), false, (id, snapshot)-> { mTempProfile.lastVersionId = id; mDefaultVersion.setText(id); - })); - - // Set up the icon change click listener - mProfileIcon.setOnClickListener(v -> CropperUtils.startCropper(mCropperLauncher)); - - - - loadValues(LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE, ""), view.getContext()); + }); } From 68fa25cafc6bbfd379bb03e9a39e87942abe64af Mon Sep 17 00:00:00 2001 From: Mathias-Boulay Date: Fri, 29 Nov 2024 21:14:16 +0100 Subject: [PATCH 3/5] Fix(hotbar): first touch on 0 index not being taken into account --- .../customcontrols/mouse/HotbarView.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) 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 522b0fe5b8..b969fc83a1 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 @@ -11,6 +11,7 @@ import androidx.annotation.Nullable; +import net.kdt.pojavlaunch.GrabListener; import net.kdt.pojavlaunch.LwjglGlfwKeycode; import net.kdt.pojavlaunch.prefs.LauncherPreferences; import net.kdt.pojavlaunch.utils.MCOptionUtils; @@ -26,8 +27,16 @@ 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 GrabListener mGrabListener = new GrabListener() { + @Override + public void onGrabState(boolean isGrabbing) { + mLastIndex = -1; + mDropGesture.cancel(); + } + }; + private int mWidth; - private int mLastIndex; + private int mLastIndex = -1; private int mGuiScale; public HotbarView(Context context) { @@ -66,6 +75,13 @@ protected void onAttachedToWindow() { } mGuiScale = MCOptionUtils.getMcScale(); repositionView(); + CallbackBridge.addGrabListener(mGrabListener); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + CallbackBridge.removeGrabListener(mGrabListener); } private void repositionView() { From c6fe3c3de5cbda3e729884dccce2b5a15110fcb9 Mon Sep 17 00:00:00 2001 From: Mathias-Boulay Date: Fri, 29 Nov 2024 21:17:29 +0100 Subject: [PATCH 4/5] QoL(gesture): decouple gyroscope from long press gesture --- .../mouse/InGameEventProcessor.java | 8 +++++-- .../mouse/LeftClickGesture.java | 22 +++++++++++++++---- .../mouse/RightClickGesture.java | 9 ++++++-- 3 files changed, 31 insertions(+), 8 deletions(-) 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 f4e5fe7639..bc05007002 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 @@ -33,8 +33,12 @@ public boolean processTouchEvent(MotionEvent motionEvent) { case MotionEvent.ACTION_MOVE: mTracker.trackEvent(motionEvent); float[] motionVector = mTracker.getMotionVector(); - CallbackBridge.mouseX += (float) (motionVector[0] * mSensitivity); - CallbackBridge.mouseY += (float) (motionVector[1] * mSensitivity); + float deltaX = (float) (motionVector[0] * mSensitivity); + float deltaY = (float) (motionVector[1] * mSensitivity); + mLeftClickGesture.setMotion(deltaX, deltaY); + mRightClickGesture.setMotion(deltaX, deltaY); + CallbackBridge.mouseX += deltaX; + CallbackBridge.mouseY += deltaY; 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/LeftClickGesture.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/LeftClickGesture.java index d422ed6838..38ffef803b 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/LeftClickGesture.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/LeftClickGesture.java @@ -13,7 +13,7 @@ public class LeftClickGesture extends ValidatorGesture { public static final int FINGER_STILL_THRESHOLD = (int) Tools.dpToPx(9); - private float mGestureStartX, mGestureStartY; + private float mGestureStartX, mGestureStartY, mGestureEndX, mGestureEndY; private boolean mMouseActivated; public LeftClickGesture(Handler handler) { @@ -22,14 +22,14 @@ public LeftClickGesture(Handler handler) { public final void inputEvent() { if(submit()) { - mGestureStartX = CallbackBridge.mouseX; - mGestureStartY = CallbackBridge.mouseY; + mGestureStartX = mGestureEndX = CallbackBridge.mouseX; + mGestureStartY = mGestureEndY = CallbackBridge.mouseY; } } @Override public boolean checkAndTrigger() { - boolean fingerStill = LeftClickGesture.isFingerStill(mGestureStartX, mGestureStartY, FINGER_STILL_THRESHOLD); + boolean fingerStill = LeftClickGesture.isFingerStill(mGestureStartX, mGestureStartY, mGestureEndX, mGestureEndY, FINGER_STILL_THRESHOLD); // If the finger is still, fire the gesture. if(fingerStill) { sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT, true); @@ -47,6 +47,11 @@ public void onGestureCancelled(boolean isSwitching) { } } + public void setMotion(float deltaX, float deltaY) { + mGestureEndX += deltaX; + mGestureEndY += deltaY; + } + /** * Check if the finger is still when compared to mouseX/mouseY in CallbackBridge. * @param startX the starting X of the gesture @@ -61,4 +66,13 @@ public static boolean isFingerStill(float startX, float startY, float threshold) startY ) <= threshold; } + + public static boolean isFingerStill(float startX, float startY, float endX, float endY, float threshold) { + return MathUtils.dist( + endX, + endY, + startX, + startY + ) <= threshold; + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/RightClickGesture.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/RightClickGesture.java index 75ccbba6c7..d24874f7ed 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/RightClickGesture.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/mouse/RightClickGesture.java @@ -9,7 +9,7 @@ public class RightClickGesture extends ValidatorGesture{ private boolean mGestureEnabled = true; private boolean mGestureValid = true; - private float mGestureStartX, mGestureStartY; + private float mGestureStartX, mGestureStartY, mGestureEndX, mGestureEndY; public RightClickGesture(Handler mHandler) { super(mHandler, 150); } @@ -24,6 +24,11 @@ public final void inputEvent() { } } + public void setMotion(float deltaX, float deltaY) { + mGestureEndX += deltaX; + mGestureEndY += deltaY; + } + @Override public boolean checkAndTrigger() { // If the validate() method was called, it means that the user held on for too long. The cancellation should be ignored. @@ -38,7 +43,7 @@ public boolean checkAndTrigger() { public void onGestureCancelled(boolean isSwitching) { mGestureEnabled = true; if(!mGestureValid || isSwitching) return; - boolean fingerStill = LeftClickGesture.isFingerStill(mGestureStartX, mGestureStartY, LeftClickGesture.FINGER_STILL_THRESHOLD); + boolean fingerStill = LeftClickGesture.isFingerStill(mGestureStartX, mGestureStartY, mGestureEndX, mGestureEndY, LeftClickGesture.FINGER_STILL_THRESHOLD); if(!fingerStill) return; CallbackBridge.sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT, true); CallbackBridge.sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT, false); From fc81b87e7fdcd820f771dd4393188c2626774dda Mon Sep 17 00:00:00 2001 From: Mathias-Boulay Date: Sun, 1 Dec 2024 23:29:16 +0100 Subject: [PATCH 5/5] Fix(controls): non square joystick, set size via text --- app_pojavlauncher/build.gradle | 2 +- .../customcontrols/CustomControls.java | 6 +-- .../customcontrols/LayoutConverter.java | 53 ++++++++++++++----- .../handleview/EditControlSideDialog.java | 9 ++++ 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/app_pojavlauncher/build.gradle b/app_pojavlauncher/build.gradle index 3654bba93c..19bcb89309 100644 --- a/app_pojavlauncher/build.gradle +++ b/app_pojavlauncher/build.gradle @@ -204,7 +204,7 @@ dependencies { implementation 'com.github.PojavLauncherTeam:portrait-ssp:6c02fd739b' implementation 'com.github.Mathias-Boulay:ExtendedView:1.0.0' implementation 'com.github.Mathias-Boulay:android_gamepad_remapper:2.0.3' - implementation 'com.github.Mathias-Boulay:virtual-joystick-android:2e7aa25e50' + implementation 'com.github.Mathias-Boulay:virtual-joystick-android:1.14' // implementation 'com.intuit.sdp:sdp-android:1.0.5' // implementation 'com.intuit.ssp:ssp-android:1.0.5' diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/CustomControls.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/CustomControls.java index 3983b38491..a73f23eede 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/CustomControls.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/CustomControls.java @@ -57,13 +57,13 @@ public CustomControls(Context ctx) { this.mControlDataList.add(new ControlData(ctx, R.string.control_jump, new int[]{LwjglGlfwKeycode.GLFW_KEY_SPACE}, "${right} - ${margin} * 2 - ${width}", "${bottom} - ${margin} * 2 - ${height}", true)); //The default controls are conform to the V3 - version = 7; + version = 8; } public void save(String path) throws IOException { - //Current version is the V3.1 so the version as to be marked as 7 ! - version = 7; + //Current version is the V3.2 so the version as to be marked as 8 ! + version = 8; Tools.write(path, Tools.GLOBAL_GSON.toJson(this)); } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/LayoutConverter.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/LayoutConverter.java index 25352897bc..81c949fd31 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/LayoutConverter.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/customcontrols/LayoutConverter.java @@ -24,27 +24,56 @@ public static CustomControls loadAndConvertIfNecessary(String jsonPath) throws I CustomControls layout = LayoutConverter.convertV1Layout(layoutJobj); layout.save(jsonPath); return layout; - } else if (layoutJobj.getInt("version") == 2) { - CustomControls layout = LayoutConverter.convertV2Layout(layoutJobj); - layout.save(jsonPath); - return layout; - }else if (layoutJobj.getInt("version") >= 3 && layoutJobj.getInt("version") <= 5) { - return LayoutConverter.convertV3_4Layout(layoutJobj); - } else if (layoutJobj.getInt("version") == 6 || layoutJobj.getInt("version") == 7) { - return Tools.GLOBAL_GSON.fromJson(jsonLayoutData, CustomControls.class); } else { - return null; + int version = layoutJobj.getInt("version"); + if (version == 2) { + CustomControls layout = LayoutConverter.convertV2Layout(layoutJobj); + layout.save(jsonPath); + return layout; + } + if (version == 3 || version == 4 || version == 5) { + return LayoutConverter.convertV3_4Layout(layoutJobj); + } + if (version == 6 || version == 7) { + return convertV6_7Layout(layoutJobj); + } + else if (version == 8) { + return Tools.GLOBAL_GSON.fromJson(jsonLayoutData, CustomControls.class); + } } + return null; } catch (JSONException e) { throw new JsonSyntaxException("Failed to load", e); } } + /** + * Normalize the layout to v8 from v6/7. An issue from the joystick height and position has to be fixed. + * @param oldLayoutJson The old layout + * @return The new layout with the fixed joystick height + */ + public static CustomControls convertV6_7Layout(JSONObject oldLayoutJson) { + CustomControls layout = Tools.GLOBAL_GSON.fromJson(oldLayoutJson.toString(), CustomControls.class); + for (ControlJoystickData data : layout.mJoystickDataList) { + if (data.getHeight() > data.getWidth()) { + // Make the size square, adjust the dynamic position related to height + float ratio = data.getHeight() / data.getWidth(); + + data.dynamicX = data.dynamicX.replace("${height}", "(" + ratio + " * ${height})"); + data.dynamicY = data.dynamicY.replace("${height}", "(" + ratio + " * ${height})") + " + (" + (ratio-1) + " * ${height})"; + + data.setHeight(data.getWidth()); + } + } + layout.version = 8; + return layout; + } + /** * Normalize the layout to v6 from v3/4: The stroke width is no longer dependant on the button size */ - public static CustomControls convertV3_4Layout(JSONObject oldLayoutJson) { + private static CustomControls convertV3_4Layout(JSONObject oldLayoutJson) { CustomControls layout = Tools.GLOBAL_GSON.fromJson(oldLayoutJson.toString(), CustomControls.class); convertStrokeWidth(layout); layout.version = 6; @@ -52,7 +81,7 @@ public static CustomControls convertV3_4Layout(JSONObject oldLayoutJson) { } - public static CustomControls convertV2Layout(JSONObject oldLayoutJson) throws JSONException { + private static CustomControls convertV2Layout(JSONObject oldLayoutJson) throws JSONException { CustomControls layout = Tools.GLOBAL_GSON.fromJson(oldLayoutJson.toString(), CustomControls.class); JSONArray layoutMainArray = oldLayoutJson.getJSONArray("mControlDataList"); layout.mControlDataList = new ArrayList<>(layoutMainArray.length()); @@ -95,7 +124,7 @@ public static CustomControls convertV2Layout(JSONObject oldLayoutJson) throws JS return layout; } - public static CustomControls convertV1Layout(JSONObject oldLayoutJson) throws JSONException { + private static CustomControls convertV1Layout(JSONObject oldLayoutJson) throws JSONException { CustomControls empty = new CustomControls(); JSONArray layoutMainArray = oldLayoutJson.getJSONArray("mControlDataList"); for (int i = 0; i < layoutMainArray.length(); i++) { 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 index 14bf4dde4f..47b9b68d9f 100644 --- 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 @@ -332,6 +332,7 @@ private void bindLayout() { /** * A long function linking all the displayed data on the popup and, * the currently edited mCurrentlyEditedButton + * @noinspection SuspiciousNameCombination */ private void setupRealTimeListeners() { mNameEditText.addTextChangedListener((SimpleTextWatcher) s -> { @@ -349,6 +350,10 @@ private void setupRealTimeListeners() { float width = safeParseFloat(s.toString()); if (width >= 0) { mCurrentlyEditedButton.getProperties().setWidth(width); + if (mCurrentlyEditedButton.getProperties() instanceof ControlJoystickData) { + // Joysticks are square + mCurrentlyEditedButton.getProperties().setHeight(width); + } mCurrentlyEditedButton.updateProperties(); } }); @@ -359,6 +364,10 @@ private void setupRealTimeListeners() { float height = safeParseFloat(s.toString()); if (height >= 0) { mCurrentlyEditedButton.getProperties().setHeight(height); + if (mCurrentlyEditedButton.getProperties() instanceof ControlJoystickData) { + // Joysticks are square + mCurrentlyEditedButton.getProperties().setWidth(height); + } mCurrentlyEditedButton.updateProperties(); } });